UNPKG

canvasimo

Version:

An HTML5 canvas drawing library, with 150+ useful methods, jQuery-like fluent interface, and cross-browser compatibility enhancements.

150 lines (123 loc) 4.59 kB
import { DEFAULT_FONT_PARTS, INCORRECT_POINT_FORMAT, MATCHES_FONT_SIZE, MATCHES_FONT_STYLE, MATCHES_FONT_VARIANT, MATCHES_FONT_WEIGHT, MATCHES_NORMAL, MATCHES_SPECIAL_FILL, MATCHES_SPECIAL_FONT, MATCHES_WHITESPACE, } from './constants'; import logger from './logger'; import { FillRule, Points, } from './types'; export const isPoint = (point?: {x: number, y: number} | [number, number] | number): point is {x: number, y: number} => { return typeof point === 'object' && 'x' in point && 'y' in point; }; export const isTuplePoint = (point?: {x: number, y: number} | [number, number] | number): point is [number, number] => { return typeof point === 'object' && Array.isArray(point) && point.length === 2; }; let warnedAboutLineHeight = false; export const getFontParts = (input: string | undefined, density: number, getter: boolean) => { if (!input) { return DEFAULT_FONT_PARTS; } const font = input.trim(); if (MATCHES_SPECIAL_FONT.test(font)) { return [font]; } const matchFontSize = MATCHES_FONT_SIZE.exec(font); if (!matchFontSize) { return DEFAULT_FONT_PARTS; } const fontString = matchFontSize[0].trim(); const leadingSpaces = matchFontSize[1].length; const size = matchFontSize[2]; const unit = matchFontSize[3]; const lineHeight = matchFontSize[4]; if (lineHeight && !warnedAboutLineHeight) { logger.warn( `Attempted to set the font line height with "${fontString}", ` + 'but this is not supported by canvas. ' + 'Use the Canvasimo TextMultiline methods with the lineHeight parameter instead.' ); warnedAboutLineHeight = true; } const fontSize = (unit !== '%' ? (getter ? parseFloat(size) / density : parseFloat(size) * density) : size) + unit; const parts = font.substring(matchFontSize.index + leadingSpaces).split(MATCHES_WHITESPACE); const fontFamily = parts[parts.length - 1]; const optional = font.substring(0, matchFontSize.index); const optionalParts = optional ? optional.split(MATCHES_WHITESPACE) : null; let fontStyle; let fontVariant; let fontWeight; if (optionalParts) { while (optionalParts.length) { if (MATCHES_FONT_STYLE.test(optionalParts[0])) { fontStyle = optionalParts.splice(0, 1)[0]; } else if (MATCHES_FONT_VARIANT.test(optionalParts[0])) { fontVariant = optionalParts.splice(0, 1)[0]; } else if (MATCHES_FONT_WEIGHT.test(optionalParts[0])) { fontWeight = optionalParts.splice(0, 1)[0]; } else if (MATCHES_NORMAL.test(optionalParts[0])) { optionalParts.splice(0, 1); } else { return DEFAULT_FONT_PARTS; } } } return [ fontStyle || DEFAULT_FONT_PARTS[0], fontVariant || DEFAULT_FONT_PARTS[1], fontWeight || DEFAULT_FONT_PARTS[2], fontSize, fontFamily, ]; }; export const formatFont = (input: string, density: number, getter: boolean): string => getFontParts(input, density, getter).join(' '); export const forPoints = (points: Points, callback: (x: number, y: number, index: number) => any): void => { if (!Array.isArray(points) || (typeof points[0] === 'number' && (points.length % 2) !== 0)) { throw new Error(INCORRECT_POINT_FORMAT); } if (!points.length || points.length === 1 || (typeof points[0] === 'number' && points.length < 4)) { return; } const firstPoint = points[0]; const secondPoint = points[1]; if (isPoint(firstPoint)) { (points as Array<{x: number, y: number}>).forEach((point, index) => { if (!isPoint(point)) { throw new Error(`Expected point with format {x, y} but got ${point}`); } callback(point.x, point.y, index); }); } else if (isTuplePoint(firstPoint)) { (points as Array<[number, number]>).forEach((point, index) => { if (!isTuplePoint(point)) { throw new Error(`Expected point with format [x, y] but got ${point}`); } callback(point[0], point[1], index); }); } else if (typeof secondPoint === 'number') { // tslint:disable-next-line:prefer-for-of for (let i = 0; i < points.length; i += 2) { const pointX = points[i]; const pointY = points[i + 1]; if (typeof pointX !== 'number' || typeof pointY !== 'number') { throw new Error(`Expected points to be an array of numbers but got ${pointX}, ${pointY}`); } callback(pointX, pointY, i / 2); } } else { throw new Error(INCORRECT_POINT_FORMAT); } }; export const isFillRule = (value: any): value is FillRule => typeof value === 'string' && MATCHES_SPECIAL_FILL.test(value);