123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- /*************************************************************
- *
- * Copyright (c) 2022-2022 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Implements a mixin for node-based adaptors that overrides
- * the methods that obtain DOM node sizes, when those aren't
- * available from the DOM itself.
- *
- * @author dpvc@mathjax.org (Davide Cervone)
- */
- import {DOMAdaptor} from '../core/DOMAdaptor.js';
- import {userOptions, defaultOptions, OptionList} from '../util/Options.js';
- /**
- * A constructor for a given class
- *
- * @template T The class to construct
- */
- export type Constructor<T> = (new(...args: any[]) => T);
- /**
- * The type of an Adaptor class
- */
- export type AdaptorConstructor<N, T, D> = Constructor<DOMAdaptor<N, T, D>>;
- /**
- * The options to the NodeMixin
- */
- export const NodeMixinOptions: OptionList = {
- badCSS: true, // getComputedStyles() is not implemented in the DOM
- badSizes: true, // element sizes (e.g., ClientWidth, etc.) are not implemented in the DOM
- };
- /**
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export function NodeMixin<N, T, D, A extends AdaptorConstructor<N, T, D>>(
- Base: A,
- options: typeof NodeMixinOptions = {}
- ): A {
- options = userOptions(defaultOptions({}, NodeMixinOptions), options);
- return class NodeAdaptor extends Base {
- /**
- * The default options
- */
- public static OPTIONS: OptionList = {
- ...(options.badCSS ? {
- fontSize: 16, // We can't compute the font size, so always use this
- fontFamily: 'Times', // We can't compute the font family, so always use this
- } : {}),
- ...(options.badSizes ? {
- cjkCharWidth: 1, // Width (in em units) of full width characters
- unknownCharWidth: .6, // Width (in em units) of unknown (non-full-width) characters
- unknownCharHeight: .8, // Height (in em units) of unknown characters
- } : {})
- };
- /**
- * Pattern to identify CJK (i.e., full-width) characters
- */
- public static cjkPattern = new RegExp([
- '[',
- '\u1100-\u115F', // Hangul Jamo
- '\u2329\u232A', // LEFT-POINTING ANGLE BRACKET, RIGHT-POINTING ANGLE BRACKET
- '\u2E80-\u303E', // CJK Radicals Supplement ... CJK Symbols and Punctuation
- '\u3040-\u3247', // Hiragana ... Enclosed CJK Letters and Months
- '\u3250-\u4DBF', // Enclosed CJK Letters and Months ... CJK Unified Ideographs Extension A
- '\u4E00-\uA4C6', // CJK Unified Ideographs ... Yi Radicals
- '\uA960-\uA97C', // Hangul Jamo Extended-A
- '\uAC00-\uD7A3', // Hangul Syllables
- '\uF900-\uFAFF', // CJK Compatibility Ideographs
- '\uFE10-\uFE19', // Vertical Forms
- '\uFE30-\uFE6B', // CJK Compatibility Forms ... Small Form Variants
- '\uFF01-\uFF60\uFFE0-\uFFE6', // Halfwidth and Fullwidth Forms
- '\u{1B000}-\u{1B001}', // Kana Supplement
- '\u{1F200}-\u{1F251}', // Enclosed Ideographic Supplement
- '\u{20000}-\u{3FFFD}', // CJK Unified Ideographs Extension B ... Tertiary Ideographic Plane
- ']'
- ].join(''), 'gu');
- /**
- * The options for the instance
- */
- public options: OptionList;
- /**
- * @param {any} window The window to work with
- * @param {OptionList} options The options for the adaptor
- * @constructor
- */
- constructor(...args: any[]) {
- super(args[0]);
- let CLASS = this.constructor as typeof NodeAdaptor;
- this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), args[1]);
- }
- /**
- * For DOMs that don't handle CSS well, use the font size from the options
- *
- * @override
- */
- public fontSize(node: N) {
- return (options.badCSS ? this.options.fontSize : super.fontSize(node));
- }
- /**
- * For DOMs that don't handle CSS well, use the font family from the options
- *
- * @override
- */
- public fontFamily(node: N) {
- return (options.badCSS ? this.options.fontFamily : super.fontFamily(node));
- }
- /**
- * @override
- */
- public nodeSize(node: N, em: number = 1, local: boolean = null) {
- if (!options.badSizes) {
- return super.nodeSize(node, em, local);
- }
- const text = this.textContent(node);
- const non = Array.from(text.replace(NodeAdaptor.cjkPattern, '')).length; // # of non-CJK chars
- const CJK = Array.from(text).length - non; // # of cjk chars
- return [
- CJK * this.options.cjkCharWidth + non * this.options.unknownCharWidth,
- this.options.unknownCharHeight
- ] as [number, number];
- }
- /**
- * @override
- */
- public nodeBBox(node: N) {
- return (options.badSizes ? {left: 0, right: 0, top: 0, bottom: 0} : super.nodeBBox(node));
- }
- };
- }
|