{"version":3,"file":"index.mjs","names":[],"sources":["../index.ts"],"sourcesContent":["import { Readable } from 'node:stream'\nimport { type Canvas, createCanvas, registerFont } from 'canvas'\nimport type {\n  LineProperty,\n  MaxMetrics,\n  ParsedOptions,\n  TextSvgOptions\n} from './types/index.ts'\n\nconst text2svg = (\n  text: string,\n  inputOptions: TextSvgOptions = {}\n): Buffer | Readable | string | Canvas => {\n  // Options\n  const options: ParsedOptions = parseOptions(inputOptions)\n\n  // Register a custom font\n  if (options.localFontPath && options.localFontName) {\n    registerFont(options.localFontPath, {\n      family: options.localFontName\n    })\n  }\n\n  const canvas = createCanvas(0, 0, 'svg')\n  const ctx = canvas.getContext('2d')\n\n  const max: MaxMetrics = {\n    left: 0,\n    right: 0,\n    ascent: 0,\n    descent: 0\n  }\n\n  let lastDescent: number = 0\n  const lineProps: LineProperty[] = []\n  ctx.font = options.font\n  text.split('\\n').forEach((line) => {\n    const words = line.split(' ')\n    let newLine = ''\n    let left: number, right: number, ascent: number, descent: number\n\n    for (let n = 0; n < words.length; n++) {\n      let testLine: string\n      if (n === 0) {\n        testLine = words[n]\n      } else {\n        testLine = `${newLine} ${words[n]}`\n      }\n\n      const metrics = ctx.measureText(testLine)\n\n      if (\n        options.wrap &&\n        options.maxWidth &&\n        metrics.width > options.maxWidth &&\n        n > 0\n      ) {\n        const metrics = ctx.measureText(newLine)\n        left = -1 * metrics.actualBoundingBoxLeft\n        right = metrics.actualBoundingBoxRight\n        ascent = metrics.actualBoundingBoxAscent\n        descent = metrics.actualBoundingBoxDescent\n\n        max.left = Math.max(max.left, left)\n        max.right = Math.max(max.right, right)\n        max.ascent = Math.max(max.ascent, ascent)\n        max.descent = Math.max(max.descent, descent)\n        lastDescent = descent\n        lineProps.push({\n          line: newLine,\n          left,\n          right,\n          ascent,\n          descent\n        })\n        newLine = words[n]\n      } else {\n        newLine = testLine\n      }\n    }\n\n    const metrics = ctx.measureText(newLine)\n\n    left = -1 * metrics.actualBoundingBoxLeft\n    right = metrics.actualBoundingBoxRight\n    ascent = metrics.actualBoundingBoxAscent\n    descent = metrics.actualBoundingBoxDescent\n\n    max.left = Math.max(max.left, left)\n    max.right = Math.max(max.right, right)\n    max.ascent = Math.max(max.ascent, ascent)\n    max.descent = Math.max(max.descent, descent)\n    lastDescent = descent\n\n    lineProps.push({\n      line: newLine,\n      left,\n      right,\n      ascent,\n      descent\n    })\n  })\n\n  const lineHeight = max.ascent + max.descent + options.lineSpacing\n\n  const contentWidth = options.width ? options.width : max.left + max.right\n  const contentHeight =\n    lineHeight * lineProps.length -\n    options.lineSpacing -\n    (max.descent - lastDescent)\n\n  if (options.width) {\n    canvas.width = options.width\n  } else {\n    canvas.width =\n      Math.ceil(contentWidth) +\n      options.borderLeftWidth +\n      options.borderRightWidth +\n      options.paddingLeft +\n      options.paddingRight\n  }\n\n  if (options.height) {\n    canvas.height = options.height\n  } else {\n    canvas.height =\n      Math.ceil(contentHeight) +\n      options.borderTopWidth +\n      options.borderBottomWidth +\n      options.paddingTop +\n      options.paddingBottom\n  }\n\n  const hasBorder =\n    options.borderLeftWidth > 0 ||\n    options.borderTopWidth > 0 ||\n    options.borderRightWidth > 0 ||\n    options.borderBottomWidth > 0\n\n  if (hasBorder) {\n    ctx.fillStyle = options.borderColor\n    ctx.fillRect(0, 0, canvas.width, canvas.height)\n  }\n\n  if (options.backgroundColor) {\n    ctx.fillStyle = options.backgroundColor\n    ctx.fillRect(\n      options.borderLeftWidth,\n      options.borderTopWidth,\n      canvas.width - (options.borderLeftWidth + options.borderRightWidth),\n      canvas.height - (options.borderTopWidth + options.borderBottomWidth)\n    )\n  } else if (hasBorder) {\n    ctx.clearRect(\n      options.borderLeftWidth,\n      options.borderTopWidth,\n      canvas.width - (options.borderLeftWidth + options.borderRightWidth),\n      canvas.height - (options.borderTopWidth + options.borderBottomWidth)\n    )\n  }\n\n  ctx.font = options.font\n  ctx.fillStyle = options.textColor\n  ctx.antialias = 'gray'\n  ctx.textAlign = options.textAlign\n  ctx.lineWidth = options.strokeWidth\n  ctx.strokeStyle = options.strokeColor\n\n  let offsetY = options.borderTopWidth + options.paddingTop\n  lineProps.forEach((lineProp) => {\n    // Calculate Y\n    let x = 0\n    const y = max.ascent + offsetY\n\n    // Calculate X\n    switch (options.textAlign) {\n      case 'start':\n      case 'left':\n        x = lineProp.left + options.borderLeftWidth + options.paddingLeft\n        break\n\n      case 'end':\n      case 'right':\n        x =\n          canvas.width -\n          lineProp.left -\n          options.borderRightWidth -\n          options.paddingRight\n        break\n\n      case 'center':\n        x = contentWidth / 2 + options.borderLeftWidth + options.paddingLeft\n        break\n    }\n\n    ctx.fillText(lineProp.line, x, y)\n\n    if (options.strokeWidth > 0) {\n      ctx.strokeText(lineProp.line, x, y)\n    }\n\n    offsetY += lineHeight\n  })\n\n  switch (options.output) {\n    case 'buffer':\n      return canvas.toBuffer()\n    case 'stream': {\n      return Readable.from(canvas.toBuffer())\n    }\n    case 'dataURL':\n      return canvas.toDataURL()\n    case 'canvas':\n      return canvas\n    default:\n      throw new Error(`output type:${options.output} is not supported.`)\n  }\n}\n\nfunction parseOptions(options: TextSvgOptions): ParsedOptions {\n  return {\n    font: options.font || '30px sans-serif',\n    textAlign: options.textAlign || 'left',\n    textColor: options.textColor || options.color || 'black',\n    backgroundColor: options.bgColor || options.backgroundColor || null,\n    lineSpacing: options.lineSpacing || 0,\n\n    strokeWidth: options.strokeWidth || 0,\n    strokeColor: options.strokeColor || 'white',\n\n    paddingLeft: options.paddingLeft || options.padding || 0,\n    paddingTop: options.paddingTop || options.padding || 0,\n    paddingRight: options.paddingRight || options.padding || 0,\n    paddingBottom: options.paddingBottom || options.padding || 0,\n\n    borderLeftWidth: options.borderLeftWidth || options.borderWidth || 0,\n    borderTopWidth: options.borderTopWidth || options.borderWidth || 0,\n    borderBottomWidth: options.borderBottomWidth || options.borderWidth || 0,\n    borderRightWidth: options.borderRightWidth || options.borderWidth || 0,\n    borderColor: options.borderColor ?? 'black',\n\n    localFontName: options.localFontName || null,\n    localFontPath: options.localFontPath || null,\n\n    wrap: options.wrap || false,\n    maxWidth: options.maxWidth || undefined,\n\n    output: options.output || 'buffer',\n\n    width: options.width || undefined,\n    height: options.height || undefined\n  }\n}\n\nexport default text2svg\nexport { type ParsedOptions, type TextSvgOptions, text2svg }\n"],"mappings":";;;AASA,MAAM,YACJ,MACA,eAA+B,CAAC,MACQ;CAExC,MAAM,UAAyB,aAAa,YAAY;CAGxD,IAAI,QAAQ,iBAAiB,QAAQ,eACnC,aAAa,QAAQ,eAAe,EAClC,QAAQ,QAAQ,cAClB,CAAC;CAGH,MAAM,SAAS,aAAa,GAAG,GAAG,KAAK;CACvC,MAAM,MAAM,OAAO,WAAW,IAAI;CAElC,MAAM,MAAkB;EACtB,MAAM;EACN,OAAO;EACP,QAAQ;EACR,SAAS;CACX;CAEA,IAAI,cAAsB;CAC1B,MAAM,YAA4B,CAAC;CACnC,IAAI,OAAO,QAAQ;CACnB,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,SAAS;EACjC,MAAM,QAAQ,KAAK,MAAM,GAAG;EAC5B,IAAI,UAAU;EACd,IAAI,MAAc,OAAe,QAAgB;EAEjD,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,IAAI;GACJ,IAAI,MAAM,GACR,WAAW,MAAM;QAEjB,WAAW,GAAG,QAAQ,GAAG,MAAM;GAGjC,MAAM,UAAU,IAAI,YAAY,QAAQ;GAExC,IACE,QAAQ,QACR,QAAQ,YACR,QAAQ,QAAQ,QAAQ,YACxB,IAAI,GACJ;IACA,MAAM,UAAU,IAAI,YAAY,OAAO;IACvC,OAAO,KAAK,QAAQ;IACpB,QAAQ,QAAQ;IAChB,SAAS,QAAQ;IACjB,UAAU,QAAQ;IAElB,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,IAAI;IAClC,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,KAAK;IACrC,IAAI,SAAS,KAAK,IAAI,IAAI,QAAQ,MAAM;IACxC,IAAI,UAAU,KAAK,IAAI,IAAI,SAAS,OAAO;IAC3C,cAAc;IACd,UAAU,KAAK;KACb,MAAM;KACN;KACA;KACA;KACA;IACF,CAAC;IACD,UAAU,MAAM;GAClB,OACE,UAAU;EAEd;EAEA,MAAM,UAAU,IAAI,YAAY,OAAO;EAEvC,OAAO,KAAK,QAAQ;EACpB,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB,UAAU,QAAQ;EAElB,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,IAAI;EAClC,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,KAAK;EACrC,IAAI,SAAS,KAAK,IAAI,IAAI,QAAQ,MAAM;EACxC,IAAI,UAAU,KAAK,IAAI,IAAI,SAAS,OAAO;EAC3C,cAAc;EAEd,UAAU,KAAK;GACb,MAAM;GACN;GACA;GACA;GACA;EACF,CAAC;CACH,CAAC;CAED,MAAM,aAAa,IAAI,SAAS,IAAI,UAAU,QAAQ;CAEtD,MAAM,eAAe,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,OAAO,IAAI;CACpE,MAAM,gBACJ,aAAa,UAAU,SACvB,QAAQ,eACP,IAAI,UAAU;CAEjB,IAAI,QAAQ,OACV,OAAO,QAAQ,QAAQ;MAEvB,OAAO,QACL,KAAK,KAAK,YAAY,IACtB,QAAQ,kBACR,QAAQ,mBACR,QAAQ,cACR,QAAQ;CAGZ,IAAI,QAAQ,QACV,OAAO,SAAS,QAAQ;MAExB,OAAO,SACL,KAAK,KAAK,aAAa,IACvB,QAAQ,iBACR,QAAQ,oBACR,QAAQ,aACR,QAAQ;CAGZ,MAAM,YACJ,QAAQ,kBAAkB,KAC1B,QAAQ,iBAAiB,KACzB,QAAQ,mBAAmB,KAC3B,QAAQ,oBAAoB;CAE9B,IAAI,WAAW;EACb,IAAI,YAAY,QAAQ;EACxB,IAAI,SAAS,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;CAChD;CAEA,IAAI,QAAQ,iBAAiB;EAC3B,IAAI,YAAY,QAAQ;EACxB,IAAI,SACF,QAAQ,iBACR,QAAQ,gBACR,OAAO,SAAS,QAAQ,kBAAkB,QAAQ,mBAClD,OAAO,UAAU,QAAQ,iBAAiB,QAAQ,kBACpD;CACF,OAAO,IAAI,WACT,IAAI,UACF,QAAQ,iBACR,QAAQ,gBACR,OAAO,SAAS,QAAQ,kBAAkB,QAAQ,mBAClD,OAAO,UAAU,QAAQ,iBAAiB,QAAQ,kBACpD;CAGF,IAAI,OAAO,QAAQ;CACnB,IAAI,YAAY,QAAQ;CACxB,IAAI,YAAY;CAChB,IAAI,YAAY,QAAQ;CACxB,IAAI,YAAY,QAAQ;CACxB,IAAI,cAAc,QAAQ;CAE1B,IAAI,UAAU,QAAQ,iBAAiB,QAAQ;CAC/C,UAAU,SAAS,aAAa;EAE9B,IAAI,IAAI;EACR,MAAM,IAAI,IAAI,SAAS;EAGvB,QAAQ,QAAQ,WAAhB;GACE,KAAK;GACL,KAAK;IACH,IAAI,SAAS,OAAO,QAAQ,kBAAkB,QAAQ;IACtD;GAEF,KAAK;GACL,KAAK;IACH,IACE,OAAO,QACP,SAAS,OACT,QAAQ,mBACR,QAAQ;IACV;GAEF,KAAK;IACH,IAAI,eAAe,IAAI,QAAQ,kBAAkB,QAAQ;IACzD;EACJ;EAEA,IAAI,SAAS,SAAS,MAAM,GAAG,CAAC;EAEhC,IAAI,QAAQ,cAAc,GACxB,IAAI,WAAW,SAAS,MAAM,GAAG,CAAC;EAGpC,WAAW;CACb,CAAC;CAED,QAAQ,QAAQ,QAAhB;EACE,KAAK,UACH,OAAO,OAAO,SAAS;EACzB,KAAK,UACH,OAAO,SAAS,KAAK,OAAO,SAAS,CAAC;EAExC,KAAK,WACH,OAAO,OAAO,UAAU;EAC1B,KAAK,UACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,eAAe,QAAQ,OAAO,mBAAmB;CACrE;AACF;AAEA,SAAS,aAAa,SAAwC;CAC5D,OAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,WAAW,QAAQ,aAAa;EAChC,WAAW,QAAQ,aAAa,QAAQ,SAAS;EACjD,iBAAiB,QAAQ,WAAW,QAAQ,mBAAmB;EAC/D,aAAa,QAAQ,eAAe;EAEpC,aAAa,QAAQ,eAAe;EACpC,aAAa,QAAQ,eAAe;EAEpC,aAAa,QAAQ,eAAe,QAAQ,WAAW;EACvD,YAAY,QAAQ,cAAc,QAAQ,WAAW;EACrD,cAAc,QAAQ,gBAAgB,QAAQ,WAAW;EACzD,eAAe,QAAQ,iBAAiB,QAAQ,WAAW;EAE3D,iBAAiB,QAAQ,mBAAmB,QAAQ,eAAe;EACnE,gBAAgB,QAAQ,kBAAkB,QAAQ,eAAe;EACjE,mBAAmB,QAAQ,qBAAqB,QAAQ,eAAe;EACvE,kBAAkB,QAAQ,oBAAoB,QAAQ,eAAe;EACrE,aAAa,QAAQ,eAAe;EAEpC,eAAe,QAAQ,iBAAiB;EACxC,eAAe,QAAQ,iBAAiB;EAExC,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ,YAAY,KAAA;EAE9B,QAAQ,QAAQ,UAAU;EAE1B,OAAO,QAAQ,SAAS,KAAA;EACxB,QAAQ,QAAQ,UAAU,KAAA;CAC5B;AACF"}