Skip to content

Commit

Permalink
Content Model: Fix 194024 and 220289 (#2012)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong authored Aug 8, 2023
1 parent 4680506 commit aa49e67
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const defaultProcessorMap: ElementProcessorMap = {
p: pProcessor,
pre: formatContainerProcessor,
s: knownElementProcessor,
section: knownElementProcessor,
span: knownElementProcessor,
strike: knownElementProcessor,
strong: knownElementProcessor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FontSizeFormat } from 'roosterjs-content-model-types';
import { FormatHandler } from '../FormatHandler';
import { isSuperOrSubScript } from './superOrSubScriptFormatHandler';
import { parseValueWithUnit } from '../utils/parseValueWithUnit';

/**
* @internal
Expand All @@ -13,7 +14,11 @@ export const fontSizeFormatHandler: FormatHandler<FontSizeFormat> = {
// when font size is 'smaller' and the style is for superscript/subscript,
// the font size will be handled by superOrSubScript handler
if (fontSize && !isSuperOrSubScript(fontSize, verticalAlign) && fontSize != 'inherit') {
format.fontSize = fontSize;
if (element.style.fontSize) {
format.fontSize = normalizeFontSize(fontSize, context.segmentFormat.fontSize);
} else if (defaultStyle.fontSize) {
format.fontSize = fontSize;
}
}
},
apply: (format, element, context) => {
Expand All @@ -22,3 +27,49 @@ export const fontSizeFormatHandler: FormatHandler<FontSizeFormat> = {
}
},
};

// https://developer.mozilla.org/en-US/docs/Web/CSS/font-size
const KnownFontSizes: Record<string, string> = {
'xx-small': '6.75pt',
'x-small': '7.5pt',
small: '9.75pt',
medium: '12pt',
large: '13.5pt',
'x-large': '18pt',
'xx-large': '24pt',
'xxx-large': '36pt',
};

function normalizeFontSize(fontSize: string, contextFont: string | undefined): string | undefined {
const knownFontSize = KnownFontSizes[fontSize];

if (knownFontSize) {
return knownFontSize;
} else if (
fontSize == 'smaller' ||
fontSize == 'larger' ||
fontSize.endsWith('em') ||
fontSize.endsWith('%')
) {
if (!contextFont) {
return undefined;
} else {
const existingFontSize = parseValueWithUnit(contextFont, undefined /*element*/, 'px');

if (existingFontSize) {
switch (fontSize) {
case 'smaller':
return Math.round((existingFontSize * 500) / 6) / 100 + 'px';
case 'larger':
return Math.round((existingFontSize * 600) / 5) / 100 + 'px';
default:
return parseValueWithUnit(fontSize, existingFontSize, 'px') + 'px';
}
}
}
} else if (fontSize == 'inherit' || fontSize == 'revert' || fontSize == 'unset') {
return undefined;
} else {
return fontSize;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const MarginValueRegex = /(-?\d+(\.\d+)?)([a-z]+|%)/;
/**
* Parse unit value with its unit
* @param value The source value to parse
* @param element The source element which has this unit value.
* @param currentSizePxOrElement The source element which has this unit value, or current font size (in px) from context.
* @param resultUnit Unit for result, can be px or pt. @default px
*/
export function parseValueWithUnit(
value: string = '',
element?: HTMLElement,
currentSizePxOrElement?: number | HTMLElement,
resultUnit: 'px' | 'pt' = 'px'
): number {
const match = MarginValueRegex.exec(value);
Expand All @@ -28,13 +28,13 @@ export function parseValueWithUnit(
result = ptToPx(num);
break;
case 'em':
result = element ? getFontSize(element) * num : 0;
result = getFontSize(currentSizePxOrElement) * num;
break;
case 'ex':
result = element ? (getFontSize(element) * num) / 2 : 0;
result = (getFontSize(currentSizePxOrElement) * num) / 2;
break;
case '%':
result = element ? (element.offsetWidth * num) / 100 : 0;
result = (getFontSize(currentSizePxOrElement) * num) / 100;
break;
default:
// TODO: Support more unit if need
Expand All @@ -49,12 +49,18 @@ export function parseValueWithUnit(
return result;
}

function getFontSize(element: HTMLElement) {
const styleInPt = getComputedStyle(element, 'font-size');
const floatInPt = parseFloat(styleInPt);
const floatInPx = ptToPx(floatInPt);
function getFontSize(currentSizeOrElement?: number | HTMLElement): number {
if (typeof currentSizeOrElement === 'undefined') {
return 0;
} else if (typeof currentSizeOrElement === 'number') {
return currentSizeOrElement;
} else {
const styleInPt = getComputedStyle(currentSizeOrElement, 'font-size');
const floatInPt = parseFloat(styleInPt);
const floatInPx = ptToPx(floatInPt);

return floatInPx;
return floatInPx;
}
}

function ptToPx(pt: number): number {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,114 @@ describe('fontSizeFormatHandler.parse', () => {

expect(format.fontSize).toBe('20px');
});

it('xx-small', () => {
div.style.fontSize = 'xx-small';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('6.75pt');
});
it('x-small', () => {
div.style.fontSize = 'x-small';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('7.5pt');
});
it('small', () => {
div.style.fontSize = 'small';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('9.75pt');
});
it('medium', () => {
div.style.fontSize = 'medium';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('12pt');
});
it('large', () => {
div.style.fontSize = 'large';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('13.5pt');
});
it('x-large', () => {
div.style.fontSize = 'x-large';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('18pt');
});
it('xx-large', () => {
div.style.fontSize = 'xx-large';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('24pt');
});
it('xxx-large', () => {
div.style.fontSize = 'xxx-large';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('36pt');
});

it('smaller without context', () => {
div.style.fontSize = 'smaller';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe(undefined);
});

it('smaller with context', () => {
div.style.fontSize = 'smaller';
context.segmentFormat.fontSize = '12pt';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('13.33px');
});

it('larger without context', () => {
div.style.fontSize = 'larger';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe(undefined);
});

it('larger with context', () => {
div.style.fontSize = 'larger';
context.segmentFormat.fontSize = '10pt';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('16px');
});

it('em without context', () => {
div.style.fontSize = '2em';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe(undefined);
});

it('em with context', () => {
div.style.fontSize = '2em';
context.segmentFormat.fontSize = '12pt';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('32px');
});

it('% without context', () => {
div.style.fontSize = '50%';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe(undefined);
});
it('% with context', () => {
div.style.fontSize = '50%';
context.segmentFormat.fontSize = '12pt';
fontSizeFormatHandler.parse(format, div, context, {});

expect(format.fontSize).toBe('8px');
});
});

describe('fontSizeFormatHandler.apply', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as getComputedStyles from 'roosterjs-editor-dom/lib/utils/getComputedStyles';
import { parseValueWithUnit } from '../../../lib/formatHandlers/utils/parseValueWithUnit';

describe('parseValueWithUnit', () => {
describe('parseValueWithUnit with element', () => {
function runTest(unit: string, results: number[]) {
const mockedElement = {
offsetWidth: 1000,
Expand Down Expand Up @@ -50,7 +50,64 @@ describe('parseValueWithUnit', () => {
});

it('%', () => {
runTest('% ', [0, 10, 11, -11]);
runTest('% ', [0, 0.2, 0.22, -0.22]);
});

it('px to pt', () => {
const result = parseValueWithUnit('16px', undefined, 'pt');

expect(result).toBe(12);
});

it('pt to pt', () => {
const result = parseValueWithUnit('16pt', undefined, 'pt');

expect(result).toBe(16);
});
});

describe('parseValueWithUnit with number', () => {
function runTest(unit: string, results: number[]) {
['0', '1', '1.1', '-1.1'].forEach((value, i) => {
const input = value + unit;
const result = parseValueWithUnit(input, 20);

expect(result).toBe(results[i], input);
});
}

it('empty', () => {
expect(parseValueWithUnit()).toBe(0);
expect(parseValueWithUnit('')).toBe(0);
expect(parseValueWithUnit('', {} as HTMLElement)).toBe(0);
});

it('px', () => {
runTest('px', [0, 1, 1.1, -1.1]);
});

it('pt', () => {
runTest('pt', [0, 1.333, 1.467, -1.467]);
});

it('em', () => {
runTest('em', [0, 20, 22, -22]);
});

it('ex', () => {
runTest('ex', [0, 10, 11, -11]);
});

it('no unit', () => {
runTest('', [0, 0, 0, 0]);
});

it('unknown unit', () => {
runTest('unknown', [0, 0, 0, 0]);
});

it('%', () => {
runTest('% ', [0, 0.2, 0.22, -0.22]);
});

it('px to pt', () => {
Expand Down

0 comments on commit aa49e67

Please sign in to comment.