All files / roosterjs-content-model-plugins/lib/paste/WordDesktop processPastedContentFromWordDesktop.ts

100% Statements 30/30
100% Branches 16/16
100% Functions 6/6
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 891x 1x 1x 1x 1x 1x 1x                         1x   1x             1x 26x   26x 26x 26x 26x 26x 26x     1x     26x 327x   327x           182x               37x       2x                         79x 30x     79x     1x 30x 1x      
import { addParser } from '../utils/addParser';
import { getStyleMetadata } from './getStyleMetadata';
import { getStyles } from '../utils/getStyles';
import { processWordComments } from './processWordComments';
import { processWordList } from './processWordLists';
import { removeNegativeTextIndentParser } from './removeNegativeTextIndentParser';
import { setProcessor } from '../utils/setProcessor';
import type { WordMetadata } from './WordMetadata';
import type {
    BeforePasteEvent,
    ContentModelBlockFormat,
    ContentModelListItemLevelFormat,
    ContentModelTableFormat,
    DOMCreator,
    DomToModelContext,
    ElementProcessor,
    FormatParser,
} from 'roosterjs-content-model-types';
 
const PERCENTAGE_REGEX = /%/;
// Default line height in browsers according to https://developer.mozilla.org/en-US/docs/Web/CSS/line-height#normal
const DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE = 1.2;
 
/**
 * @internal
 * Handles Pasted content when source is Word Desktop
 * @param ev BeforePasteEvent
 */
export function processPastedContentFromWordDesktop(ev: BeforePasteEvent, domCreator: DOMCreator) {
    const metadataMap: Map<string, WordMetadata> = getStyleMetadata(ev, domCreator);
 
    setProcessor(ev.domToModelOption, 'element', wordDesktopElementProcessor(metadataMap));
    addParser(ev.domToModelOption, 'block', adjustPercentileLineHeight);
    addParser(ev.domToModelOption, 'block', removeNegativeTextIndentParser);
    addParser(ev.domToModelOption, 'listLevel', listLevelParser);
    addParser(ev.domToModelOption, 'container', wordTableParser);
    addParser(ev.domToModelOption, 'table', wordTableParser);
}
 
const wordDesktopElementProcessor = (
    metadataKey: Map<string, WordMetadata>
): ElementProcessor<HTMLElement> => {
    return (group, element, context) => {
        const styles = getStyles(element);
        // Process Word Lists or Word Commands, otherwise use the default processor on this element.
        if (
            !(
                processWordList(styles, group, element, context, metadataKey) ||
                processWordComments(styles, element)
            )
        ) {
            context.defaultElementProcessors.element(group, element, context);
        }
    };
};
 
function adjustPercentileLineHeight(format: ContentModelBlockFormat, element: HTMLElement): void {
    //If the line height is less than the browser default line height, line between the text is going to be too narrow
    let parsedLineHeight: number;
    if (
        PERCENTAGE_REGEX.test(element.style.lineHeight) &&
        !isNaN((parsedLineHeight = parseInt(element.style.lineHeight)))
    ) {
        format.lineHeight = (
            DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE *
            (parsedLineHeight / 100)
        ).toString();
    }
}
 
function listLevelParser(
    format: ContentModelListItemLevelFormat,
    element: HTMLElement,
    context: DomToModelContext,
    defaultStyle: Readonly<Partial<CSSStyleDeclaration>>
): void {
    if (element.style.marginLeft != '') {
        format.marginLeft = defaultStyle.marginLeft;
    }
 
    format.marginBottom = undefined;
}
 
const wordTableParser: FormatParser<ContentModelTableFormat> = (format): void => {
    if (format.marginLeft?.startsWith('-')) {
        delete format.marginLeft;
    }
};