/*
* This file is part of TREB.
*
* TREB is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with TREB. If not, see .
*
* Copyright 2022-2026 trebco, llc.
* info@treb.app
*
*/
import { UA } from 'treb-grid';
import { Measurement } from 'treb-utils';
import { Style, type FontSize } from './style';
/**
* these are the stacks we're currently supporting.
*/
export const font_stack_names = [
'default',
'old-style',
'transitional',
'handwritten',
'monospace',
'industrial',
'ui',
] as const;
export type FontStackType = typeof font_stack_names[number];
export const font_stack_labels: Record = {
'default': 'Sans-serif',
'old-style': 'Old style',
'transitional': 'Serif',
'handwritten': 'Handwritten',
'monospace': 'Monospace',
'industrial': 'Industrial sans',
'ui': 'System UI',
};
/**
* representation of a font stack as an object we can manage in themes.
* the impetus for this is having some font "sets" we can use in a sheet
* that we know will render in a reasonable cross-platform way.
*
* we're also taking advantage of chrome supporting font features in
* canvas. we have to do a little bit of carve-out for ffx browsers on
* windows but that should be it, and the fallback is OK.
*/
export interface FontStack {
/** the font family from css. this will usually be a list. */
family: string;
/** the actual font used */
font?: string;
/** default size for grid cells. may be different for different fonts. */
size: FontSize;
/**
* font variants. we used this for sitka text to apply lining-nums and
* tabular-nums, although we're not using sitka anymore. atm only chrome
* supports font variants in canvas (boo).
*/
variants?: string;
}
const font_cache: Map = new Map();
/**
*
* @param name - for reporting purposes only
* @param computed - computed css to resolve variables
* @returns
*/
export const GenerateFontStack = (name: string, computed: CSSStyleDeclaration): FontStack => {
const family = computed.fontFamily;
let font = '';
const elements = family.split(/,/);
for (let element of elements) {
element = element.replace(/'"/g, '').trim();
const lc = element.toLowerCase();
//
// platform-specific hacks. this is kind of unfortunate.
//
if (UA.is_firefox && /sitka text/i.test(lc)) {
continue;
}
let check = font_cache.get(lc);
if (typeof check === 'undefined') {
check = Measurement.FontLoaded(element);
font_cache.set(lc, check);
}
if (check) {
font = element;
break;
}
}
if (!font) {
console.warn('no font found for font stack', name);
}
// check the base size, and any adjustments for this font
const variable_name = font.toLowerCase().replace(/['"]/g, '').replace(/\W+/g, '-');
const base = computed.getPropertyValue('--treb-font-stack-default-size');
const adjusted = computed.getPropertyValue(`--treb-font-stack-${variable_name}-size`);
const variants = computed.getPropertyValue(`--treb-font-stack-${variable_name}-variant`);
const size = Style.ParseFontSize(adjusted || base || '10pt').font_size || { unit: 'pt', value: 10 };
// console.info({stack: name, family, font, base, adjusted, size, variants});
const stack = {
family,
font,
variants,
size,
};
return stack;
};