diff --git a/packages/core/ui/styling/font-common.ts b/packages/core/ui/styling/font-common.ts index 95791a931..e47de83bc 100644 --- a/packages/core/ui/styling/font-common.ts +++ b/packages/core/ui/styling/font-common.ts @@ -30,7 +30,7 @@ export abstract class Font implements FontDefinition { public abstract withFontWeight(weight: FontWeightType): Font; public abstract withFontSize(size: number): Font; public abstract withFontScale(scale: number): Font; - public abstract withFontVariationSettings(variationSettings: Array | null): Font; + public abstract withFontVariationSettings(variationSettings: FontVariationSettingsType[]): Font; public static equals(value1: Font, value2: Font): boolean { // both values are falsy @@ -43,7 +43,7 @@ export abstract class Font implements FontDefinition { return false; } - return value1.fontFamily === value2.fontFamily && value1.fontSize === value2.fontSize && value1.fontStyle === value2.fontStyle && value1.fontWeight === value2.fontWeight; + return value1.fontFamily === value2.fontFamily && value1.fontSize === value2.fontSize && value1.fontStyle === value2.fontStyle && value1.fontWeight === value2.fontWeight && value1.fontScale === value2.fontScale && FontVariationSettings.toString(value1.fontVariationSettings) === FontVariationSettings.toString(value2.fontVariationSettings); } } @@ -93,7 +93,9 @@ export namespace FontVariationSettings { // See https://drafts.csswg.org/css-fonts/#font-variation-settings-def. // Axis name strings longer or shorter than four characters are invalid. if (!isNaN(axisValue) && axisName.length === 6 && ((axisName.startsWith("'") && axisName.endsWith("'")) || (axisName.startsWith('"') && axisName.endsWith('"')))) { - parsed.push({ axis: axisName, value: axisValue }); + // Remove quotes as they might cause problems when using name as an object key + const unquotedAxisName = axisName.substring(1, axisName.length - 1); + parsed.push({ axis: unquotedAxisName, value: axisValue }); } else { Trace.write('Invalid value (font-variation-settings): ' + variationSettingsValue, Trace.categories.Error, Trace.messageType.error); } @@ -110,7 +112,7 @@ export namespace FontVariationSettings { export function toString(fontVariationSettings: FontVariationSettingsType[] | null): string | null { if (fontVariationSettings?.length) { - return fontVariationSettings.map(({ axis, value }) => `${axis} ${value}`).join(', '); + return fontVariationSettings.map(({ axis, value }) => `'${axis}' ${value}`).join(', '); } return null; diff --git a/packages/core/ui/styling/font-interfaces.ts b/packages/core/ui/styling/font-interfaces.ts index babea81f2..6fd7180c1 100644 --- a/packages/core/ui/styling/font-interfaces.ts +++ b/packages/core/ui/styling/font-interfaces.ts @@ -8,7 +8,7 @@ export interface ParsedFont { lineHeight?: string; fontSize?: string; fontFamily?: string; - fontVariationSettings?: Array; + fontVariationSettings?: FontVariationSettingsType[]; } export type FontVariationSettingsType = { diff --git a/packages/core/ui/styling/font.android.ts b/packages/core/ui/styling/font.android.ts index c06c98d03..0463770d1 100644 --- a/packages/core/ui/styling/font.android.ts +++ b/packages/core/ui/styling/font.android.ts @@ -16,32 +16,28 @@ export class Font extends FontBase { private _typeface: android.graphics.Typeface; - constructor(family: string, size: number, style?: FontStyleType, weight?: FontWeightType, fontVariationSettings?: Array) { - super(family, size, style, weight, 1, fontVariationSettings); - } - public withFontFamily(family: string): Font { - return new Font(family, this.fontSize, this.fontStyle, this.fontWeight, this.fontVariationSettings); + return new Font(family, this.fontSize, this.fontStyle, this.fontWeight, 1, this.fontVariationSettings); } public withFontStyle(style: FontStyleType): Font { - return new Font(this.fontFamily, this.fontSize, style, this.fontWeight, this.fontVariationSettings); + return new Font(this.fontFamily, this.fontSize, style, this.fontWeight, 1, this.fontVariationSettings); } public withFontWeight(weight: FontWeightType): Font { - return new Font(this.fontFamily, this.fontSize, this.fontStyle, weight, this.fontVariationSettings); + return new Font(this.fontFamily, this.fontSize, this.fontStyle, weight, 1, this.fontVariationSettings); } public withFontSize(size: number): Font { - return new Font(this.fontFamily, size, this.fontStyle, this.fontWeight, this.fontVariationSettings); + return new Font(this.fontFamily, size, this.fontStyle, this.fontWeight, 1, this.fontVariationSettings); } public withFontScale(scale: number): Font { - return new Font(this.fontFamily, this.fontSize, this.fontStyle, this.fontWeight, this.fontVariationSettings); + return new Font(this.fontFamily, this.fontSize, this.fontStyle, this.fontWeight, 1, this.fontVariationSettings); } public withFontVariationSettings(variationSettings: Array | null): Font { - return new Font(this.fontFamily, this.fontSize, this.fontStyle, this.fontWeight, variationSettings); + return new Font(this.fontFamily, this.fontSize, this.fontStyle, this.fontWeight, 1, variationSettings); } getAndroidTypeface(): android.graphics.Typeface { @@ -91,9 +87,7 @@ function loadFontFromFile(fontFamily: string, font: Font): android.graphics.Type if (SDK_VERSION >= 26) { const builder = new android.graphics.Typeface.Builder(fontAssetPath); if (builder) { - if (font.fontVariationSettings !== undefined) { - builder.setFontVariationSettings(font.fontVariationSettings?.length ? FontVariationSettings.toString(font.fontVariationSettings) : ''); - } + builder.setFontVariationSettings(font.fontVariationSettings?.length ? FontVariationSettings.toString(font.fontVariationSettings) : ''); result = builder.build(); } else { result = android.graphics.Typeface.createFromFile(fontAssetPath); @@ -133,16 +127,13 @@ function createTypeface(font: Font): android.graphics.Typeface { case genericFontFamilies.serif: result = android.graphics.Typeface.create('serif' + getFontWeightSuffix(font.fontWeight), fontStyle); break; - case genericFontFamilies.sansSerif: case genericFontFamilies.system: result = android.graphics.Typeface.create('sans-serif' + getFontWeightSuffix(font.fontWeight), fontStyle); break; - case genericFontFamilies.monospace: result = android.graphics.Typeface.create('monospace' + getFontWeightSuffix(font.fontWeight), fontStyle); break; - default: { result = loadFontFromFile(fontFamily, font); if (result && fontStyle) { diff --git a/packages/core/ui/styling/font.d.ts b/packages/core/ui/styling/font.d.ts index d86ad3e99..6423c85f0 100644 --- a/packages/core/ui/styling/font.d.ts +++ b/packages/core/ui/styling/font.d.ts @@ -9,7 +9,7 @@ export declare class Font extends FontBase { public fontWeight: FontWeightType; public fontSize: number; public fontScale: number; - public fontVariationSettings?: FontVariationSettings[]; + public fontVariationSettings?: FontVariationSettingsType[]; public isBold: boolean; public isItalic: boolean; @@ -24,7 +24,7 @@ export declare class Font extends FontBase { public withFontWeight(weight: FontWeightType): Font; public withFontSize(size: number): Font; public withFontScale(scale: number): Font; - public withFontVariationSettings(variationSettings: FontVariationSettings[] | null): Font; + public withFontVariationSettings(variationSettings: FontVariationSettings[]): Font; public static equals(value1: Font, value2: Font): boolean; } diff --git a/packages/core/ui/styling/font.ios.ts b/packages/core/ui/styling/font.ios.ts index 3201efdac..43f659ad6 100644 --- a/packages/core/ui/styling/font.ios.ts +++ b/packages/core/ui/styling/font.ios.ts @@ -13,6 +13,8 @@ interface FontDescriptor { isItalic: boolean; } +type FontVariationAxisType = 'kCGFontVariationAxisDefaultValue' | 'kCGFontVariationAxisMaxValue' | 'kCGFontVariationAxisMinValue' | 'kCGFontVariationAxisName'; + const uiFontCache = new Map(); function computeFontCacheKey(fontDescriptor: FontDescriptor) { @@ -33,20 +35,27 @@ function getUIFontCached(fontDescriptor: FontDescriptor) { let uiFont = NativeScriptUtils.createUIFont(fontDescriptor as any); if (fontDescriptor.fontVariationSettings?.length) { let font = CGFontCreateWithFontName(uiFont.fontName); - const variationAxes: NSArray> = CGFontCopyVariationAxes(font); - const variationAxesNames: string[] = []; - for (const axis of variationAxes) { - variationAxesNames.push(String(axis.objectForKey('kCGFontVariationAxisName'))); - } - const variationSettings = {}; - for (const variationSetting of fontDescriptor.fontVariationSettings) { - const axisName = fuzzySearch(variationSetting.axis, variationAxesNames); - if (axisName?.length) { - variationSettings[axisName[0]] = variationSetting.value; + const variationAxes: NSArray> = CGFontCopyVariationAxes(font); + // This can be null if font doesn't support axes + if (variationAxes?.count) { + const variationSettings = NSMutableDictionary.new(); + const variationAxesCount = variationAxes.count; + const variationAxesNames: string[] = []; + + for (let i = 0, length = variationAxes.count; i < length; i++) { + variationAxesNames.push(String(variationAxes[i].objectForKey('kCGFontVariationAxisName'))); } + + for (const variationSetting of fontDescriptor.fontVariationSettings) { + const axisName = fuzzySearch(variationSetting.axis, variationAxesNames); + if (axisName?.length) { + variationSettings.setValueForKey(variationSetting.value, axisName[0]); + } + } + + font = CGFontCreateCopyWithVariations(font, variationSettings); + uiFont = CTFontCreateWithGraphicsFont(font, fontDescriptor.fontSize, null, null); } - font = CGFontCreateCopyWithVariations(font, variationSettings as any); - uiFont = CTFontCreateWithGraphicsFont(font, fontDescriptor.fontSize, null, null); } uiFontCache.set(cacheKey, uiFont); diff --git a/packages/core/ui/styling/style-properties.d.ts b/packages/core/ui/styling/style-properties.d.ts index 3c68567c4..61f99e3d0 100644 --- a/packages/core/ui/styling/style-properties.d.ts +++ b/packages/core/ui/styling/style-properties.d.ts @@ -3,7 +3,7 @@ import { CoreTypes } from '../../core-types'; import { Color } from '../../color'; import { CssProperty, CssAnimationProperty, ShorthandProperty, InheritedCssProperty } from '../core/properties'; import { Style } from './style'; -import { Font, FontStyleType, FontWeightType } from './font'; +import { Font, FontStyleType, FontWeightType, FontVariationSettingsType } from './font'; import { Background } from './background'; export namespace Length { @@ -101,6 +101,7 @@ export const fontWeightProperty: InheritedCssProperty; export const backgroundInternalProperty: CssProperty; export const fontInternalProperty: InheritedCssProperty; export const fontScaleInternalProperty: InheritedCssProperty; +export const fontVariationSettingsProperty: InheritedCssProperty; export const androidElevationProperty: CssProperty; export const androidDynamicElevationOffsetProperty: CssProperty; diff --git a/packages/core/ui/styling/style-properties.ts b/packages/core/ui/styling/style-properties.ts index 0ee71247a..fbea95aa2 100644 --- a/packages/core/ui/styling/style-properties.ts +++ b/packages/core/ui/styling/style-properties.ts @@ -1424,7 +1424,7 @@ const fontProperty = new ShorthandProperty({ }); fontProperty.register(Style); -export const fontVariationSettingsProperty = new InheritedCssProperty | null>({ +export const fontVariationSettingsProperty = new InheritedCssProperty({ name: 'fontVariationSettings', cssName: 'font-variation-settings', affectsLayout: global.isIOS, diff --git a/packages/core/ui/styling/style/index.ts b/packages/core/ui/styling/style/index.ts index 3d501521b..e083f7243 100644 --- a/packages/core/ui/styling/style/index.ts +++ b/packages/core/ui/styling/style/index.ts @@ -1,6 +1,6 @@ import { Style as StyleDefinition } from '.'; import { Color } from '../../../color'; -import { Font, FontStyleType, FontWeightType } from '../font'; +import { Font, FontStyleType, FontWeightType, FontVariationSettingsType } from '../font'; import { Background } from '../background'; import { ViewBase } from '../../core/view-base'; import { LinearGradient } from '../../styling/linear-gradient'; @@ -156,6 +156,7 @@ export class Style extends Observable implements StyleDefinition { public fontFamily: string; public fontStyle: FontStyleType; public fontWeight: FontWeightType; + public fontVariationSettings: FontVariationSettingsType[]; public font: string; public maxLines: CoreTypes.MaxLinesType; diff --git a/packages/core/ui/text-base/formatted-string.d.ts b/packages/core/ui/text-base/formatted-string.d.ts index cfff33486..0e042306e 100644 --- a/packages/core/ui/text-base/formatted-string.d.ts +++ b/packages/core/ui/text-base/formatted-string.d.ts @@ -6,7 +6,7 @@ import { Span } from './span'; import { ObservableArray } from '../../data/observable-array'; import { ViewBase } from '../core/view-base'; import { Color } from '../../color'; -import { FontStyleType, FontWeightType } from '../styling/font'; +import { FontStyleType, FontVariationSettingsType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; /** @@ -43,6 +43,11 @@ export class FormattedString extends ViewBase { */ public fontWeight: FontWeightType; + /** + * Gets or sets the font variation settings which will be used for all spans that doesn't have a specific value. + */ + public fontVariationSettings: FontVariationSettingsType[]; + /** * Gets or sets text decorations which will be used for all spans that doesn't have a specific value. */ diff --git a/packages/core/ui/text-base/formatted-string.ts b/packages/core/ui/text-base/formatted-string.ts index f12a38eac..939f938d9 100644 --- a/packages/core/ui/text-base/formatted-string.ts +++ b/packages/core/ui/text-base/formatted-string.ts @@ -5,7 +5,7 @@ import { ObservableArray, ChangedData } from '../../data/observable-array'; import { AddArrayFromBuilder, AddChildFromBuilder } from '../core/view'; import { ViewBase } from '../core/view-base'; import { Color } from '../../color'; -import { FontStyleType, FontWeightType } from '../styling/font'; +import { FontStyleType, FontVariationSettingsType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; export class FormattedString extends ViewBase implements FormattedStringDefinition, AddArrayFromBuilder, AddChildFromBuilder { @@ -45,6 +45,13 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti this.style.fontWeight = value; } + get fontVariationSettings(): FontVariationSettingsType[] { + return this.style.fontVariationSettings; + } + set fontVariationSettings(value: FontVariationSettingsType[]) { + this.style.fontVariationSettings = value; + } + get textDecoration(): CoreTypes.TextDecorationType { return this.style.textDecoration; } @@ -153,6 +160,7 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti style.on('fontSizeChange', this.onPropertyChange, this); style.on('fontStyleChange', this.onPropertyChange, this); style.on('fontWeightChange', this.onPropertyChange, this); + style.on('fontVariationSettingsChange', this.onPropertyChange, this); style.on('textDecorationChange', this.onPropertyChange, this); style.on('colorChange', this.onPropertyChange, this); style.on('backgroundColorChange', this.onPropertyChange, this); @@ -171,6 +179,7 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti style.off('fontSizeChange', this.onPropertyChange, this); style.off('fontStyleChange', this.onPropertyChange, this); style.off('fontWeightChange', this.onPropertyChange, this); + style.off('fontVariationSettingsChange', this.onPropertyChange, this); style.off('textDecorationChange', this.onPropertyChange, this); style.off('colorChange', this.onPropertyChange, this); style.off('backgroundColorChange', this.onPropertyChange, this); diff --git a/packages/core/ui/text-base/index.android.ts b/packages/core/ui/text-base/index.android.ts index 6c1535573..cb538617b 100644 --- a/packages/core/ui/text-base/index.android.ts +++ b/packages/core/ui/text-base/index.android.ts @@ -607,7 +607,7 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, const fontFamily = span.fontFamily; if (fontFamily) { - const font = new Font(fontFamily, 0, italic ? 'italic' : 'normal', bold ? 'bold' : 'normal'); + const font = new Font(fontFamily, 0, italic ? 'italic' : 'normal', bold ? 'bold' : 'normal', spanStyle.fontScaleInternal, spanStyle.fontVariationSettings); const typeface = font.getAndroidTypeface() || android.graphics.Typeface.create(fontFamily, 0); const typefaceSpan: android.text.style.TypefaceSpan = new org.nativescript.widgets.CustomTypefaceSpan(fontFamily, typeface); ssb.setSpan(typefaceSpan, start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); diff --git a/packages/core/ui/text-base/index.ios.ts b/packages/core/ui/text-base/index.ios.ts index 0fb160eb9..06416d73d 100644 --- a/packages/core/ui/text-base/index.ios.ts +++ b/packages/core/ui/text-base/index.ios.ts @@ -358,7 +358,7 @@ export class TextBase extends TextBaseCommon { createMutableStringDetails(span: Span, text: string, index?: number): any { const fontScale = adjustMinMaxFontScale(span.style.fontScaleInternal, span); - const font = new Font(span.style.fontFamily, span.style.fontSize, span.style.fontStyle, span.style.fontWeight, fontScale); + const font = new Font(span.style.fontFamily, span.style.fontSize, span.style.fontStyle, span.style.fontWeight, fontScale, span.style.fontVariationSettings); const iosFont = font.getUIFont(this.nativeTextViewProtected.font); const backgroundColor = (span.style.backgroundColor || (span.parent).backgroundColor || (span.parent.parent).backgroundColor); diff --git a/packages/core/ui/text-base/span.d.ts b/packages/core/ui/text-base/span.d.ts index 9ab2b9919..27b5daef4 100644 --- a/packages/core/ui/text-base/span.d.ts +++ b/packages/core/ui/text-base/span.d.ts @@ -1,6 +1,6 @@ import { Color } from '../../color'; import { ViewBase } from '../core/view-base'; -import { FontStyleType, FontWeightType } from '../styling/font'; +import { FontStyleType, FontVariationSettingsType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; /** @@ -27,6 +27,11 @@ export class Span extends ViewBase { */ public fontWeight: FontWeightType; + /** + * Gets or sets the font variation settings of the span. + */ + public fontVariationSettings: FontVariationSettingsType[]; + /** * Gets or sets text decorations for the span. */ diff --git a/packages/core/ui/text-base/span.ts b/packages/core/ui/text-base/span.ts index 83dd56bff..006ba45de 100644 --- a/packages/core/ui/text-base/span.ts +++ b/packages/core/ui/text-base/span.ts @@ -1,7 +1,7 @@ import { Color } from '../../color'; import { Span as SpanDefinition } from './span'; import { ViewBase } from '../core/view-base'; -import { FontStyleType, FontWeightType } from '../styling/font'; +import { FontStyleType, FontVariationSettingsType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; import { EventData } from '../../data/observable'; import { isNullOrUndefined, isString } from '../../utils/types'; @@ -41,6 +41,13 @@ export class Span extends ViewBase implements SpanDefinition { this.style.fontWeight = value; } + get fontVariationSettings(): FontVariationSettingsType[] { + return this.style.fontVariationSettings; + } + set fontVariationSettings(value: FontVariationSettingsType[]) { + this.style.fontVariationSettings = value; + } + get textDecoration(): CoreTypes.TextDecorationType { return this.style.textDecoration; }