Fix pattern custom glyph rendering across documents

This commit is contained in:
Anthony Kim
2026-08-23 20:12:13 -07:00
parent 6a49e1aa61
commit 163104e14f
3 changed files with 157 additions and 3 deletions
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { createPatternCanvas } from './CustomGlyphRasterizer';
describe('CustomGlyphRasterizer', () => {
describe('createPatternCanvas', () => {
it('prefers an offscreen canvas without using the DOM canvas factory', () => {
const expectedCanvas = {} as OffscreenCanvas;
const calls: string[] = [];
const canvas = createPatternCanvas(
2,
3,
(width, height) => {
calls.push(`offscreen:${width}x${height}`);
return expectedCanvas;
},
() => {
calls.push('dom');
return {} as HTMLCanvasElement;
}
);
assert.deepEqual({ canvas, calls }, {
canvas: expectedCanvas,
calls: ['offscreen:2x3']
});
});
it('uses the main-realm DOM canvas factory when OffscreenCanvas is unavailable', () => {
const expectedCanvas = {} as HTMLCanvasElement;
const calls: string[] = [];
const canvas = createPatternCanvas(
4,
5,
undefined,
(width, height) => {
calls.push(`dom:${width}x${height}`);
return expectedCanvas;
}
);
assert.deepEqual({ canvas, calls }, {
canvas: expectedCanvas,
calls: ['dom:4x5']
});
});
});
});
@@ -8,6 +8,29 @@ import type { ILogService } from 'common/services/Services';
import { customGlyphDefinitions } from './CustomGlyphDefinitions';
import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
type PatternCanvas = HTMLCanvasElement | OffscreenCanvas;
type PatternCanvasFactory = (width: number, height: number) => PatternCanvas;
const createOffscreenPatternCanvas: PatternCanvasFactory | undefined = typeof OffscreenCanvas === 'undefined'
? undefined
: (width, height) => new OffscreenCanvas(width, height);
const createDomPatternCanvas: PatternCanvasFactory = (width, height) => {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
};
export function createPatternCanvas(
width: number,
height: number,
offscreenCanvasFactory: PatternCanvasFactory | undefined = createOffscreenPatternCanvas,
domCanvasFactory: PatternCanvasFactory = createDomPatternCanvas
): PatternCanvas {
return offscreenCanvasFactory?.(width, height) ?? domCanvasFactory(width, height);
}
/**
* Try drawing a custom block element or box drawing character, returning whether it was
* successfully drawn.
@@ -458,9 +481,9 @@ function drawPatternChar(
if (!pattern) {
const width = charDefinition[0].length;
const height = charDefinition.length;
const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas');
tmpCanvas.width = width;
tmpCanvas.height = height;
// The atlas canvas can be adopted into another document, so temporary resources must not use
// its mutable ownerDocument.
const tmpCanvas = createPatternCanvas(width, height);
const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
const imageData = new ImageData(width, height);