"use strict";

var SVGtoPDF = function (doc, svg, x, y, options) {
  "use strict";

  const NamedColors = {
    aliceblue: [240, 248, 255],
    antiquewhite: [250, 235, 215],
    aqua: [0, 255, 255],
    aquamarine: [127, 255, 212],
    azure: [240, 255, 255],
    beige: [245, 245, 220],
    bisque: [255, 228, 196],
    black: [0, 0, 0],
    blanchedalmond: [255, 235, 205],
    blue: [0, 0, 255],
    blueviolet: [138, 43, 226],
    brown: [165, 42, 42],
    burlywood: [222, 184, 135],
    cadetblue: [95, 158, 160],
    chartreuse: [127, 255, 0],
    chocolate: [210, 105, 30],
    coral: [255, 127, 80],
    cornflowerblue: [100, 149, 237],
    cornsilk: [255, 248, 220],
    crimson: [220, 20, 60],
    cyan: [0, 255, 255],
    darkblue: [0, 0, 139],
    darkcyan: [0, 139, 139],
    darkgoldenrod: [184, 134, 11],
    darkgray: [169, 169, 169],
    darkgrey: [169, 169, 169],
    darkgreen: [0, 100, 0],
    darkkhaki: [189, 183, 107],
    darkmagenta: [139, 0, 139],
    darkolivegreen: [85, 107, 47],
    darkorange: [255, 140, 0],
    darkorchid: [153, 50, 204],
    darkred: [139, 0, 0],
    darksalmon: [233, 150, 122],
    darkseagreen: [143, 188, 143],
    darkslateblue: [72, 61, 139],
    darkslategray: [47, 79, 79],
    darkslategrey: [47, 79, 79],
    darkturquoise: [0, 206, 209],
    darkviolet: [148, 0, 211],
    deeppink: [255, 20, 147],
    deepskyblue: [0, 191, 255],
    dimgray: [105, 105, 105],
    dimgrey: [105, 105, 105],
    dodgerblue: [30, 144, 255],
    firebrick: [178, 34, 34],
    floralwhite: [255, 250, 240],
    forestgreen: [34, 139, 34],
    fuchsia: [255, 0, 255],
    gainsboro: [220, 220, 220],
    ghostwhite: [248, 248, 255],
    gold: [255, 215, 0],
    goldenrod: [218, 165, 32],
    gray: [128, 128, 128],
    grey: [128, 128, 128],
    green: [0, 128, 0],
    greenyellow: [173, 255, 47],
    honeydew: [240, 255, 240],
    hotpink: [255, 105, 180],
    indianred: [205, 92, 92],
    indigo: [75, 0, 130],
    ivory: [255, 255, 240],
    khaki: [240, 230, 140],
    lavender: [230, 230, 250],
    lavenderblush: [255, 240, 245],
    lawngreen: [124, 252, 0],
    lemonchiffon: [255, 250, 205],
    lightblue: [173, 216, 230],
    lightcoral: [240, 128, 128],
    lightcyan: [224, 255, 255],
    lightgoldenrodyellow: [250, 250, 210],
    lightgray: [211, 211, 211],
    lightgrey: [211, 211, 211],
    lightgreen: [144, 238, 144],
    lightpink: [255, 182, 193],
    lightsalmon: [255, 160, 122],
    lightseagreen: [32, 178, 170],
    lightskyblue: [135, 206, 250],
    lightslategray: [119, 136, 153],
    lightslategrey: [119, 136, 153],
    lightsteelblue: [176, 196, 222],
    lightyellow: [255, 255, 224],
    lime: [0, 255, 0],
    limegreen: [50, 205, 50],
    linen: [250, 240, 230],
    magenta: [255, 0, 255],
    maroon: [128, 0, 0],
    mediumaquamarine: [102, 205, 170],
    mediumblue: [0, 0, 205],
    mediumorchid: [186, 85, 211],
    mediumpurple: [147, 112, 219],
    mediumseagreen: [60, 179, 113],
    mediumslateblue: [123, 104, 238],
    mediumspringgreen: [0, 250, 154],
    mediumturquoise: [72, 209, 204],
    mediumvioletred: [199, 21, 133],
    midnightblue: [25, 25, 112],
    mintcream: [245, 255, 250],
    mistyrose: [255, 228, 225],
    moccasin: [255, 228, 181],
    navajowhite: [255, 222, 173],
    navy: [0, 0, 128],
    oldlace: [253, 245, 230],
    olive: [128, 128, 0],
    olivedrab: [107, 142, 35],
    orange: [255, 165, 0],
    orangered: [255, 69, 0],
    orchid: [218, 112, 214],
    palegoldenrod: [238, 232, 170],
    palegreen: [152, 251, 152],
    paleturquoise: [175, 238, 238],
    palevioletred: [219, 112, 147],
    papayawhip: [255, 239, 213],
    peachpuff: [255, 218, 185],
    peru: [205, 133, 63],
    pink: [255, 192, 203],
    plum: [221, 160, 221],
    powderblue: [176, 224, 230],
    purple: [128, 0, 128],
    rebeccapurple: [102, 51, 153],
    red: [255, 0, 0],
    rosybrown: [188, 143, 143],
    royalblue: [65, 105, 225],
    saddlebrown: [139, 69, 19],
    salmon: [250, 128, 114],
    sandybrown: [244, 164, 96],
    seagreen: [46, 139, 87],
    seashell: [255, 245, 238],
    sienna: [160, 82, 45],
    silver: [192, 192, 192],
    skyblue: [135, 206, 235],
    slateblue: [106, 90, 205],
    slategray: [112, 128, 144],
    slategrey: [112, 128, 144],
    snow: [255, 250, 250],
    springgreen: [0, 255, 127],
    steelblue: [70, 130, 180],
    tan: [210, 180, 140],
    teal: [0, 128, 128],
    thistle: [216, 191, 216],
    tomato: [255, 99, 71],
    turquoise: [64, 224, 208],
    violet: [238, 130, 238],
    wheat: [245, 222, 179],
    white: [255, 255, 255],
    whitesmoke: [245, 245, 245],
    yellow: [255, 255, 0]
  };
  const DefaultColors = {
    black: [NamedColors.black, 1],
    white: [NamedColors.white, 1],
    transparent: [NamedColors.black, 0]
  };
  const Entities = {
    quot: 34,
    amp: 38,
    lt: 60,
    gt: 62,
    apos: 39,
    OElig: 338,
    oelig: 339,
    Scaron: 352,
    scaron: 353,
    Yuml: 376,
    circ: 710,
    tilde: 732,
    ensp: 8194,
    emsp: 8195,
    thinsp: 8201,
    zwnj: 8204,
    zwj: 8205,
    lrm: 8206,
    rlm: 8207,
    ndash: 8211,
    mdash: 8212,
    lsquo: 8216,
    rsquo: 8217,
    sbquo: 8218,
    ldquo: 8220,
    rdquo: 8221,
    bdquo: 8222,
    dagger: 8224,
    Dagger: 8225,
    permil: 8240,
    lsaquo: 8249,
    rsaquo: 8250,
    euro: 8364,
    nbsp: 160,
    iexcl: 161,
    cent: 162,
    pound: 163,
    curren: 164,
    yen: 165,
    brvbar: 166,
    sect: 167,
    uml: 168,
    copy: 169,
    ordf: 170,
    laquo: 171,
    not: 172,
    shy: 173,
    reg: 174,
    macr: 175,
    deg: 176,
    plusmn: 177,
    sup2: 178,
    sup3: 179,
    acute: 180,
    micro: 181,
    para: 182,
    middot: 183,
    cedil: 184,
    sup1: 185,
    ordm: 186,
    raquo: 187,
    frac14: 188,
    frac12: 189,
    frac34: 190,
    iquest: 191,
    Agrave: 192,
    Aacute: 193,
    Acirc: 194,
    Atilde: 195,
    Auml: 196,
    Aring: 197,
    AElig: 198,
    Ccedil: 199,
    Egrave: 200,
    Eacute: 201,
    Ecirc: 202,
    Euml: 203,
    Igrave: 204,
    Iacute: 205,
    Icirc: 206,
    Iuml: 207,
    ETH: 208,
    Ntilde: 209,
    Ograve: 210,
    Oacute: 211,
    Ocirc: 212,
    Otilde: 213,
    Ouml: 214,
    times: 215,
    Oslash: 216,
    Ugrave: 217,
    Uacute: 218,
    Ucirc: 219,
    Uuml: 220,
    Yacute: 221,
    THORN: 222,
    szlig: 223,
    agrave: 224,
    aacute: 225,
    acirc: 226,
    atilde: 227,
    auml: 228,
    aring: 229,
    aelig: 230,
    ccedil: 231,
    egrave: 232,
    eacute: 233,
    ecirc: 234,
    euml: 235,
    igrave: 236,
    iacute: 237,
    icirc: 238,
    iuml: 239,
    eth: 240,
    ntilde: 241,
    ograve: 242,
    oacute: 243,
    ocirc: 244,
    otilde: 245,
    ouml: 246,
    divide: 247,
    oslash: 248,
    ugrave: 249,
    uacute: 250,
    ucirc: 251,
    uuml: 252,
    yacute: 253,
    thorn: 254,
    yuml: 255,
    fnof: 402,
    Alpha: 913,
    Beta: 914,
    Gamma: 915,
    Delta: 916,
    Epsilon: 917,
    Zeta: 918,
    Eta: 919,
    Theta: 920,
    Iota: 921,
    Kappa: 922,
    Lambda: 923,
    Mu: 924,
    Nu: 925,
    Xi: 926,
    Omicron: 927,
    Pi: 928,
    Rho: 929,
    Sigma: 931,
    Tau: 932,
    Upsilon: 933,
    Phi: 934,
    Chi: 935,
    Psi: 936,
    Omega: 937,
    alpha: 945,
    beta: 946,
    gamma: 947,
    delta: 948,
    epsilon: 949,
    zeta: 950,
    eta: 951,
    theta: 952,
    iota: 953,
    kappa: 954,
    lambda: 955,
    mu: 956,
    nu: 957,
    xi: 958,
    omicron: 959,
    pi: 960,
    rho: 961,
    sigmaf: 962,
    sigma: 963,
    tau: 964,
    upsilon: 965,
    phi: 966,
    chi: 967,
    psi: 968,
    omega: 969,
    thetasym: 977,
    upsih: 978,
    piv: 982,
    bull: 8226,
    hellip: 8230,
    prime: 8242,
    Prime: 8243,
    oline: 8254,
    frasl: 8260,
    weierp: 8472,
    image: 8465,
    real: 8476,
    trade: 8482,
    alefsym: 8501,
    larr: 8592,
    uarr: 8593,
    rarr: 8594,
    darr: 8595,
    harr: 8596,
    crarr: 8629,
    lArr: 8656,
    uArr: 8657,
    rArr: 8658,
    dArr: 8659,
    hArr: 8660,
    forall: 8704,
    part: 8706,
    exist: 8707,
    empty: 8709,
    nabla: 8711,
    isin: 8712,
    notin: 8713,
    ni: 8715,
    prod: 8719,
    sum: 8721,
    minus: 8722,
    lowast: 8727,
    radic: 8730,
    prop: 8733,
    infin: 8734,
    ang: 8736,
    and: 8743,
    or: 8744,
    cap: 8745,
    cup: 8746,
    int: 8747,
    there4: 8756,
    sim: 8764,
    cong: 8773,
    asymp: 8776,
    ne: 8800,
    equiv: 8801,
    le: 8804,
    ge: 8805,
    sub: 8834,
    sup: 8835,
    nsub: 8836,
    sube: 8838,
    supe: 8839,
    oplus: 8853,
    otimes: 8855,
    perp: 8869,
    sdot: 8901,
    lceil: 8968,
    rceil: 8969,
    lfloor: 8970,
    rfloor: 8971,
    lang: 9001,
    rang: 9002,
    loz: 9674,
    spades: 9824,
    clubs: 9827,
    hearts: 9829,
    diams: 9830
  };
  const PathArguments = {
    A: 7,
    a: 7,
    C: 6,
    c: 6,
    H: 1,
    h: 1,
    L: 2,
    l: 2,
    M: 2,
    m: 2,
    Q: 4,
    q: 4,
    S: 4,
    s: 4,
    T: 2,
    t: 2,
    V: 1,
    v: 1,
    Z: 0,
    z: 0
  };
  const PathFlags = {
    A3: true,
    A4: true,
    a3: true,
    a4: true
  };
  const Properties = {
    'color': {
      inherit: true,
      initial: undefined
    },
    'visibility': {
      inherit: true,
      initial: 'visible',
      values: {
        'hidden': 'hidden',
        'collapse': 'hidden',
        'visible': 'visible'
      }
    },
    'fill': {
      inherit: true,
      initial: DefaultColors.black
    },
    'stroke': {
      inherit: true,
      initial: 'none'
    },
    'stop-color': {
      inherit: false,
      initial: DefaultColors.black
    },
    'fill-opacity': {
      inherit: true,
      initial: 1
    },
    'stroke-opacity': {
      inherit: true,
      initial: 1
    },
    'stop-opacity': {
      inherit: false,
      initial: 1
    },
    'fill-rule': {
      inherit: true,
      initial: 'nonzero',
      values: {
        'nonzero': 'nonzero',
        'evenodd': 'evenodd'
      }
    },
    'clip-rule': {
      inherit: true,
      initial: 'nonzero',
      values: {
        'nonzero': 'nonzero',
        'evenodd': 'evenodd'
      }
    },
    'stroke-width': {
      inherit: true,
      initial: 1
    },
    'stroke-dasharray': {
      inherit: true,
      initial: []
    },
    'stroke-dashoffset': {
      inherit: true,
      initial: 0
    },
    'stroke-miterlimit': {
      inherit: true,
      initial: 4
    },
    'stroke-linejoin': {
      inherit: true,
      initial: 'miter',
      values: {
        'miter': 'miter',
        'round': 'round',
        'bevel': 'bevel'
      }
    },
    'stroke-linecap': {
      inherit: true,
      initial: 'butt',
      values: {
        'butt': 'butt',
        'round': 'round',
        'square': 'square'
      }
    },
    'font-size': {
      inherit: true,
      initial: 16,
      values: {
        'xx-small': 9,
        'x-small': 10,
        'small': 13,
        'medium': 16,
        'large': 18,
        'x-large': 24,
        'xx-large': 32
      }
    },
    'font-family': {
      inherit: true,
      initial: 'sans-serif'
    },
    'font-weight': {
      inherit: true,
      initial: 'normal',
      values: {
        '600': 'bold',
        '700': 'bold',
        '800': 'bold',
        '900': 'bold',
        'bold': 'bold',
        'bolder': 'bold',
        '500': 'normal',
        '400': 'normal',
        '300': 'normal',
        '200': 'normal',
        '100': 'normal',
        'normal': 'normal',
        'lighter': 'normal'
      }
    },
    'font-style': {
      inherit: true,
      initial: 'normal',
      values: {
        'italic': 'italic',
        'oblique': 'italic',
        'normal': 'normal'
      }
    },
    'text-anchor': {
      inherit: true,
      initial: 'start',
      values: {
        'start': 'start',
        'middle': 'middle',
        'end': 'end'
      }
    },
    'direction': {
      inherit: true,
      initial: 'ltr',
      values: {
        'ltr': 'ltr',
        'rtl': 'rtl'
      }
    },
    'dominant-baseline': {
      inherit: true,
      initial: 'baseline',
      values: {
        'auto': 'baseline',
        'baseline': 'baseline',
        'before-edge': 'before-edge',
        'text-before-edge': 'before-edge',
        'middle': 'middle',
        'central': 'central',
        'after-edge': 'after-edge',
        'text-after-edge': 'after-edge',
        'ideographic': 'ideographic',
        'alphabetic': 'alphabetic',
        'hanging': 'hanging',
        'mathematical': 'mathematical'
      }
    },
    'alignment-baseline': {
      inherit: false,
      initial: undefined,
      values: {
        'auto': 'baseline',
        'baseline': 'baseline',
        'before-edge': 'before-edge',
        'text-before-edge': 'before-edge',
        'middle': 'middle',
        'central': 'central',
        'after-edge': 'after-edge',
        'text-after-edge': 'after-edge',
        'ideographic': 'ideographic',
        'alphabetic': 'alphabetic',
        'hanging': 'hanging',
        'mathematical': 'mathematical'
      }
    },
    'baseline-shift': {
      inherit: true,
      initial: 'baseline',
      values: {
        'baseline': 'baseline',
        'sub': 'sub',
        'super': 'super'
      }
    },
    'word-spacing': {
      inherit: true,
      initial: 0,
      values: {
        normal: 0
      }
    },
    'letter-spacing': {
      inherit: true,
      initial: 0,
      values: {
        normal: 0
      }
    },
    'text-decoration': {
      inherit: false,
      initial: 'none',
      values: {
        'none': 'none',
        'underline': 'underline',
        'overline': 'overline',
        'line-through': 'line-through'
      }
    },
    'xml:space': {
      inherit: true,
      initial: 'default',
      css: 'white-space',
      values: {
        'preserve': 'preserve',
        'default': 'default',
        'pre': 'preserve',
        'pre-line': 'preserve',
        'pre-wrap': 'preserve',
        'nowrap': 'default'
      }
    },
    'marker-start': {
      inherit: true,
      initial: 'none'
    },
    'marker-mid': {
      inherit: true,
      initial: 'none'
    },
    'marker-end': {
      inherit: true,
      initial: 'none'
    },
    'opacity': {
      inherit: false,
      initial: 1
    },
    'transform': {
      inherit: false,
      initial: [1, 0, 0, 1, 0, 0]
    },
    'display': {
      inherit: false,
      initial: 'inline',
      values: {
        'none': 'none',
        'inline': 'inline',
        'block': 'inline'
      }
    },
    'clip-path': {
      inherit: false,
      initial: 'none'
    },
    'mask': {
      inherit: false,
      initial: 'none'
    },
    'overflow': {
      inherit: false,
      initial: 'hidden',
      values: {
        'hidden': 'hidden',
        'scroll': 'hidden',
        'visible': 'visible'
      }
    },
    'vector-effect': {
      inherit: true,
      initial: 'none',
      values: {
        'none': 'none',
        'non-scaling-stroke': 'non-scaling-stroke'
      }
    }
  };
  function docBeginGroup(bbox) {
    let group = new function PDFGroup() {}();
    group.name = 'G' + (doc._groupCount = (doc._groupCount || 0) + 1);
    group.resources = doc.ref();
    group.xobj = doc.ref({
      Type: 'XObject',
      Subtype: 'Form',
      FormType: 1,
      BBox: bbox,
      Group: {
        S: 'Transparency',
        CS: 'DeviceRGB',
        I: true,
        K: false
      },
      Resources: group.resources
    });
    group.xobj.write('');
    group.savedMatrix = doc._ctm;
    group.savedPage = doc.page;
    groupStack.push(group);
    doc._ctm = [1, 0, 0, 1, 0, 0];
    doc.page = {
      width: doc.page.width,
      height: doc.page.height,
      write: function (data) {
        group.xobj.write(data);
      },
      fonts: {},
      xobjects: {},
      ext_gstates: {},
      patterns: {}
    };
    return group;
  }
  function docEndGroup(group) {
    if (group !== groupStack.pop()) {
      throw 'Group not matching';
    }
    if (Object.keys(doc.page.fonts).length) {
      group.resources.data.Font = doc.page.fonts;
    }
    if (Object.keys(doc.page.xobjects).length) {
      group.resources.data.XObject = doc.page.xobjects;
    }
    if (Object.keys(doc.page.ext_gstates).length) {
      group.resources.data.ExtGState = doc.page.ext_gstates;
    }
    if (Object.keys(doc.page.patterns).length) {
      group.resources.data.Pattern = doc.page.patterns;
    }
    group.resources.end();
    group.xobj.end();
    doc._ctm = group.savedMatrix;
    doc.page = group.savedPage;
  }
  function docInsertGroup(group) {
    doc.page.xobjects[group.name] = group.xobj;
    doc.addContent('/' + group.name + ' Do');
  }
  function docApplyMask(group, clip) {
    let name = 'M' + (doc._maskCount = (doc._maskCount || 0) + 1);
    let gstate = doc.ref({
      Type: 'ExtGState',
      CA: 1,
      ca: 1,
      BM: 'Normal',
      SMask: {
        S: 'Luminosity',
        G: group.xobj,
        BC: clip ? [0, 0, 0] : [1, 1, 1]
      }
    });
    gstate.end();
    doc.page.ext_gstates[name] = gstate;
    doc.addContent('/' + name + ' gs');
  }
  function docCreatePattern(group, dx, dy, matrix) {
    let pattern = {
      type: 'PDFPattern',
      group: group,
      dx: dx,
      dy: dy,
      matrix: matrix || [1, 0, 0, 1, 0, 0]
    };
    return pattern;
  }
  function docUsePattern(pattern, stroke) {
    let name = 'P' + (doc._patternCount = (doc._patternCount || 0) + 1);
    let ref = doc.ref({
      Type: 'Pattern',
      PatternType: 1,
      PaintType: 1,
      TilingType: 2,
      BBox: [0, 0, pattern.dx, pattern.dy],
      XStep: pattern.dx,
      YStep: pattern.dy,
      Matrix: multiplyMatrix(doc._ctm, pattern.matrix),
      Resources: {
        ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
        XObject: function () {
          let temp = {};
          temp[pattern.group.name] = pattern.group.xobj;
          return temp;
        }()
      }
    });
    ref.write('/' + pattern.group.name + ' Do');
    ref.end();
    doc.page.patterns[name] = ref;
    if (stroke) {
      doc.addContent('/Pattern CS');
      doc.addContent('/' + name + ' SCN');
    } else {
      doc.addContent('/Pattern cs');
      doc.addContent('/' + name + ' scn');
    }
  }
  function docBeginText(font, size) {
    if (!doc.page.fonts[font.id]) {
      doc.page.fonts[font.id] = font.ref();
    }
    doc.addContent('BT').addContent('/' + font.id + ' ' + size + ' Tf');
  }
  function docSetTextMatrix(a, b, c, d, e, f) {
    doc.addContent(validateNumber(a) + ' ' + validateNumber(b) + ' ' + validateNumber(-c) + ' ' + validateNumber(-d) + ' ' + validateNumber(e) + ' ' + validateNumber(f) + ' Tm');
  }
  function docSetTextMode(fill, stroke) {
    let mode = fill && stroke ? 2 : stroke ? 1 : fill ? 0 : 3;
    doc.addContent(mode + ' Tr');
  }
  function docWriteGlyphs(positions, font) {
    let commands = [];
    let commandStr = '';
    const skew = font.fauxItalic ? -0.25 : 0;

    // Add the given character to the 'TJ' command string.
    function addChar(char) {
      commandStr += char.glyph;
      if (char.kern === 0) return;
      commands.push(`<${commandStr}> ${validateNumber(char.kern)}`);
      commandStr = '';
    }
    ;

    // Flush the current TJ command string to the output stream.
    function flush() {
      if (commandStr.length) {
        commands.push(`<${commandStr}> 0`);
        commandStr = '';
      }
      if (commands.length) {
        doc.addContent(`[${commands.join(' ')}] TJ`);
        commands = [];
      }
    }
    ;
    for (let i = 0; i < positions.length; i++) {
      const pos = positions[i];
      if (pos.hidden || isEqual(pos.width, 0)) {
        flush();
        continue;
      }
      if (pos.continuous) {
        addChar(pos);
        continue;
      }

      // If this character is non-continuous, flush the command buffer.
      flush();

      // Start a new TJ command after writing a Text Matrix (Tm)
      const cos = Math.cos(pos.rotate);
      const sin = Math.sin(pos.rotate);
      docSetTextMatrix(cos * pos.scale, sin * pos.scale, cos * skew - sin, sin * skew + cos, pos.x, pos.y);
      addChar(pos);
    }
    ;

    // Flush any remaining characters in the buffer.
    flush();
  }
  function docEndText() {
    doc.addContent('ET');
  }
  function docFillColor(color) {
    if (color[0].type === 'PDFPattern') {
      doc.fillOpacity(color[1]);
      docUsePattern(color[0], false);
    } else {
      doc.fillColor(color[0], color[1]);
    }
  }
  function docStrokeColor(color) {
    if (color[0].type === 'PDFPattern') {
      doc.strokeOpacity(color[1]);
      docUsePattern(color[0], true);
    } else {
      doc.strokeColor(color[0], color[1]);
    }
  }
  // PDFKit doesn't accept any 0s in the dash array, but that's perfectly
  // valid in SVG. So this function applys a dash array and offset, detecting
  // any 0s in the dash array and updating it and the dash offset as needed to
  // remove the zeros, but preserve the end result.
  //
  // `dashArray` must have an even number of elements
  function docApplyDash(dashArray, dashOffset) {
    let index;
    // Anytime there's a 0 that isn't the first or last element of the array,
    // we can remove it by combining the previous or next value. If it's a
    // dash, then it's a zero-length dash between two spaces, so the dash can
    // be eliminated and spaces combined by summing them, replacing all three
    // values with the sum of the two spaces. If the 0 value is a space, then
    // it's a zero-length space between two dashes, and the dashes can be
    // similarly combined. So first we run that logic iteratively to remove
    // all the 0s from the dash array that aren't the first or last element.
    // Note that because we replace 3 values with one value, this doesn't
    // change the even-ness of the length of dashArray.
    while ((index = dashArray.slice(1, -1).indexOf(0)) !== -1) {
      let actualIndex = index + 1;
      let replacementValue = dashArray[actualIndex - 1] + dashArray[actualIndex + 1];
      dashArray = dashArray.slice(0, actualIndex - 1).concat([replacementValue]).concat(dashArray.slice(actualIndex + 2));
    }

    // The stroke array only having two elements (a dash value and space
    // value) is a special case.
    if (dashArray.length === 2) {
      if (dashArray[0] === 0) {
        // Regardless of the space value, the dash length is zero, so we're
        // not actually drawing a stroke. We can't describe that in a
        // doc.dash() call in a way that PDFKit will accept, so we set the
        // stroke opacity to zero as our best approximation.
        doc.strokeOpacity(0);
        return;
      } else if (dashArray[1] === 0) {
        // Regardless of the dash value, the space value is zero, meaning
        // we're actually drawing a solid stroke, not a dashed one. We can
        // make this happen by just emptying out the dash array.
        dashArray = [];
      }
    } else {
      if (dashArray[0] === 0) {
        // The first dash is zero-length. We fix this by combining the first
        // space (just after the first dash) with the last space and updating
        // the dash offset accordingly. For example, if we had
        //
        // [ 0 4 3 2 5 1 ] (dash offset 0)
        //
        // ␣␣␣␣---␣␣-----␣
        // ⎸
        //
        // we'd end up with
        //
        // [ 3 2 5 5 ] (dash offset -4)
        //
        // ---␣␣-----␣␣␣␣␣
        //            ⎸
        //
        // Another example where the dash array also ends with a 0:
        //
        // [ 0 4 3 2 5 0 ] (dash offset 0)
        //
        // ␣␣␣␣---␣␣-----
        // ⎸
        //
        // we'd end up with
        //
        // [ 3 2 5 4 ] (dash offset -4)
        //
        // ---␣␣-----␣␣␣␣
        //           ⎸
        dashOffset -= dashArray[1];
        dashArray[dashArray.length - 1] += dashArray[1];
        dashArray = dashArray.slice(2);
      }
      if (dashArray[dashArray.length - 1] === 0) {
        // The last space is zero-length. We fix this by combining the last dash
        // (just before the last space) with the first dash and updating the
        // dash offset accordingly. For example, if we had
        //
        // [ 1 4 3 2 5 0 ] (dash offset 0)
        //
        // -␣␣␣␣---␣␣-----
        // ⎸
        //
        // we'd end up with
        //
        // [ 6 4 3 2 ] (dash offset 5)
        //
        // ------␣␣␣␣---␣␣
        //      ⎸
        //
        dashOffset += dashArray[dashArray.length - 2];
        dashArray[0] += dashArray[dashArray.length - 2];
        dashArray = dashArray.slice(0, -2);
      }
    }

    // Ensure the dash offset is non-negative (because of crbug.com/660850).
    // First compute the total length of the dash array so we can add it to
    // dash offset until dash offset is non-negative.
    let length = 0;
    for (let i = 0; i < dashArray.length; i++) {
      length += dashArray[i];
    }
    if (length > 0) {
      while (dashOffset < 0) {
        dashOffset += length;
      }
    }
    doc.dash(dashArray, {
      phase: dashOffset
    });
  }
  function docInsertLink(x, y, w, h, url) {
    let ref = doc.ref({
      Type: 'Annot',
      Subtype: 'Link',
      Rect: [x, y, w, h],
      Border: [0, 0, 0],
      A: {
        S: 'URI',
        URI: new String(url)
      }
    });
    ref.end();
    links.push(ref);
  }
  function parseXml(xml) {
    let SvgNode = function (tag, type, value, error) {
      this.error = error;
      this.nodeName = tag;
      this.nodeValue = value;
      this.nodeType = type;
      this.attributes = Object.create(null);
      this.childNodes = [];
      this.parentNode = null;
      this.id = '';
      this.textContent = '';
      this.classList = [];
    };
    SvgNode.prototype.getAttribute = function (attr) {
      return this.attributes[attr] != null ? this.attributes[attr] : null;
    };
    SvgNode.prototype.getElementById = function (id) {
      let result = null;
      (function recursive(node) {
        if (result) {
          return;
        }
        if (node.nodeType === 1) {
          if (node.id === id) {
            result = node;
          }
          for (let i = 0; i < node.childNodes.length; i++) {
            recursive(node.childNodes[i]);
          }
        }
      })(this);
      return result;
    };
    SvgNode.prototype.getElementsByTagName = function (tag) {
      let result = [];
      (function recursive(node) {
        if (node.nodeType === 1) {
          if (node.nodeName === tag) {
            result.push(node);
          }
          for (let i = 0; i < node.childNodes.length; i++) {
            recursive(node.childNodes[i]);
          }
        }
      })(this);
      return result;
    };
    let parser = new StringParser(xml.trim()),
      result,
      child,
      error = false;
    let recursive = function () {
      let temp, child;
      if (temp = parser.match(/^<([\w:.-]+)\s*/, true)) {
        // Opening tag
        let node = new SvgNode(temp[1], 1, null, error);
        while (temp = parser.match(/^([\w:.-]+)(?:\s*=\s*"([^"]*)"|\s*=\s*'([^']*)')?\s*/, true)) {
          // Attribute
          let attr = temp[1],
            value = decodeEntities(temp[2] || temp[3] || '');
          if (!node.attributes[attr]) {
            node.attributes[attr] = value;
            if (attr === 'id') {
              node.id = value;
            }
            if (attr === 'class') {
              node.classList = value.split(' ');
            }
          } else {
            warningCallback('parseXml: duplicate attribute "' + attr + '"');
            error = true;
          }
        }
        if (parser.match(/^>/)) {
          // End of opening tag
          while (child = recursive()) {
            node.childNodes.push(child);
            child.parentNode = node;
            node.textContent += child.nodeType === 3 || child.nodeType === 4 ? child.nodeValue : child.textContent;
          }
          if (temp = parser.match(/^<\/([\w:.-]+)\s*>/, true)) {
            // Closing tag
            if (temp[1] === node.nodeName) {
              return node;
            } else {
              warningCallback('parseXml: tag not matching, opening "' + node.nodeName + '" & closing "' + temp[1] + '"');
              error = true;
              return node;
            }
          } else {
            warningCallback('parseXml: tag not matching, opening "' + node.nodeName + '" & not closing');
            error = true;
            return node;
          }
        } else if (parser.match(/^\/>/)) {
          // Self-closing tag
          return node;
        } else {
          warningCallback('parseXml: tag could not be parsed "' + node.nodeName + '"');
          error = true;
        }
      } else if (temp = parser.match(/^<!--[\s\S]*?-->/)) {
        // Comment
        return new SvgNode(null, 8, temp, error);
      } else if (temp = parser.match(/^<\?[\s\S]*?\?>/)) {
        // Processing instructions
        return new SvgNode(null, 7, temp, error);
      } else if (temp = parser.match(/^<!DOCTYPE\s*([\s\S]*?)>/)) {
        // Doctype
        return new SvgNode(null, 10, temp, error);
      } else if (temp = parser.match(/^<!\[CDATA\[([\s\S]*?)\]\]>/, true)) {
        // Cdata node
        return new SvgNode('#cdata-section', 4, temp[1], error);
      } else if (temp = parser.match(/^([^<]+)/, true)) {
        // Text node
        return new SvgNode('#text', 3, decodeEntities(temp[1]), error);
      }
    };
    while (child = recursive()) {
      if (child.nodeType === 1 && !result) {
        result = child;
      } else if (child.nodeType === 1 || child.nodeType === 3 && child.nodeValue.trim() !== '') {
        warningCallback('parseXml: data after document end has been discarded');
      }
    }
    if (parser.matchAll()) {
      warningCallback('parseXml: parsing error');
    }
    return result;
  }
  ;
  function decodeEntities(str) {
    return str.replace(/&(?:#([0-9]+)|#[xX]([0-9A-Fa-f]+)|([0-9A-Za-z]+));/g, function (mt, m0, m1, m2) {
      if (m0) {
        return String.fromCharCode(parseInt(m0, 10));
      } else if (m1) {
        return String.fromCharCode(parseInt(m1, 16));
      } else if (m2 && Entities[m2]) {
        return String.fromCharCode(Entities[m2]);
      } else {
        return mt;
      }
    });
  }
  function parseColor(raw) {
    let temp, result;
    raw = (raw || '').trim();
    if (temp = NamedColors[raw]) {
      result = [temp.slice(), 1];
    } else if (temp = raw.match(/^cmyk\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
      temp[1] = parseInt(temp[1]);
      temp[2] = parseInt(temp[2]);
      temp[3] = parseInt(temp[3]);
      temp[4] = parseFloat(temp[4]);
      if (temp[1] <= 100 && temp[2] <= 100 && temp[3] <= 100 && temp[4] <= 100) {
        result = [temp.slice(1, 5), 1];
      }
    } else if (temp = raw.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
      temp[1] = parseInt(temp[1]);
      temp[2] = parseInt(temp[2]);
      temp[3] = parseInt(temp[3]);
      temp[4] = parseFloat(temp[4]);
      if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256 && temp[4] <= 1) {
        result = [temp.slice(1, 4), temp[4]];
      }
    } else if (temp = raw.match(/^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$/i)) {
      temp[1] = parseInt(temp[1]);
      temp[2] = parseInt(temp[2]);
      temp[3] = parseInt(temp[3]);
      if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256) {
        result = [temp.slice(1, 4), 1];
      }
    } else if (temp = raw.match(/^rgb\(\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/i)) {
      temp[1] = 2.55 * parseFloat(temp[1]);
      temp[2] = 2.55 * parseFloat(temp[2]);
      temp[3] = 2.55 * parseFloat(temp[3]);
      if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256) {
        result = [temp.slice(1, 4), 1];
      }
    } else if (temp = raw.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i)) {
      result = [[parseInt(temp[1], 16), parseInt(temp[2], 16), parseInt(temp[3], 16)], 1];
    } else if (temp = raw.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i)) {
      result = [[0x11 * parseInt(temp[1], 16), 0x11 * parseInt(temp[2], 16), 0x11 * parseInt(temp[3], 16)], 1];
    }
    return colorCallback ? colorCallback(result, raw) : result;
  }
  function opacityToColor(color, opacity, isMask) {
    let newColor = color[0].slice(),
      newOpacity = color[1] * opacity;
    if (isMask) {
      for (let i = 0; i < color.length; i++) {
        newColor[i] *= newOpacity;
      }
      return [newColor, 1];
    } else {
      return [newColor, newOpacity];
    }
  }
  function multiplyMatrix() {
    function multiply(a, b) {
      return [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
    }
    let result = arguments[0];
    for (let i = 1; i < arguments.length; i++) {
      result = multiply(result, arguments[i]);
    }
    return result;
  }
  function transformPoint(p, m) {
    return [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
  }
  function getGlobalMatrix() {
    let ctm = doc._ctm;
    for (let i = groupStack.length - 1; i >= 0; i--) {
      ctm = multiplyMatrix(groupStack[i].savedMatrix, ctm);
    }
    return ctm;
  }
  function getPageBBox() {
    return new SvgShape().M(0, 0).L(doc.page.width, 0).L(doc.page.width, doc.page.height).L(0, doc.page.height).transform(inverseMatrix(getGlobalMatrix())).getBoundingBox();
  }
  function getPageScale() {
    const bbox = getPageBBox();
    const width = doc.page.width;
    return width / bbox[2];
  }
  function inverseMatrix(m) {
    let dt = m[0] * m[3] - m[1] * m[2];
    return [m[3] / dt, -m[1] / dt, -m[2] / dt, m[0] / dt, (m[2] * m[5] - m[3] * m[4]) / dt, (m[1] * m[4] - m[0] * m[5]) / dt];
  }
  function validateMatrix(m) {
    let m0 = validateNumber(m[0]),
      m1 = validateNumber(m[1]),
      m2 = validateNumber(m[2]),
      m3 = validateNumber(m[3]),
      m4 = validateNumber(m[4]),
      m5 = validateNumber(m[5]);
    if (isNotEqual(m0 * m3 - m1 * m2, 0)) {
      return [m0, m1, m2, m3, m4, m5];
    }
  }
  function solveEquation(curve) {
    let a = curve[2] || 0,
      b = curve[1] || 0,
      c = curve[0] || 0;
    if (isEqual(a, 0) && isEqual(b, 0)) {
      return [];
    } else if (isEqual(a, 0)) {
      return [-c / b];
    } else {
      let d = b * b - 4 * a * c;
      if (isNotEqual(d, 0) && d > 0) {
        return [(-b + Math.sqrt(d)) / (2 * a), (-b - Math.sqrt(d)) / (2 * a)];
      } else if (isEqual(d, 0)) {
        return [-b / (2 * a)];
      } else {
        return [];
      }
    }
  }
  function getCurveValue(t, curve) {
    return (curve[0] || 0) + (curve[1] || 0) * t + (curve[2] || 0) * t * t + (curve[3] || 0) * t * t * t;
  }
  function isEqual(number, ref) {
    return Math.abs(number - ref) < 1e-10;
  }
  function isNotEqual(number, ref) {
    return Math.abs(number - ref) >= 1e-10;
  }
  function validateNumber(n) {
    return n > -1e21 && n < 1e21 ? Math.round(n * 1e6) / 1e6 : 0;
  }
  function isArrayLike(v) {
    return typeof v === 'object' && v !== null && typeof v.length === 'number';
  }
  function parseTranform(v) {
    let parser = new StringParser((v || '').trim()),
      result = [1, 0, 0, 1, 0, 0],
      temp;
    while (temp = parser.match(/^([A-Za-z]+)\s*[(]([^(]+)[)]/, true)) {
      let func = temp[1],
        nums = [],
        parser2 = new StringParser(temp[2].trim()),
        temp2;
      while (temp2 = parser2.matchNumber()) {
        nums.push(Number(temp2));
        parser2.matchSeparator();
      }
      if (func === 'matrix' && nums.length === 6) {
        result = multiplyMatrix(result, [nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]]);
      } else if (func === 'translate' && nums.length === 2) {
        result = multiplyMatrix(result, [1, 0, 0, 1, nums[0], nums[1]]);
      } else if (func === 'translate' && nums.length === 1) {
        result = multiplyMatrix(result, [1, 0, 0, 1, nums[0], 0]);
      } else if (func === 'scale' && nums.length === 2) {
        result = multiplyMatrix(result, [nums[0], 0, 0, nums[1], 0, 0]);
      } else if (func === 'scale' && nums.length === 1) {
        result = multiplyMatrix(result, [nums[0], 0, 0, nums[0], 0, 0]);
      } else if (func === 'rotate' && nums.length === 3) {
        let a = nums[0] * Math.PI / 180;
        result = multiplyMatrix(result, [1, 0, 0, 1, nums[1], nums[2]], [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0], [1, 0, 0, 1, -nums[1], -nums[2]]);
      } else if (func === 'rotate' && nums.length === 1) {
        let a = nums[0] * Math.PI / 180;
        result = multiplyMatrix(result, [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
      } else if (func === 'skewX' && nums.length === 1) {
        let a = nums[0] * Math.PI / 180;
        result = multiplyMatrix(result, [1, 0, Math.tan(a), 1, 0, 0]);
      } else if (func === 'skewY' && nums.length === 1) {
        let a = nums[0] * Math.PI / 180;
        result = multiplyMatrix(result, [1, Math.tan(a), 0, 1, 0, 0]);
      } else {
        return;
      }
      parser.matchSeparator();
    }
    if (parser.matchAll()) {
      return;
    }
    return result;
  }
  function parseAspectRatio(aspectRatio, availWidth, availHeight, elemWidth, elemHeight, initAlign) {
    let temp = (aspectRatio || '').trim().match(/^(none)$|^x(Min|Mid|Max)Y(Min|Mid|Max)(?:\s+(meet|slice))?$/) || [],
      ratioType = temp[1] || temp[4] || 'meet',
      xAlign = temp[2] || 'Mid',
      yAlign = temp[3] || 'Mid',
      scaleX = availWidth / elemWidth,
      scaleY = availHeight / elemHeight,
      dx = {
        'Min': 0,
        'Mid': 0.5,
        'Max': 1
      }[xAlign] - (initAlign || 0),
      dy = {
        'Min': 0,
        'Mid': 0.5,
        'Max': 1
      }[yAlign] - (initAlign || 0);
    if (ratioType === 'slice') {
      scaleY = scaleX = Math.max(scaleX, scaleY);
    } else if (ratioType === 'meet') {
      scaleY = scaleX = Math.min(scaleX, scaleY);
    }
    return [scaleX, 0, 0, scaleY, dx * (availWidth - elemWidth * scaleX), dy * (availHeight - elemHeight * scaleY)];
  }
  function parseStyleAttr(v) {
    let result = Object.create(null);
    v = (v || '').trim().split(/;/);
    for (let i = 0; i < v.length; i++) {
      let key = (v[i].split(':')[0] || '').trim(),
        value = (v[i].split(':')[1] || '').trim();
      if (key) {
        result[key] = value;
      }
    }
    if (result['marker']) {
      if (!result['marker-start']) {
        result['marker-start'] = result['marker'];
      }
      if (!result['marker-mid']) {
        result['marker-mid'] = result['marker'];
      }
      if (!result['marker-end']) {
        result['marker-end'] = result['marker'];
      }
    }
    if (result['font']) {
      let fontFamily = null,
        fontSize = null,
        fontStyle = "normal",
        fontWeight = "normal",
        fontVariant = "normal";
      let parts = result['font'].split(/\s+/);
      for (let i = 0; i < parts.length; i++) {
        switch (parts[i]) {
          case "normal":
            break;
          case "italic":
          case "oblique":
            fontStyle = parts[i];
            break;
          case "small-caps":
            fontVariant = parts[i];
            break;
          case "bold":
          case "bolder":
          case "lighter":
          case "100":
          case "200":
          case "300":
          case "400":
          case "500":
          case "600":
          case "700":
          case "800":
          case "900":
            fontWeight = parts[i];
            break;
          default:
            if (!fontSize) {
              fontSize = parts[i].split('/')[0];
            } else {
              if (!fontFamily) {
                fontFamily = parts[i];
              } else {
                fontFamily += ' ' + parts[i];
              }
            }
            break;
        }
      }
      if (!result['font-style']) {
        result['font-style'] = fontStyle;
      }
      if (!result['font-variant']) {
        result['font-variant'] = fontVariant;
      }
      if (!result['font-weight']) {
        result['font-weight'] = fontWeight;
      }
      if (!result['font-size']) {
        result['font-size'] = fontSize;
      }
      if (!result['font-family']) {
        result['font-family'] = fontFamily;
      }
    }
    return result;
  }
  function parseSelector(v) {
    let parts = v.split(/(?=[.#])/g),
      ids = [],
      classes = [],
      tags = [],
      temp;
    for (let i = 0; i < parts.length; i++) {
      if (temp = parts[i].match(/^[#]([_A-Za-z0-9-]+)$/)) {
        ids.push(temp[1]);
      } else if (temp = parts[i].match(/^[.]([_A-Za-z0-9-]+)$/)) {
        classes.push(temp[1]);
      } else if (temp = parts[i].match(/^([_A-Za-z0-9-]+)$/)) {
        tags.push(temp[1]);
      } else if (parts[i] !== '*') {
        return;
      }
    }
    return {
      tags: tags,
      ids: ids,
      classes: classes,
      specificity: ids.length * 10000 + classes.length * 100 + tags.length
    };
  }
  function parseStyleSheet(v) {
    let parser = new StringParser(v.trim()),
      rules = [],
      rule;
    while (rule = parser.match(/^\s*([^\{\}]*?)\s*\{([^\{\}]*?)\}/, true)) {
      let selectors = rule[1].split(/\s*,\s*/g),
        css = parseStyleAttr(rule[2]);
      for (let i = 0; i < selectors.length; i++) {
        let selector = parseSelector(selectors[i]);
        if (selector) {
          rules.push({
            selector: selector,
            css: css
          });
        }
      }
    }
    return rules;
  }
  function matchesSelector(elem, selector) {
    if (elem.nodeType !== 1) {
      return false;
    }
    for (let i = 0; i < selector.tags.length; i++) {
      if (selector.tags[i] !== elem.nodeName) {
        return false;
      }
    }
    for (let i = 0; i < selector.ids.length; i++) {
      if (selector.ids[i] !== elem.id) {
        return false;
      }
    }
    for (let i = 0; i < selector.classes.length; i++) {
      if (elem.classList.indexOf(selector.classes[i]) === -1) {
        return false;
      }
    }
    return true;
  }
  function getStyle(elem) {
    let result = Object.create(null);
    let specificities = Object.create(null);
    for (let i = 0; i < styleRules.length; i++) {
      let rule = styleRules[i];
      if (matchesSelector(elem, rule.selector)) {
        for (let key in rule.css) {
          if (!(specificities[key] > rule.selector.specificity)) {
            result[key] = rule.css[key];
            specificities[key] = rule.selector.specificity;
          }
        }
      }
    }
    return result;
  }
  function combineArrays(array1, array2) {
    return array1.concat(array2.slice(array1.length));
  }
  function getAscent(font, size) {
    return Math.max(font.ascender, (font.bbox[3] || font.bbox.maxY) * (font.scale || 1)) * size / 1000;
  }
  function getDescent(font, size) {
    return Math.min(font.descender, (font.bbox[1] || font.bbox.minY) * (font.scale || 1)) * size / 1000;
  }
  function getXHeight(font, size) {
    return (font.xHeight || 0.5 * (font.ascender - font.descender)) * size / 1000;
  }
  function getBaseline(font, size, baseline, shift) {
    let dy1, dy2;
    switch (baseline) {
      case 'middle':
        dy1 = 0.5 * getXHeight(font, size);
        break;
      case 'central':
        dy1 = 0.5 * (getDescent(font, size) + getAscent(font, size));
        break;
      case 'after-edge':
      case 'text-after-edge':
        dy1 = getDescent(font, size);
        break;
      case 'alphabetic':
      case 'auto':
      case 'baseline':
        dy1 = 0;
        break;
      case 'mathematical':
        dy1 = 0.5 * getAscent(font, size);
        break;
      case 'hanging':
        dy1 = 0.8 * getAscent(font, size);
        break;
      case 'before-edge':
      case 'text-before-edge':
        dy1 = getAscent(font, size);
        break;
      default:
        dy1 = 0;
        break;
    }
    switch (shift) {
      case 'baseline':
        dy2 = 0;
        break;
      case 'super':
        dy2 = 0.6 * size;
        break;
      case 'sub':
        dy2 = -0.6 * size;
        break;
      default:
        dy2 = shift;
        break;
    }
    return dy1 - dy2;
  }
  function getTextPos(font, size, text) {
    let encoded = font.encode('' + text),
      hex = encoded[0],
      pos = encoded[1],
      data = [];
    for (let i = 0; i < hex.length; i++) {
      let unicode = font.unicode ? font.unicode[parseInt(hex[i], 16)] : [text.charCodeAt(i)];
      data.push({
        glyph: hex[i],
        unicode: unicode,
        kern: pos[i].advanceWidth - pos[i].xAdvance,
        width: pos[i].advanceWidth * size / 1000,
        xOffset: pos[i].xOffset * size / 1000,
        yOffset: pos[i].yOffset * size / 1000,
        xAdvance: pos[i].xAdvance * size / 1000,
        yAdvance: pos[i].yAdvance * size / 1000
      });
    }
    return data;
  }
  function createSVGElement(obj, inherits) {
    switch (obj.nodeName) {
      case 'use':
        return new SvgElemUse(obj, inherits);
      case 'symbol':
        return new SvgElemSymbol(obj, inherits);
      case 'g':
        return new SvgElemGroup(obj, inherits);
      case 'a':
        return new SvgElemLink(obj, inherits);
      case 'svg':
        return new SvgElemSvg(obj, inherits);
      case 'image':
        return new SVGElemImage(obj, inherits);
      case 'rect':
        return new SvgElemRect(obj, inherits);
      case 'circle':
        return new SvgElemCircle(obj, inherits);
      case 'ellipse':
        return new SvgElemEllipse(obj, inherits);
      case 'line':
        return new SvgElemLine(obj, inherits);
      case 'polyline':
        return new SvgElemPolyline(obj, inherits);
      case 'polygon':
        return new SvgElemPolygon(obj, inherits);
      case 'path':
        return new SvgElemPath(obj, inherits);
      case 'text':
        return new SvgElemText(obj, inherits);
      case 'tspan':
        return new SvgElemTspan(obj, inherits);
      case 'textPath':
        return new SvgElemTextPath(obj, inherits);
      case '#text':
      case '#cdata-section':
        return new SvgElemTextNode(obj, inherits);
      default:
        return new SvgElem(obj, inherits);
    }
  }
  var StringParser = function (str) {
    this.match = function (exp, all) {
      let temp = str.match(exp);
      if (!temp || temp.index !== 0) {
        return;
      }
      str = str.substring(temp[0].length);
      return all ? temp : temp[0];
    };
    this.matchSeparator = function () {
      return this.match(/^(?:\s*,\s*|\s*|)/);
    };
    this.matchSpace = function () {
      return this.match(/^(?:\s*)/);
    };
    this.matchLengthUnit = function () {
      return this.match(/^(?:px|pt|cm|mm|in|pc|em|ex|%|)/);
    };
    this.matchNumber = function () {
      return this.match(/^(?:[-+]?(?:[0-9]+[.][0-9]+|[0-9]+[.]|[.][0-9]+|[0-9]+)(?:[eE][-+]?[0-9]+)?)/);
    };
    this.matchAll = function () {
      return this.match(/^[\s\S]+/);
    };
  };
  var BezierSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
    let divisions = 6 * precision;
    let equationX = [p1x, -3 * p1x + 3 * c1x, 3 * p1x - 6 * c1x + 3 * c2x, -p1x + 3 * c1x - 3 * c2x + p2x];
    let equationY = [p1y, -3 * p1y + 3 * c1y, 3 * p1y - 6 * c1y + 3 * c2y, -p1y + 3 * c1y - 3 * c2y + p2y];
    let derivativeX = [-3 * p1x + 3 * c1x, 6 * p1x - 12 * c1x + 6 * c2x, -3 * p1x + 9 * c1x - 9 * c2x + 3 * p2x];
    let derivativeY = [-3 * p1y + 3 * c1y, 6 * p1y - 12 * c1y + 6 * c2y, -3 * p1y + 9 * c1y - 9 * c2y + 3 * p2y];
    let lengthMap = [0];
    for (let i = 1; i <= divisions; i++) {
      let t = (i - 0.5) / divisions;
      let dx = getCurveValue(t, derivativeX) / divisions,
        dy = getCurveValue(t, derivativeY) / divisions,
        l = Math.sqrt(dx * dx + dy * dy);
      lengthMap[i] = lengthMap[i - 1] + l;
    }
    this.totalLength = lengthMap[divisions];
    this.startPoint = [p1x, p1y, isEqual(p1x, c1x) && isEqual(p1y, c1y) ? Math.atan2(c2y - c1y, c2x - c1x) : Math.atan2(c1y - p1y, c1x - p1x)];
    this.endPoint = [p2x, p2y, isEqual(c2x, p2x) && isEqual(c2y, p2y) ? Math.atan2(c2y - c1y, c2x - c1x) : Math.atan2(p2y - c2y, p2x - c2x)];
    this.getBoundingBox = function () {
      let temp;
      let minX = getCurveValue(0, equationX),
        minY = getCurveValue(0, equationY),
        maxX = getCurveValue(1, equationX),
        maxY = getCurveValue(1, equationY);
      if (minX > maxX) {
        temp = maxX;
        maxX = minX;
        minX = temp;
      }
      if (minY > maxY) {
        temp = maxY;
        maxY = minY;
        minY = temp;
      }
      let rootsX = solveEquation(derivativeX);
      for (let i = 0; i < rootsX.length; i++) {
        if (rootsX[i] >= 0 && rootsX[i] <= 1) {
          let x = getCurveValue(rootsX[i], equationX);
          if (x < minX) {
            minX = x;
          }
          if (x > maxX) {
            maxX = x;
          }
        }
      }
      let rootsY = solveEquation(derivativeY);
      for (let i = 0; i < rootsY.length; i++) {
        if (rootsY[i] >= 0 && rootsY[i] <= 1) {
          let y = getCurveValue(rootsY[i], equationY);
          if (y < minY) {
            minY = y;
          }
          if (y > maxY) {
            maxY = y;
          }
        }
      }
      return [minX, minY, maxX, maxY];
    };
    this.getPointAtLength = function (l) {
      if (isEqual(l, 0)) {
        return this.startPoint;
      }
      if (isEqual(l, this.totalLength)) {
        return this.endPoint;
      }
      if (l < 0 || l > this.totalLength) {
        return;
      }
      for (let i = 1; i <= divisions; i++) {
        let l1 = lengthMap[i - 1],
          l2 = lengthMap[i];
        if (l1 <= l && l <= l2) {
          let t = (i - (l2 - l) / (l2 - l1)) / divisions,
            x = getCurveValue(t, equationX),
            y = getCurveValue(t, equationY),
            dx = getCurveValue(t, derivativeX),
            dy = getCurveValue(t, derivativeY);
          return [x, y, Math.atan2(dy, dx)];
        }
      }
    };
  };
  var LineSegment = function (p1x, p1y, p2x, p2y) {
    this.totalLength = Math.sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y));
    this.startPoint = [p1x, p1y, Math.atan2(p2y - p1y, p2x - p1x)];
    this.endPoint = [p2x, p2y, Math.atan2(p2y - p1y, p2x - p1x)];
    this.getBoundingBox = function () {
      return [Math.min(this.startPoint[0], this.endPoint[0]), Math.min(this.startPoint[1], this.endPoint[1]), Math.max(this.startPoint[0], this.endPoint[0]), Math.max(this.startPoint[1], this.endPoint[1])];
    };
    this.getPointAtLength = function (l) {
      if (l >= 0 && l <= this.totalLength) {
        let r = l / this.totalLength || 0,
          x = this.startPoint[0] + r * (this.endPoint[0] - this.startPoint[0]),
          y = this.startPoint[1] + r * (this.endPoint[1] - this.startPoint[1]);
        return [x, y, this.startPoint[2]];
      }
    };
  };
  var SvgShape = function () {
    this.pathCommands = [];
    this.pathSegments = [];
    this.startPoint = null;
    this.endPoint = null;
    this.totalLength = 0;
    let startX = 0,
      startY = 0,
      currX = 0,
      currY = 0,
      lastCom,
      lastCtrlX,
      lastCtrlY;
    this.move = function (x, y) {
      startX = currX = x;
      startY = currY = y;
      return null;
    };
    this.line = function (x, y) {
      let segment = new LineSegment(currX, currY, x, y);
      currX = x;
      currY = y;
      return segment;
    };
    this.curve = function (c1x, c1y, c2x, c2y, x, y) {
      let segment = new BezierSegment(currX, currY, c1x, c1y, c2x, c2y, x, y);
      currX = x;
      currY = y;
      return segment;
    };
    this.close = function () {
      let segment = new LineSegment(currX, currY, startX, startY);
      currX = startX;
      currY = startY;
      return segment;
    };
    this.addCommand = function (data) {
      this.pathCommands.push(data);
      let segment = this[data[0]].apply(this, data.slice(3));
      if (segment) {
        segment.hasStart = data[1];
        segment.hasEnd = data[2];
        this.startPoint = this.startPoint || segment.startPoint;
        this.endPoint = segment.endPoint;
        this.pathSegments.push(segment);
        this.totalLength += segment.totalLength;
      }
    };
    this.M = function (x, y) {
      this.addCommand(['move', true, true, x, y]);
      lastCom = 'M';
      return this;
    };
    this.m = function (x, y) {
      return this.M(currX + x, currY + y);
    };
    this.Z = this.z = function () {
      this.addCommand(['close', true, true]);
      lastCom = 'Z';
      return this;
    };
    this.L = function (x, y) {
      this.addCommand(['line', true, true, x, y]);
      lastCom = 'L';
      return this;
    };
    this.l = function (x, y) {
      return this.L(currX + x, currY + y);
    };
    this.H = function (x) {
      return this.L(x, currY);
    };
    this.h = function (x) {
      return this.L(currX + x, currY);
    };
    this.V = function (y) {
      return this.L(currX, y);
    };
    this.v = function (y) {
      return this.L(currX, currY + y);
    };
    this.C = function (c1x, c1y, c2x, c2y, x, y) {
      this.addCommand(['curve', true, true, c1x, c1y, c2x, c2y, x, y]);
      lastCom = 'C';
      lastCtrlX = c2x;
      lastCtrlY = c2y;
      return this;
    };
    this.c = function (c1x, c1y, c2x, c2y, x, y) {
      return this.C(currX + c1x, currY + c1y, currX + c2x, currY + c2y, currX + x, currY + y);
    };
    this.S = function (c1x, c1y, x, y) {
      return this.C(currX + (lastCom === 'C' ? currX - lastCtrlX : 0), currY + (lastCom === 'C' ? currY - lastCtrlY : 0), c1x, c1y, x, y);
    };
    this.s = function (c1x, c1y, x, y) {
      return this.C(currX + (lastCom === 'C' ? currX - lastCtrlX : 0), currY + (lastCom === 'C' ? currY - lastCtrlY : 0), currX + c1x, currY + c1y, currX + x, currY + y);
    };
    this.Q = function (cx, cy, x, y) {
      let c1x = currX + 2 / 3 * (cx - currX),
        c1y = currY + 2 / 3 * (cy - currY),
        c2x = x + 2 / 3 * (cx - x),
        c2y = y + 2 / 3 * (cy - y);
      this.addCommand(['curve', true, true, c1x, c1y, c2x, c2y, x, y]);
      lastCom = 'Q';
      lastCtrlX = cx;
      lastCtrlY = cy;
      return this;
    };
    this.q = function (c1x, c1y, x, y) {
      return this.Q(currX + c1x, currY + c1y, currX + x, currY + y);
    };
    this.T = function (x, y) {
      return this.Q(currX + (lastCom === 'Q' ? currX - lastCtrlX : 0), currY + (lastCom === 'Q' ? currY - lastCtrlY : 0), x, y);
    };
    this.t = function (x, y) {
      return this.Q(currX + (lastCom === 'Q' ? currX - lastCtrlX : 0), currY + (lastCom === 'Q' ? currY - lastCtrlY : 0), currX + x, currY + y);
    };
    this.A = function (rx, ry, fi, fa, fs, x, y) {
      if (isEqual(rx, 0) || isEqual(ry, 0)) {
        this.addCommand(['line', true, true, x, y]);
      } else {
        fi = fi * (Math.PI / 180);
        rx = Math.abs(rx);
        ry = Math.abs(ry);
        fa = 1 * !!fa;
        fs = 1 * !!fs;
        let x1 = Math.cos(fi) * (currX - x) / 2 + Math.sin(fi) * (currY - y) / 2,
          y1 = Math.cos(fi) * (currY - y) / 2 - Math.sin(fi) * (currX - x) / 2,
          lambda = x1 * x1 / (rx * rx) + y1 * y1 / (ry * ry);
        if (lambda > 1) {
          rx *= Math.sqrt(lambda);
          ry *= Math.sqrt(lambda);
        }
        let r = Math.sqrt(Math.max(0, rx * rx * ry * ry - rx * rx * y1 * y1 - ry * ry * x1 * x1) / (rx * rx * y1 * y1 + ry * ry * x1 * x1)),
          x2 = (fa === fs ? -1 : 1) * r * rx * y1 / ry,
          y2 = (fa === fs ? 1 : -1) * r * ry * x1 / rx;
        let cx = Math.cos(fi) * x2 - Math.sin(fi) * y2 + (currX + x) / 2,
          cy = Math.sin(fi) * x2 + Math.cos(fi) * y2 + (currY + y) / 2,
          th1 = Math.atan2((y1 - y2) / ry, (x1 - x2) / rx),
          th2 = Math.atan2((-y1 - y2) / ry, (-x1 - x2) / rx);
        if (fs === 0 && th2 - th1 > 0) {
          th2 -= 2 * Math.PI;
        } else if (fs === 1 && th2 - th1 < 0) {
          th2 += 2 * Math.PI;
        }
        let segms = Math.ceil(Math.abs(th2 - th1) / (Math.PI / precision));
        for (let i = 0; i < segms; i++) {
          let th3 = th1 + i * (th2 - th1) / segms,
            th4 = th1 + (i + 1) * (th2 - th1) / segms,
            t = 4 / 3 * Math.tan((th4 - th3) / 4);
          let c1x = cx + Math.cos(fi) * rx * (Math.cos(th3) - t * Math.sin(th3)) - Math.sin(fi) * ry * (Math.sin(th3) + t * Math.cos(th3)),
            c1y = cy + Math.sin(fi) * rx * (Math.cos(th3) - t * Math.sin(th3)) + Math.cos(fi) * ry * (Math.sin(th3) + t * Math.cos(th3)),
            c2x = cx + Math.cos(fi) * rx * (Math.cos(th4) + t * Math.sin(th4)) - Math.sin(fi) * ry * (Math.sin(th4) - t * Math.cos(th4)),
            c2y = cy + Math.sin(fi) * rx * (Math.cos(th4) + t * Math.sin(th4)) + Math.cos(fi) * ry * (Math.sin(th4) - t * Math.cos(th4)),
            endX = cx + Math.cos(fi) * rx * Math.cos(th4) - Math.sin(fi) * ry * Math.sin(th4),
            endY = cy + Math.sin(fi) * rx * Math.cos(th4) + Math.cos(fi) * ry * Math.sin(th4);
          this.addCommand(['curve', i === 0, i === segms - 1, c1x, c1y, c2x, c2y, endX, endY]);
        }
      }
      lastCom = 'A';
      return this;
    };
    this.a = function (rx, ry, fi, fa, fs, x, y) {
      return this.A(rx, ry, fi, fa, fs, currX + x, currY + y);
    };
    this.path = function (d) {
      let command,
        value,
        temp,
        parser = new StringParser((d || '').trim());
      while (command = parser.match(/^[astvzqmhlcASTVZQMHLC]/)) {
        parser.matchSeparator();
        let values = [];
        while (value = PathFlags[command + values.length] ? parser.match(/^[01]/) : parser.matchNumber()) {
          parser.matchSeparator();
          if (values.length === PathArguments[command]) {
            this[command].apply(this, values);
            values = [];
            if (command === 'M') {
              command = 'L';
            } else if (command === 'm') {
              command = 'l';
            }
          }
          values.push(Number(value));
        }
        if (values.length === PathArguments[command]) {
          this[command].apply(this, values);
        } else {
          warningCallback('SvgPath: command ' + command + ' with ' + values.length + ' numbers');
          return;
        }
      }
      if (temp = parser.matchAll()) {
        warningCallback('SvgPath: unexpected string ' + temp);
      }
      return this;
    };
    this.getBoundingBox = function () {
      let bbox = [Infinity, Infinity, -Infinity, -Infinity];
      function addBounds(bbox1) {
        if (bbox1[0] < bbox[0]) {
          bbox[0] = bbox1[0];
        }
        if (bbox1[2] > bbox[2]) {
          bbox[2] = bbox1[2];
        }
        if (bbox1[1] < bbox[1]) {
          bbox[1] = bbox1[1];
        }
        if (bbox1[3] > bbox[3]) {
          bbox[3] = bbox1[3];
        }
      }
      for (let i = 0; i < this.pathSegments.length; i++) {
        addBounds(this.pathSegments[i].getBoundingBox());
      }
      if (bbox[0] === Infinity) {
        bbox[0] = 0;
      }
      if (bbox[1] === Infinity) {
        bbox[1] = 0;
      }
      if (bbox[2] === -Infinity) {
        bbox[2] = 0;
      }
      if (bbox[3] === -Infinity) {
        bbox[3] = 0;
      }
      return bbox;
    };
    this.getPointAtLength = function (l) {
      if (l >= 0 && l <= this.totalLength) {
        let temp;
        for (let i = 0; i < this.pathSegments.length; i++) {
          if (temp = this.pathSegments[i].getPointAtLength(l)) {
            return temp;
          }
          l -= this.pathSegments[i].totalLength;
        }
        return this.endPoint;
      }
    };
    this.transform = function (m) {
      this.pathSegments = [];
      this.startPoint = null;
      this.endPoint = null;
      this.totalLength = 0;
      for (let i = 0; i < this.pathCommands.length; i++) {
        let data = this.pathCommands.shift();
        for (let j = 3; j < data.length; j += 2) {
          let p = transformPoint([data[j], data[j + 1]], m);
          data[j] = p[0];
          data[j + 1] = p[1];
        }
        this.addCommand(data);
      }
      return this;
    };
    this.mergeShape = function (shape) {
      for (let i = 0; i < shape.pathCommands.length; i++) {
        this.addCommand(shape.pathCommands[i].slice());
      }
      return this;
    };
    this.clone = function () {
      return new SvgShape().mergeShape(this);
    };
    this.insertInDocument = function () {
      for (let i = 0; i < this.pathCommands.length; i++) {
        let command = this.pathCommands[i][0],
          values = this.pathCommands[i].slice(3);
        switch (command) {
          case 'move':
            doc.moveTo(values[0], values[1]);
            break;
          case 'line':
            doc.lineTo(values[0], values[1]);
            break;
          case 'curve':
            doc.bezierCurveTo(values[0], values[1], values[2], values[3], values[4], values[5]);
            break;
          case 'close':
            doc.closePath();
            break;
        }
      }
    };
    this.getSubPaths = function () {
      let subPaths = [],
        shape = new SvgShape();
      for (let i = 0; i < this.pathCommands.length; i++) {
        let data = this.pathCommands[i],
          command = this.pathCommands[i][0];
        if (command === 'move' && i !== 0) {
          subPaths.push(shape);
          shape = new SvgShape();
        }
        shape.addCommand(data);
      }
      subPaths.push(shape);
      return subPaths;
    };
    this.getMarkers = function () {
      let markers = [],
        subPaths = this.getSubPaths();
      for (let i = 0; i < subPaths.length; i++) {
        let subPath = subPaths[i],
          subPathMarkers = [];
        for (let j = 0; j < subPath.pathSegments.length; j++) {
          let segment = subPath.pathSegments[j];
          if (isNotEqual(segment.totalLength, 0) || j === 0 || j === subPath.pathSegments.length - 1) {
            if (segment.hasStart) {
              let startMarker = segment.getPointAtLength(0),
                prevEndMarker = subPathMarkers.pop();
              if (prevEndMarker) {
                startMarker[2] = 0.5 * (prevEndMarker[2] + startMarker[2]);
              }
              subPathMarkers.push(startMarker);
            }
            if (segment.hasEnd) {
              let endMarker = segment.getPointAtLength(segment.totalLength);
              subPathMarkers.push(endMarker);
            }
          }
        }
        markers = markers.concat(subPathMarkers);
      }
      return markers;
    };
  };
  var SvgElem = function (obj, inherits) {
    let styleCache = Object.create(null);
    let childrenCache = null;
    this.name = obj.nodeName;
    this.isOuterElement = obj === svg || !obj.parentNode;
    this.inherits = inherits || (!this.isOuterElement ? createSVGElement(obj.parentNode, null) : null);
    this.stack = this.inherits ? this.inherits.stack.concat(obj) : [obj];
    this.style = parseStyleAttr(typeof obj.getAttribute === 'function' && obj.getAttribute('style'));
    this.css = useCSS ? getComputedStyle(obj) : getStyle(obj);
    this.allowedChildren = [];
    this.attr = function (key) {
      if (typeof obj.getAttribute === 'function') {
        return obj.getAttribute(key);
      }
    };
    this.resolveUrl = function (value) {
      let temp = (value || '').match(/^\s*(?:url\("(.*)#(.*)"\)|url\('(.*)#(.*)'\)|url\((.*)#(.*)\)|(.*)#(.*))\s*$/) || [];
      let file = temp[1] || temp[3] || temp[5] || temp[7],
        id = temp[2] || temp[4] || temp[6] || temp[8];
      if (id) {
        if (!file) {
          let svgObj = svg.getElementById(id);
          if (svgObj) {
            if (this.stack.indexOf(svgObj) === -1) {
              return svgObj;
            } else {
              warningCallback('SVGtoPDF: loop of circular references for id "' + id + '"');
              return;
            }
          }
        }
        if (documentCallback) {
          let svgs = documentCache[file];
          if (!svgs) {
            svgs = documentCallback(file);
            if (!isArrayLike(svgs)) {
              svgs = [svgs];
            }
            for (let i = 0; i < svgs.length; i++) {
              if (typeof svgs[i] === 'string') {
                svgs[i] = parseXml(svgs[i]);
              }
            }
            documentCache[file] = svgs;
          }
          for (let i = 0; i < svgs.length; i++) {
            let svgObj = svgs[i].getElementById(id);
            if (svgObj) {
              if (this.stack.indexOf(svgObj) === -1) {
                return svgObj;
              } else {
                warningCallback('SVGtoPDF: loop of circular references for id "' + file + '#' + id + '"');
                return;
              }
            }
          }
        }
      }
    };
    this.computeUnits = function (value, unit, percent, isFontSize) {
      if (unit === '%') {
        return parseFloat(value) / 100 * (isFontSize || percent != null ? percent : this.getViewport());
      } else if (unit === 'ex' || unit === 'em') {
        return value * {
          'em': 1,
          'ex': 0.5
        }[unit] * (isFontSize ? percent : this.get('font-size'));
      } else {
        return value * {
          '': 1,
          'px': 1,
          'pt': 96 / 72,
          'cm': 96 / 2.54,
          'mm': 96 / 25.4,
          'in': 96,
          'pc': 96 / 6
        }[unit];
      }
    };
    this.computeLength = function (value, percent, initial, isFontSize) {
      let parser = new StringParser((value || '').trim()),
        temp1,
        temp2;
      if (typeof (temp1 = parser.matchNumber()) === 'string' && typeof (temp2 = parser.matchLengthUnit()) === 'string' && !parser.matchAll()) {
        return this.computeUnits(temp1, temp2, percent, isFontSize);
      }
      return initial;
    };
    this.computeLengthList = function (value, percent, strict) {
      let parser = new StringParser((value || '').trim()),
        result = [],
        temp1,
        temp2;
      while (typeof (temp1 = parser.matchNumber()) === 'string' && typeof (temp2 = parser.matchLengthUnit()) === 'string') {
        result.push(this.computeUnits(temp1, temp2, percent));
        parser.matchSeparator();
      }
      if (strict && parser.matchAll()) {
        return;
      }
      return result;
    };
    this.getLength = function (key, percent, initial) {
      return this.computeLength(this.attr(key), percent, initial);
    };
    this.getLengthList = function (key, percent) {
      return this.computeLengthList(this.attr(key), percent);
    };
    this.getUrl = function (key) {
      return this.resolveUrl(this.attr(key));
    };
    this.getNumberList = function (key) {
      let parser = new StringParser((this.attr(key) || '').trim()),
        result = [],
        temp;
      while (temp = parser.matchNumber()) {
        result.push(Number(temp));
        parser.matchSeparator();
      }
      result.error = parser.matchAll();
      return result;
    };
    this.getViewbox = function (key, initial) {
      let viewBox = this.getNumberList(key);
      if (viewBox.length === 4 && viewBox[2] >= 0 && viewBox[3] >= 0) {
        return viewBox;
      }
      return initial;
    };
    this.getPercent = function (key, initial) {
      let value = this.attr(key);
      let parser = new StringParser((value || '').trim()),
        temp1,
        temp2;
      let number = parser.matchNumber();
      if (!number) {
        return initial;
      }
      if (parser.match('%')) {
        number *= 0.01;
      }
      if (parser.matchAll()) {
        return initial;
      }
      return Math.max(0, Math.min(1, number));
    };
    this.chooseValue = function (args) {
      for (let i = 0; i < arguments.length; i++) {
        if (arguments[i] != null && arguments[i] === arguments[i]) {
          return arguments[i];
        }
      }
      return arguments[arguments.length - 1];
    };
    this.get = function (key) {
      if (styleCache[key] !== undefined) {
        return styleCache[key];
      }
      let keyInfo = Properties[key] || {},
        value,
        result;
      for (let i = 0; i < 3; i++) {
        switch (i) {
          case 0:
            if (key !== 'transform') {
              // the CSS transform behaves strangely
              value = this.css[keyInfo.css || key];
            }
            break;
          case 1:
            value = this.style[key];
            break;
          case 2:
            value = this.attr(key);
            break;
        }
        if (value === 'inherit') {
          result = this.inherits ? this.inherits.get(key) : keyInfo.initial;
          if (result != null) {
            return styleCache[key] = result;
          }
        }
        if (keyInfo.values != null) {
          result = keyInfo.values[value];
          if (result != null) {
            return styleCache[key] = result;
          }
        }
        if (value != null) {
          let parsed;
          switch (key) {
            case 'font-size':
              result = this.computeLength(value, this.inherits ? this.inherits.get(key) : keyInfo.initial, undefined, true);
              break;
            case 'baseline-shift':
              result = this.computeLength(value, this.get('font-size'));
              break;
            case 'font-family':
              result = value || undefined;
              break;
            case 'opacity':
            case 'stroke-opacity':
            case 'fill-opacity':
            case 'stop-opacity':
              parsed = parseFloat(value);
              if (!isNaN(parsed)) {
                result = Math.max(0, Math.min(1, parsed));
              }
              break;
            case 'transform':
              result = parseTranform(value);
              break;
            case 'stroke-dasharray':
              if (value === 'none') {
                result = [];
              } else if (parsed = this.computeLengthList(value, this.getViewport(), true)) {
                let sum = 0,
                  error = false;
                for (let j = 0; j < parsed.length; j++) {
                  if (parsed[j] < 0) {
                    error = true;
                  }
                  sum += parsed[j];
                }
                if (!error) {
                  if (parsed.length % 2 === 1) {
                    parsed = parsed.concat(parsed);
                  }
                  result = sum === 0 ? [] : parsed;
                }
              }
              break;
            case 'color':
              if (value === 'none' || value === 'transparent') {
                result = 'none';
              } else {
                result = parseColor(value);
              }
              break;
            case 'fill':
            case 'stroke':
              if (value === 'none' || value === 'transparent') {
                result = 'none';
              } else if (value === 'currentColor') {
                result = this.get('color');
              } else if (parsed = parseColor(value)) {
                return parsed;
              } else if (parsed = (value || '').split(' ')) {
                let object = this.resolveUrl(parsed[0]),
                  fallbackColor = parseColor(parsed[1]);
                if (object == null) {
                  result = fallbackColor;
                } else if (object.nodeName === 'linearGradient' || object.nodeName === 'radialGradient') {
                  result = new SvgElemGradient(object, null, fallbackColor);
                } else if (object.nodeName === 'pattern') {
                  result = new SvgElemPattern(object, null, fallbackColor);
                } else {
                  result = fallbackColor;
                }
              }
              break;
            case 'stop-color':
              if (value === 'none' || value === 'transparent') {
                result = 'none';
              } else if (value === 'currentColor') {
                result = this.get('color');
              } else {
                result = parseColor(value);
              }
              break;
            case 'marker-start':
            case 'marker-mid':
            case 'marker-end':
            case 'clip-path':
            case 'mask':
              if (value === 'none') {
                result = 'none';
              } else {
                result = this.resolveUrl(value);
              }
              break;
            case 'stroke-width':
              parsed = this.computeLength(value, this.getViewport());
              if (parsed != null && parsed >= 0) {
                result = parsed;
              }
              break;
            case 'stroke-miterlimit':
              parsed = parseFloat(value);
              if (parsed != null && parsed >= 1) {
                result = parsed;
              }
              break;
            case 'word-spacing':
            case 'letter-spacing':
              result = this.computeLength(value, this.getViewport());
              break;
            case 'stroke-dashoffset':
              result = this.computeLength(value, this.getViewport());
              break;
          }
          if (result != null) {
            return styleCache[key] = result;
          }
        }
      }
      return styleCache[key] = keyInfo.inherit && this.inherits ? this.inherits.get(key) : keyInfo.initial;
    };
    this.getChildren = function () {
      if (childrenCache != null) {
        return childrenCache;
      }
      let children = [];
      for (let i = 0; i < obj.childNodes.length; i++) {
        let child = obj.childNodes[i];
        if (!child.error && this.allowedChildren.indexOf(child.nodeName) !== -1) {
          children.push(createSVGElement(child, this));
        }
      }
      return childrenCache = children;
    };
    this.getParentVWidth = function () {
      return this.inherits ? this.inherits.getVWidth() : viewportWidth;
    };
    this.getParentVHeight = function () {
      return this.inherits ? this.inherits.getVHeight() : viewportHeight;
    };
    this.getParentViewport = function () {
      return Math.sqrt(0.5 * this.getParentVWidth() * this.getParentVWidth() + 0.5 * this.getParentVHeight() * this.getParentVHeight());
    };
    this.getVWidth = function () {
      return this.getParentVWidth();
    };
    this.getVHeight = function () {
      return this.getParentVHeight();
    };
    this.getViewport = function () {
      return Math.sqrt(0.5 * this.getVWidth() * this.getVWidth() + 0.5 * this.getVHeight() * this.getVHeight());
    };
    this.getBoundingBox = function () {
      let shape = this.getBoundingShape();
      return shape.getBoundingBox();
    };
  };
  var SvgElemStylable = function (obj, inherits) {
    SvgElem.call(this, obj, inherits);
    this.transform = function () {
      doc.transform.apply(doc, this.getTransformation());
    };
    this.clip = function () {
      if (this.get('clip-path') !== 'none') {
        let clipPath = new SvgElemClipPath(this.get('clip-path'), null);
        clipPath.useMask(clipPath.attr('clipPathUnits') === 'objectBoundingBox' ? this.getBoundingBox() : null);
        return true;
      }
    };
    this.mask = function () {
      if (this.get('mask') !== 'none') {
        let mask = new SvgElemMask(this.get('mask'), null);
        mask.useMask(this.getBoundingBox());
        return true;
      }
    };
    this.getFill = function (isClip, isMask) {
      let opacity = this.get('opacity'),
        fill = this.get('fill'),
        fillOpacity = this.get('fill-opacity');
      if (isClip) {
        return DefaultColors.white;
      }
      if (fill !== 'none' && opacity && fillOpacity) {
        if (fill instanceof SvgElemGradient || fill instanceof SvgElemPattern) {
          return fill.getPaint(this.getBoundingBox(), fillOpacity * opacity, isClip, isMask);
        }
        return opacityToColor(fill, fillOpacity * opacity, isMask);
      }
    };
    this.getStroke = function (isClip, isMask) {
      let opacity = this.get('opacity'),
        stroke = this.get('stroke'),
        strokeOpacity = this.get('stroke-opacity');
      if (isClip || isEqual(this.get('stroke-width'), 0)) {
        return;
      }
      if (stroke !== 'none' && opacity && strokeOpacity) {
        if (stroke instanceof SvgElemGradient || stroke instanceof SvgElemPattern) {
          return stroke.getPaint(this.getBoundingBox(), strokeOpacity * opacity, isClip, isMask);
        }
        return opacityToColor(stroke, strokeOpacity * opacity, isMask);
      }
    };
  };
  var SvgElemHasChildren = function (obj, inherits) {
    SvgElemStylable.call(this, obj, inherits);
    this.allowedChildren = ['use', 'g', 'a', 'svg', 'image', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path', 'text'];
    this.getBoundingShape = function () {
      let shape = new SvgShape(),
        children = this.getChildren();
      for (let i = 0; i < children.length; i++) {
        if (children[i].get('display') !== 'none') {
          if (typeof children[i].getBoundingShape === 'function') {
            let childShape = children[i].getBoundingShape().clone();
            if (typeof children[i].getTransformation === 'function') {
              childShape.transform(children[i].getTransformation());
            }
            shape.mergeShape(childShape);
          }
        }
      }
      return shape;
    };
    this.drawChildren = function (isClip, isMask) {
      let children = this.getChildren();
      for (let i = 0; i < children.length; i++) {
        if (children[i].get('display') !== 'none') {
          if (typeof children[i].drawInDocument === 'function') {
            children[i].drawInDocument(isClip, isMask);
          }
        }
      }
    };
  };
  var SvgElemContainer = function (obj, inherits) {
    SvgElemHasChildren.call(this, obj, inherits);
    this.drawContent = function (isClip, isMask) {
      this.transform();
      let clipped = this.clip(),
        masked = this.mask(),
        group;
      if ((this.get('opacity') < 1 || clipped || masked) && !isClip) {
        group = docBeginGroup(getPageBBox());
      }
      this.drawChildren(isClip, isMask);
      if (group) {
        docEndGroup(group);
        doc.fillOpacity(this.get('opacity'));
        docInsertGroup(group);
      }
    };
  };
  var SvgElemUse = function (obj, inherits) {
    SvgElemContainer.call(this, obj, inherits);
    let x = this.getLength('x', this.getVWidth(), 0),
      y = this.getLength('y', this.getVHeight(), 0),
      child = this.getUrl('href') || this.getUrl('xlink:href');
    if (child) {
      child = createSVGElement(child, this);
    }
    this.getChildren = function () {
      return child ? [child] : [];
    };
    this.drawInDocument = function (isClip, isMask) {
      doc.save();
      this.drawContent(isClip, isMask);
      doc.restore();
    };
    this.getTransformation = function () {
      return multiplyMatrix(this.get('transform'), [1, 0, 0, 1, x, y]);
    };
  };
  var SvgElemSymbol = function (obj, inherits) {
    SvgElemContainer.call(this, obj, inherits);
    let width = this.getLength('width', this.getParentVWidth(), this.getParentVWidth()),
      height = this.getLength('height', this.getParentVHeight(), this.getParentVHeight());
    if (inherits instanceof SvgElemUse) {
      width = inherits.getLength('width', inherits.getParentVWidth(), width);
      height = inherits.getLength('height', inherits.getParentVHeight(), height);
    }
    let aspectRatio = (this.attr('preserveAspectRatio') || '').trim(),
      viewBox = this.getViewbox('viewBox', [0, 0, width, height]);
    this.getVWidth = function () {
      return viewBox[2];
    };
    this.getVHeight = function () {
      return viewBox[3];
    };
    this.drawInDocument = function (isClip, isMask) {
      doc.save();
      this.drawContent(isClip, isMask);
      doc.restore();
    };
    this.getTransformation = function () {
      return multiplyMatrix(parseAspectRatio(aspectRatio, width, height, viewBox[2], viewBox[3]), [1, 0, 0, 1, -viewBox[0], -viewBox[1]]);
    };
  };
  var SvgElemGroup = function (obj, inherits) {
    SvgElemContainer.call(this, obj, inherits);
    this.drawInDocument = function (isClip, isMask) {
      doc.save();
      if (this.link && !isClip && !isMask) {
        this.addLink();
      }
      this.drawContent(isClip, isMask);
      doc.restore();
    };
    this.getTransformation = function () {
      return this.get('transform');
    };
  };
  var SvgElemLink = function (obj, inherits) {
    if (inherits && inherits.isText) {
      SvgElemTspan.call(this, obj, inherits);
      this.allowedChildren = ['textPath', 'tspan', '#text', '#cdata-section', 'a'];
    } else {
      SvgElemGroup.call(this, obj, inherits);
    }
    this.link = this.attr('href') || this.attr('xlink:href');
    this.addLink = function () {
      if (this.link.match(/^(?:[a-z][a-z0-9+.-]*:|\/\/)?/i) && this.getChildren().length) {
        let bbox = this.getBoundingShape().transform(getGlobalMatrix()).getBoundingBox();
        docInsertLink(bbox[0], bbox[1], bbox[2], bbox[3], this.link);
      }
    };
  };
  var SvgElemSvg = function (obj, inherits) {
    SvgElemContainer.call(this, obj, inherits);
    let width = this.getLength('width', this.getParentVWidth(), this.getParentVWidth()),
      height = this.getLength('height', this.getParentVHeight(), this.getParentVHeight()),
      x = this.getLength('x', this.getParentVWidth(), 0),
      y = this.getLength('y', this.getParentVHeight(), 0);
    if (inherits instanceof SvgElemUse) {
      width = inherits.getLength('width', inherits.getParentVWidth(), width);
      height = inherits.getLength('height', inherits.getParentVHeight(), height);
    }
    let aspectRatio = this.attr('preserveAspectRatio'),
      viewBox = this.getViewbox('viewBox', [0, 0, width, height]);
    if (this.isOuterElement && preserveAspectRatio) {
      x = y = 0;
      width = viewportWidth;
      height = viewportHeight;
      aspectRatio = preserveAspectRatio;
    }
    this.getVWidth = function () {
      return viewBox[2];
    };
    this.getVHeight = function () {
      return viewBox[3];
    };
    this.drawInDocument = function (isClip, isMask) {
      doc.save();
      if (this.get('overflow') === 'hidden') {
        new SvgShape().M(x, y).L(x + width, y).L(x + width, y + height).L(x, y + height).Z().transform(this.get('transform')).insertInDocument();
        doc.clip();
      }
      this.drawContent(isClip, isMask);
      doc.restore();
    };
    this.getTransformation = function () {
      return multiplyMatrix(this.get('transform'), [1, 0, 0, 1, x, y], parseAspectRatio(aspectRatio, width, height, viewBox[2], viewBox[3]), [1, 0, 0, 1, -viewBox[0], -viewBox[1]]);
    };
  };
  var SVGElemImage = function (obj, inherits) {
    SvgElemStylable.call(this, obj, inherits);
    let link = imageCallback(this.attr('href') || this.attr('xlink:href') || ''),
      x = this.getLength('x', this.getVWidth(), 0),
      y = this.getLength('y', this.getVHeight(), 0),
      width = this.getLength('width', this.getVWidth(), 'auto'),
      height = this.getLength('height', this.getVHeight(), 'auto'),
      image;
    try {
      image = doc.openImage(link);
    } catch (e) {
      warningCallback('SVGElemImage: failed to open image "' + link + '" in PDFKit');
    }
    if (image) {
      if (width === 'auto' && height !== 'auto') {
        width = height * image.width / image.height;
      } else if (height === 'auto' && width !== 'auto') {
        height = width * image.height / image.width;
      } else if (width === 'auto' && height === 'auto') {
        width = image.width;
        height = image.height;
      }
    }
    if (width === 'auto' || width < 0) {
      width = 0;
    }
    if (height === 'auto' || height < 0) {
      height = 0;
    }
    this.getTransformation = function () {
      return this.get('transform');
    };
    this.getBoundingShape = function () {
      return new SvgShape().M(x, y).L(x + width, y).M(x + width, y + height).L(x, y + height);
    };
    this.drawInDocument = function (isClip, isMask) {
      if (this.get('visibility') === 'hidden' || !image) {
        return;
      }
      doc.save();
      this.transform();
      if (this.get('overflow') === 'hidden') {
        doc.rect(x, y, width, height).clip();
      }
      this.clip();
      this.mask();
      doc.translate(x, y);
      doc.transform.apply(doc, parseAspectRatio(this.attr('preserveAspectRatio'), width, height, image ? image.width : width, image ? image.height : height));
      if (!isClip) {
        doc.fillOpacity(this.get('opacity'));
        doc.image(image, 0, 0);
      } else {
        doc.rect(0, 0, image.width, image.height);
        docFillColor(DefaultColors.white).fill();
      }
      doc.restore();
    };
  };
  var SvgElemPattern = function (obj, inherits, fallback) {
    SvgElemHasChildren.call(this, obj, inherits);
    this.ref = function () {
      let ref = this.getUrl('href') || this.getUrl('xlink:href');
      if (ref && ref.nodeName === obj.nodeName) {
        return new SvgElemPattern(ref, inherits, fallback);
      }
    }.call(this);
    let _attr = this.attr;
    this.attr = function (key) {
      let attr = _attr.call(this, key);
      if (attr != null || key === 'href' || key === 'xlink:href') {
        return attr;
      }
      return this.ref ? this.ref.attr(key) : null;
    };
    let _getChildren = this.getChildren;
    this.getChildren = function () {
      let children = _getChildren.call(this);
      if (children.length > 0) {
        return children;
      }
      return this.ref ? this.ref.getChildren() : [];
    };
    this.getPaint = function (bBox, gOpacity, isClip, isMask) {
      let bBoxUnitsPattern = this.attr('patternUnits') !== 'userSpaceOnUse',
        bBoxUnitsContent = this.attr('patternContentUnits') === 'objectBoundingBox',
        x = this.getLength('x', bBoxUnitsPattern ? 1 : this.getParentVWidth(), 0),
        y = this.getLength('y', bBoxUnitsPattern ? 1 : this.getParentVHeight(), 0),
        width = this.getLength('width', bBoxUnitsPattern ? 1 : this.getParentVWidth(), 0),
        height = this.getLength('height', bBoxUnitsPattern ? 1 : this.getParentVHeight(), 0);
      if (bBoxUnitsContent && !bBoxUnitsPattern) {
        // Use the same units for pattern & pattern content
        x = (x - bBox[0]) / (bBox[2] - bBox[0]) || 0;
        y = (y - bBox[1]) / (bBox[3] - bBox[1]) || 0;
        width = width / (bBox[2] - bBox[0]) || 0;
        height = height / (bBox[3] - bBox[1]) || 0;
      } else if (!bBoxUnitsContent && bBoxUnitsPattern) {
        x = bBox[0] + x * (bBox[2] - bBox[0]);
        y = bBox[1] + y * (bBox[3] - bBox[1]);
        width = width * (bBox[2] - bBox[0]);
        height = height * (bBox[3] - bBox[1]);
      }
      let viewBox = this.getViewbox('viewBox', [0, 0, width, height]),
        aspectRatio = (this.attr('preserveAspectRatio') || '').trim(),
        aspectRatioMatrix = multiplyMatrix(parseAspectRatio(aspectRatio, width, height, viewBox[2], viewBox[3], 0), [1, 0, 0, 1, -viewBox[0], -viewBox[1]]),
        matrix = parseTranform(this.attr('patternTransform'));
      if (bBoxUnitsContent) {
        matrix = multiplyMatrix([bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]], matrix);
      }
      matrix = multiplyMatrix(matrix, [1, 0, 0, 1, x, y]);
      if ((matrix = validateMatrix(matrix)) && (aspectRatioMatrix = validateMatrix(aspectRatioMatrix)) && (width = validateNumber(width)) && (height = validateNumber(height))) {
        let group = docBeginGroup([0, 0, width, height]);
        doc.transform.apply(doc, aspectRatioMatrix);
        this.drawChildren(isClip, isMask);
        docEndGroup(group);
        return [docCreatePattern(group, width, height, matrix), gOpacity];
      } else {
        return fallback ? [fallback[0], fallback[1] * gOpacity] : undefined;
      }
    };
    this.getVWidth = function () {
      let bBoxUnitsPattern = this.attr('patternUnits') !== 'userSpaceOnUse',
        width = this.getLength('width', bBoxUnitsPattern ? 1 : this.getParentVWidth(), 0);
      return this.getViewbox('viewBox', [0, 0, width, 0])[2];
    };
    this.getVHeight = function () {
      let bBoxUnitsPattern = this.attr('patternUnits') !== 'userSpaceOnUse',
        height = this.getLength('height', bBoxUnitsPattern ? 1 : this.getParentVHeight(), 0);
      return this.getViewbox('viewBox', [0, 0, 0, height])[3];
    };
  };
  var SvgElemGradient = function (obj, inherits, fallback) {
    SvgElem.call(this, obj, inherits);
    this.allowedChildren = ['stop'];
    this.ref = function () {
      let ref = this.getUrl('href') || this.getUrl('xlink:href');
      if (ref && ref.nodeName === obj.nodeName) {
        return new SvgElemGradient(ref, inherits, fallback);
      }
    }.call(this);
    let _attr = this.attr;
    this.attr = function (key) {
      let attr = _attr.call(this, key);
      if (attr != null || key === 'href' || key === 'xlink:href') {
        return attr;
      }
      return this.ref ? this.ref.attr(key) : null;
    };
    let _getChildren = this.getChildren;
    this.getChildren = function () {
      let children = _getChildren.call(this);
      if (children.length > 0) {
        return children;
      }
      return this.ref ? this.ref.getChildren() : [];
    };
    this.getPaint = function (bBox, gOpacity, isClip, isMask) {
      let children = this.getChildren();
      if (children.length === 0) {
        return;
      }
      if (children.length === 1) {
        let child = children[0],
          stopColor = child.get('stop-color');
        if (stopColor === 'none') {
          return;
        }
        return opacityToColor(stopColor, child.get('stop-opacity') * gOpacity, isMask);
      }
      let bBoxUnits = this.attr('gradientUnits') !== 'userSpaceOnUse',
        matrix = parseTranform(this.attr('gradientTransform')),
        spread = this.attr('spreadMethod'),
        grad,
        x1,
        x2,
        y1,
        y2,
        r2,
        nAfter = 0,
        nBefore = 0,
        nTotal = 1;
      if (bBoxUnits) {
        matrix = multiplyMatrix([bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]], matrix);
      }
      if (matrix = validateMatrix(matrix)) {
        if (this.name === 'linearGradient') {
          x1 = this.getLength('x1', bBoxUnits ? 1 : this.getVWidth(), 0);
          x2 = this.getLength('x2', bBoxUnits ? 1 : this.getVWidth(), bBoxUnits ? 1 : this.getVWidth());
          y1 = this.getLength('y1', bBoxUnits ? 1 : this.getVHeight(), 0);
          y2 = this.getLength('y2', bBoxUnits ? 1 : this.getVHeight(), 0);
        } else {
          x2 = this.getLength('cx', bBoxUnits ? 1 : this.getVWidth(), bBoxUnits ? 0.5 : 0.5 * this.getVWidth());
          y2 = this.getLength('cy', bBoxUnits ? 1 : this.getVHeight(), bBoxUnits ? 0.5 : 0.5 * this.getVHeight());
          r2 = this.getLength('r', bBoxUnits ? 1 : this.getViewport(), bBoxUnits ? 0.5 : 0.5 * this.getViewport());
          x1 = this.getLength('fx', bBoxUnits ? 1 : this.getVWidth(), x2);
          y1 = this.getLength('fy', bBoxUnits ? 1 : this.getVHeight(), y2);
          if (r2 < 0) {
            warningCallback('SvgElemGradient: negative r value');
          }
          let d = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
            multiplier = 1;
          if (d > r2) {
            // according to specification
            multiplier = r2 / d;
            x1 = x2 + (x1 - x2) * multiplier;
            y1 = y2 + (y1 - y2) * multiplier;
          }
          r2 = Math.max(r2, d * multiplier * (1 + 1e-6)); // fix for edge-case gradients see issue #84
        }
        if (spread === 'reflect' || spread === 'repeat') {
          let inv = inverseMatrix(matrix),
            corner1 = transformPoint([bBox[0], bBox[1]], inv),
            corner2 = transformPoint([bBox[2], bBox[1]], inv),
            corner3 = transformPoint([bBox[2], bBox[3]], inv),
            corner4 = transformPoint([bBox[0], bBox[3]], inv);
          if (this.name === 'linearGradient') {
            // See file 'gradient-repeat-maths.png'
            nAfter = Math.max((corner1[0] - x2) * (x2 - x1) + (corner1[1] - y2) * (y2 - y1), (corner2[0] - x2) * (x2 - x1) + (corner2[1] - y2) * (y2 - y1), (corner3[0] - x2) * (x2 - x1) + (corner3[1] - y2) * (y2 - y1), (corner4[0] - x2) * (x2 - x1) + (corner4[1] - y2) * (y2 - y1)) / (Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
            nBefore = Math.max((corner1[0] - x1) * (x1 - x2) + (corner1[1] - y1) * (y1 - y2), (corner2[0] - x1) * (x1 - x2) + (corner2[1] - y1) * (y1 - y2), (corner3[0] - x1) * (x1 - x2) + (corner3[1] - y1) * (y1 - y2), (corner4[0] - x1) * (x1 - x2) + (corner4[1] - y1) * (y1 - y2)) / (Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
          } else {
            nAfter = Math.sqrt(Math.max(Math.pow(corner1[0] - x2, 2) + Math.pow(corner1[1] - y2, 2), Math.pow(corner2[0] - x2, 2) + Math.pow(corner2[1] - y2, 2), Math.pow(corner3[0] - x2, 2) + Math.pow(corner3[1] - y2, 2), Math.pow(corner4[0] - x2, 2) + Math.pow(corner4[1] - y2, 2))) / r2 - 1;
          }
          nAfter = Math.ceil(nAfter + 0.5); // Add a little more because the stroke can extend outside of the bounding box
          nBefore = Math.ceil(nBefore + 0.5);
          nTotal = nBefore + 1 + nAfter; // How many times the gradient needs to be repeated to fill the object bounding box
        }
        if (this.name === 'linearGradient') {
          grad = doc.linearGradient(x1 - nBefore * (x2 - x1), y1 - nBefore * (y2 - y1), x2 + nAfter * (x2 - x1), y2 + nAfter * (y2 - y1));
        } else {
          grad = doc.radialGradient(x1, y1, 0, x2, y2, r2 + nAfter * r2);
        }
        for (let n = 0; n < nTotal; n++) {
          let offset = 0,
            inOrder = spread !== 'reflect' || (n - nBefore) % 2 === 0;
          for (let i = 0; i < children.length; i++) {
            let child = children[inOrder ? i : children.length - 1 - i],
              stopColor = child.get('stop-color');
            if (stopColor === 'none') {
              stopColor = DefaultColors.transparent;
            }
            stopColor = opacityToColor(stopColor, child.get('stop-opacity') * gOpacity, isMask);
            offset = Math.max(offset, inOrder ? child.getPercent('offset', 0) : 1 - child.getPercent('offset', 0));
            if (i === 0 && stopColor[0].length === 4) {
              grad._colorSpace = 'DeviceCMYK';
            } // Fix until PR #763 is merged into PDFKit
            if (i === 0 && offset > 0) {
              grad.stop((n + 0) / nTotal, stopColor[0], stopColor[1]);
            }
            grad.stop((n + offset) / (nAfter + nBefore + 1), stopColor[0], stopColor[1]);
            if (i === children.length - 1 && offset < 1) {
              grad.stop((n + 1) / nTotal, stopColor[0], stopColor[1]);
            }
          }
        }
        grad.setTransform.apply(grad, matrix);
        return [grad, 1];
      } else {
        return fallback ? [fallback[0], fallback[1] * gOpacity] : undefined;
      }
    };
  };
  var SvgElemBasicShape = function (obj, inherits) {
    SvgElemStylable.call(this, obj, inherits);
    this.dashScale = 1;
    this.getBoundingShape = function () {
      return this.shape;
    };
    this.getTransformation = function () {
      return this.get('transform');
    };
    this.drawInDocument = function (isClip, isMask) {
      if (this.get('visibility') === 'hidden' || !this.shape) {
        return;
      }
      doc.save();
      if (this.get('vector-effect') === 'non-scaling-stroke') {
        this.shape.transform(this.getTransformation());
      } else {
        this.transform();
      }
      this.clip();
      if (!isClip) {
        let masked = this.mask(),
          group;
        if (masked) {
          group = docBeginGroup(getPageBBox());
        }
        let subPaths = this.shape.getSubPaths(),
          fill = this.getFill(isClip, isMask),
          stroke = this.getStroke(isClip, isMask),
          lineWidth = this.get('stroke-width'),
          lineCap = this.get('stroke-linecap');
        if (this.get('vector-effect') === 'non-scaling-stroke') {
          lineWidth = lineWidth / getPageScale();
        }
        if (fill || stroke) {
          if (fill) {
            docFillColor(fill);
          }
          if (stroke) {
            for (let j = 0; j < subPaths.length; j++) {
              if (isEqual(subPaths[j].totalLength, 0)) {
                if ((lineCap === 'square' || lineCap === 'round') && lineWidth > 0) {
                  if (subPaths[j].startPoint && subPaths[j].startPoint.length > 1) {
                    let x = subPaths[j].startPoint[0],
                      y = subPaths[j].startPoint[1];
                    docFillColor(stroke);
                    if (lineCap === 'square') {
                      doc.rect(x - 0.5 * lineWidth, y - 0.5 * lineWidth, lineWidth, lineWidth);
                    } else if (lineCap === 'round') {
                      doc.circle(x, y, 0.5 * lineWidth);
                    }
                    doc.fill();
                  }
                }
              }
            }
            let dashArray = this.get('stroke-dasharray'),
              dashOffset = this.get('stroke-dashoffset');
            if (isNotEqual(this.dashScale, 1)) {
              for (let j = 0; j < dashArray.length; j++) {
                dashArray[j] *= this.dashScale;
              }
              dashOffset *= this.dashScale;
            }
            docStrokeColor(stroke);
            doc.lineWidth(lineWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(lineCap);
            docApplyDash(dashArray, dashOffset);
          }
          for (let j = 0; j < subPaths.length; j++) {
            if (subPaths[j].totalLength > 0) {
              subPaths[j].insertInDocument();
            }
          }
          if (fill && stroke) {
            doc.fillAndStroke(this.get('fill-rule'));
          } else if (fill) {
            doc.fill(this.get('fill-rule'));
          } else if (stroke) {
            doc.stroke();
          }
        }
        let markerStart = this.get('marker-start'),
          markerMid = this.get('marker-mid'),
          markerEnd = this.get('marker-end');
        if (markerStart !== 'none' || markerMid !== 'none' || markerEnd !== 'none') {
          let markersPos = this.shape.getMarkers();
          if (markerStart !== 'none' && markersPos.length > 0) {
            let marker = new SvgElemMarker(markerStart, null);
            marker.drawMarker(false, isMask, markersPos[0], lineWidth);
          }
          if (markerMid !== 'none') {
            for (let i = 1; i < markersPos.length - 1; i++) {
              let marker = new SvgElemMarker(markerMid, null);
              marker.drawMarker(false, isMask, markersPos[i], lineWidth);
            }
          }
          if (markerEnd !== 'none' && markersPos.length > 0) {
            let marker = new SvgElemMarker(markerEnd, null);
            marker.drawMarker(false, isMask, markersPos[markersPos.length - 1], lineWidth);
          }
        }
        if (group) {
          docEndGroup(group);
          docInsertGroup(group);
        }
      } else {
        this.shape.insertInDocument();
        docFillColor(DefaultColors.white);
        doc.fill(this.get('clip-rule'));
      }
      doc.restore();
    };
  };
  var SvgElemRect = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let x = this.getLength('x', this.getVWidth(), 0),
      y = this.getLength('y', this.getVHeight(), 0),
      w = this.getLength('width', this.getVWidth(), 0),
      h = this.getLength('height', this.getVHeight(), 0),
      rx = this.getLength('rx', this.getVWidth()),
      ry = this.getLength('ry', this.getVHeight());
    if (rx === undefined && ry === undefined) {
      rx = ry = 0;
    } else if (rx === undefined && ry !== undefined) {
      rx = ry;
    } else if (rx !== undefined && ry === undefined) {
      ry = rx;
    }
    if (w > 0 && h > 0) {
      if (rx && ry) {
        rx = Math.min(rx, 0.5 * w);
        ry = Math.min(ry, 0.5 * h);
        this.shape = new SvgShape().M(x + rx, y).L(x + w - rx, y).A(rx, ry, 0, 0, 1, x + w, y + ry).L(x + w, y + h - ry).A(rx, ry, 0, 0, 1, x + w - rx, y + h).L(x + rx, y + h).A(rx, ry, 0, 0, 1, x, y + h - ry).L(x, y + ry).A(rx, ry, 0, 0, 1, x + rx, y).Z();
      } else {
        this.shape = new SvgShape().M(x, y).L(x + w, y).L(x + w, y + h).L(x, y + h).Z();
      }
    } else {
      this.shape = new SvgShape();
    }
  };
  var SvgElemCircle = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let cx = this.getLength('cx', this.getVWidth(), 0),
      cy = this.getLength('cy', this.getVHeight(), 0),
      r = this.getLength('r', this.getViewport(), 0);
    if (r > 0) {
      this.shape = new SvgShape().M(cx + r, cy).A(r, r, 0, 0, 1, cx - r, cy).A(r, r, 0, 0, 1, cx + r, cy).Z();
    } else {
      this.shape = new SvgShape();
    }
  };
  var SvgElemEllipse = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let cx = this.getLength('cx', this.getVWidth(), 0),
      cy = this.getLength('cy', this.getVHeight(), 0),
      rx = this.getLength('rx', this.getVWidth(), 0),
      ry = this.getLength('ry', this.getVHeight(), 0);
    if (rx > 0 && ry > 0) {
      this.shape = new SvgShape().M(cx + rx, cy).A(rx, ry, 0, 0, 1, cx - rx, cy).A(rx, ry, 0, 0, 1, cx + rx, cy).Z();
    } else {
      this.shape = new SvgShape();
    }
  };
  var SvgElemLine = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let x1 = this.getLength('x1', this.getVWidth(), 0),
      y1 = this.getLength('y1', this.getVHeight(), 0),
      x2 = this.getLength('x2', this.getVWidth(), 0),
      y2 = this.getLength('y2', this.getVHeight(), 0);
    this.shape = new SvgShape().M(x1, y1).L(x2, y2);
  };
  var SvgElemPolyline = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let points = this.getNumberList('points');
    this.shape = new SvgShape();
    for (let i = 0; i < points.length - 1; i += 2) {
      if (i === 0) {
        this.shape.M(points[i], points[i + 1]);
      } else {
        this.shape.L(points[i], points[i + 1]);
      }
    }
    if (points.error) {
      warningCallback('SvgElemPolygon: unexpected string ' + points.error);
    }
    if (points.length % 2 === 1) {
      warningCallback('SvgElemPolyline: uneven number of coordinates');
    }
  };
  var SvgElemPolygon = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    let points = this.getNumberList('points');
    this.shape = new SvgShape();
    for (let i = 0; i < points.length - 1; i += 2) {
      if (i === 0) {
        this.shape.M(points[i], points[i + 1]);
      } else {
        this.shape.L(points[i], points[i + 1]);
      }
    }
    this.shape.Z();
    if (points.error) {
      warningCallback('SvgElemPolygon: unexpected string ' + points.error);
    }
    if (points.length % 2 === 1) {
      warningCallback('SvgElemPolygon: uneven number of coordinates');
    }
  };
  var SvgElemPath = function (obj, inherits) {
    SvgElemBasicShape.call(this, obj, inherits);
    this.shape = new SvgShape().path(this.attr('d'));
    let pathLength = this.getLength('pathLength', this.getViewport());
    this.pathLength = pathLength > 0 ? pathLength : undefined;
    this.dashScale = this.pathLength !== undefined ? this.shape.totalLength / this.pathLength : 1;
  };
  var SvgElemMarker = function (obj, inherits) {
    SvgElemHasChildren.call(this, obj, inherits);
    let width = this.getLength('markerWidth', this.getParentVWidth(), 3),
      height = this.getLength('markerHeight', this.getParentVHeight(), 3),
      viewBox = this.getViewbox('viewBox', [0, 0, width, height]);
    this.getVWidth = function () {
      return viewBox[2];
    };
    this.getVHeight = function () {
      return viewBox[3];
    };
    this.drawMarker = function (isClip, isMask, posArray, strokeWidth) {
      doc.save();
      let orient = this.attr('orient'),
        units = this.attr('markerUnits'),
        rotate = orient === 'auto' ? posArray[2] : (parseFloat(orient) || 0) * Math.PI / 180,
        scale = units === 'userSpaceOnUse' ? 1 : strokeWidth;
      doc.transform(Math.cos(rotate) * scale, Math.sin(rotate) * scale, -Math.sin(rotate) * scale, Math.cos(rotate) * scale, posArray[0], posArray[1]);
      let refX = this.getLength('refX', this.getVWidth(), 0),
        refY = this.getLength('refY', this.getVHeight(), 0),
        aspectRatioMatrix = parseAspectRatio(this.attr('preserveAspectRatio'), width, height, viewBox[2], viewBox[3], 0.5);
      if (this.get('overflow') === 'hidden') {
        doc.rect(aspectRatioMatrix[0] * (viewBox[0] + viewBox[2] / 2 - refX) - width / 2, aspectRatioMatrix[3] * (viewBox[1] + viewBox[3] / 2 - refY) - height / 2, width, height).clip();
      }
      doc.transform.apply(doc, aspectRatioMatrix);
      doc.translate(-refX, -refY);
      let group;
      if (this.get('opacity') < 1 && !isClip) {
        group = docBeginGroup(getPageBBox());
      }
      this.drawChildren(isClip, isMask);
      if (group) {
        docEndGroup(group);
        doc.fillOpacity(this.get('opacity'));
        docInsertGroup(group);
      }
      doc.restore();
    };
  };
  var SvgElemClipPath = function (obj, inherits) {
    SvgElemHasChildren.call(this, obj, inherits);
    this.useMask = function (bBox) {
      let group = docBeginGroup(getPageBBox());
      doc.save();
      doc.transform.apply(doc, this.get('transform'));
      if (this.attr('clipPathUnits') === 'objectBoundingBox') {
        doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
      }
      this.clip();
      this.drawChildren(true, false);
      doc.restore();
      docEndGroup(group);
      docApplyMask(group, true);
    };
  };
  var SvgElemMask = function (obj, inherits) {
    SvgElemHasChildren.call(this, obj, inherits);
    this.useMask = function (bBox) {
      let group = docBeginGroup(getPageBBox());
      doc.save();
      let x, y, w, h;
      if (this.attr('maskUnits') === 'userSpaceOnUse') {
        x = this.getLength('x', this.getVWidth(), -0.1 * (bBox[2] - bBox[0]) + bBox[0]);
        y = this.getLength('y', this.getVHeight(), -0.1 * (bBox[3] - bBox[1]) + bBox[1]);
        w = this.getLength('width', this.getVWidth(), 1.2 * (bBox[2] - bBox[0]));
        h = this.getLength('height', this.getVHeight(), 1.2 * (bBox[3] - bBox[1]));
      } else {
        x = this.getLength('x', this.getVWidth(), -0.1) * (bBox[2] - bBox[0]) + bBox[0];
        y = this.getLength('y', this.getVHeight(), -0.1) * (bBox[3] - bBox[1]) + bBox[1];
        w = this.getLength('width', this.getVWidth(), 1.2) * (bBox[2] - bBox[0]);
        h = this.getLength('height', this.getVHeight(), 1.2) * (bBox[3] - bBox[1]);
      }
      if (this.attr('maskContentUnits') === 'objectBoundingBox') {
        doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
      }
      this.clip();
      this.drawChildren(false, true);
      doc.restore();
      docEndGroup(group);
      docApplyMask(group, true);
    };
  };
  var SvgElemTextContainer = function (obj, inherits) {
    SvgElemStylable.call(this, obj, inherits);
    this.allowedChildren = ['tspan', '#text', '#cdata-section', 'a'];
    this.isText = true;
    this.getBoundingShape = function () {
      let shape = new SvgShape();
      for (let i = 0; i < this._pos.length; i++) {
        let pos = this._pos[i];
        if (!pos.hidden) {
          let dx0 = pos.ascent * Math.sin(pos.rotate),
            dy0 = -pos.ascent * Math.cos(pos.rotate),
            dx1 = pos.descent * Math.sin(pos.rotate),
            dy1 = -pos.descent * Math.cos(pos.rotate),
            dx2 = pos.width * Math.cos(pos.rotate),
            dy2 = pos.width * Math.sin(pos.rotate);
          shape.M(pos.x + dx0, pos.y + dy0).L(pos.x + dx0 + dx2, pos.y + dy0 + dy2).M(pos.x + dx1 + dx2, pos.y + dy1 + dy2).L(pos.x + dx1, pos.y + dy1);
        }
      }
      return shape;
    };
    this.drawTextInDocument = function (isClip, isMask) {
      if (this.link && !isClip && !isMask) {
        this.addLink();
      }
      if (this.get('text-decoration') === 'underline') {
        this.decorate(0.05 * this._font.size, -0.075 * this._font.size, isClip, isMask);
      }
      if (this.get('text-decoration') === 'overline') {
        this.decorate(0.05 * this._font.size, getAscent(this._font.font, this._font.size) + 0.075 * this._font.size, isClip, isMask);
      }
      let fill = this.getFill(isClip, isMask),
        stroke = this.getStroke(isClip, isMask),
        strokeWidth = this.get('stroke-width');
      if (this._font.fauxBold) {
        if (!stroke) {
          stroke = fill;
          strokeWidth = this._font.size * 0.03;
        } else {
          strokeWidth += this._font.size * 0.03;
        }
      }
      let children = this.getChildren();
      for (let i = 0; i < children.length; i++) {
        let childElem = children[i];
        switch (childElem.name) {
          case 'tspan':
          case 'textPath':
          case 'a':
            if (childElem.get('display') !== 'none') {
              childElem.drawTextInDocument(isClip, isMask);
            }
            break;
          case '#text':
          case '#cdata-section':
            if (this.get('visibility') === 'hidden') {
              continue;
            }
            if (fill || stroke || isClip) {
              if (fill) {
                docFillColor(fill);
              }
              if (stroke && strokeWidth) {
                docStrokeColor(stroke);
                doc.lineWidth(strokeWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap'));
                docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
              }
              docBeginText(this._font.font, this._font.size);
              docSetTextMode(!!fill, !!stroke);
              docWriteGlyphs(childElem._pos, this._font);
              docEndText();
            }
            break;
        }
      }
      if (this.get('text-decoration') === 'line-through') {
        this.decorate(0.05 * this._font.size, 0.5 * (getAscent(this._font.font, this._font.size) + getDescent(this._font.font, this._font.size)), isClip, isMask);
      }
    };
    this.decorate = function (lineWidth, linePosition, isClip, isMask) {
      let fill = this.getFill(isClip, isMask),
        stroke = this.getStroke(isClip, isMask);
      if (fill) {
        docFillColor(fill);
      }
      if (stroke) {
        docStrokeColor(stroke);
        doc.lineWidth(this.get('stroke-width')).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap'));
        docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
      }
      for (let j = 0, pos = this._pos; j < pos.length; j++) {
        if (!pos[j].hidden && isNotEqual(pos[j].width, 0)) {
          let dx0 = (linePosition + lineWidth / 2) * Math.sin(pos[j].rotate),
            dy0 = -(linePosition + lineWidth / 2) * Math.cos(pos[j].rotate),
            dx1 = (linePosition - lineWidth / 2) * Math.sin(pos[j].rotate),
            dy1 = -(linePosition - lineWidth / 2) * Math.cos(pos[j].rotate),
            dx2 = pos[j].width * Math.cos(pos[j].rotate),
            dy2 = pos[j].width * Math.sin(pos[j].rotate);
          new SvgShape().M(pos[j].x + dx0, pos[j].y + dy0).L(pos[j].x + dx0 + dx2, pos[j].y + dy0 + dy2).L(pos[j].x + dx1 + dx2, pos[j].y + dy1 + dy2).L(pos[j].x + dx1, pos[j].y + dy1).Z().insertInDocument();
          if (fill && stroke) {
            doc.fillAndStroke();
          } else if (fill) {
            doc.fill();
          } else if (stroke) {
            doc.stroke();
          }
        }
      }
    };
  };
  var SvgElemTextNode = function (obj, inherits) {
    this.name = obj.nodeName;
    this.textContent = obj.nodeValue;
  };
  var SvgElemTspan = function (obj, inherits) {
    SvgElemTextContainer.call(this, obj, inherits);
  };
  var SvgElemTextPath = function (obj, inherits) {
    SvgElemTextContainer.call(this, obj, inherits);
    let pathObject, pathLength, temp;
    if ((temp = this.attr('path')) && temp.trim() !== '') {
      let pathLength = this.getLength('pathLength', this.getViewport());
      this.pathObject = new SvgShape().path(temp);
      this.pathLength = pathLength > 0 ? pathLength : this.pathObject.totalLength;
      this.pathScale = this.pathObject.totalLength / this.pathLength;
    } else if ((temp = this.getUrl('href') || this.getUrl('xlink:href')) && temp.nodeName === 'path') {
      let pathElem = new SvgElemPath(temp, this);
      this.pathObject = pathElem.shape.clone().transform(pathElem.get('transform'));
      this.pathLength = this.chooseValue(pathElem.pathLength, this.pathObject.totalLength);
      this.pathScale = this.pathObject.totalLength / this.pathLength;
    }
  };
  var SvgElemText = function (obj, inherits) {
    SvgElemTextContainer.call(this, obj, inherits);
    this.allowedChildren = ['textPath', 'tspan', '#text', '#cdata-section', 'a'];
    (function (textParentElem) {
      let processedText = '',
        remainingText = obj.textContent,
        textPaths = [],
        currentChunk = [],
        currentAnchor,
        currentDirection,
        currentX = 0,
        currentY = 0;
      function doAnchoring() {
        if (currentChunk.length) {
          let last = currentChunk[currentChunk.length - 1];
          let first = currentChunk[0];
          let width = last.x + last.width - first.x;
          let anchordx = {
            'startltr': 0,
            'middleltr': 0.5,
            'endltr': 1,
            'startrtl': 1,
            'middlertl': 0.5,
            'endrtl': 0
          }[currentAnchor + currentDirection] * width || 0;
          for (let i = 0; i < currentChunk.length; i++) {
            currentChunk[i].x -= anchordx;
          }
        }
        currentChunk = [];
      }
      function adjustLength(pos, length, spacingAndGlyphs) {
        let firstChar = pos[0],
          lastChar = pos[pos.length - 1],
          startX = firstChar.x,
          endX = lastChar.x + lastChar.width;
        if (spacingAndGlyphs) {
          let textScale = length / (endX - startX);
          if (textScale > 0 && textScale < Infinity) {
            for (let j = 0; j < pos.length; j++) {
              pos[j].continuous = false;
              pos[j].x = startX + textScale * (pos[j].x - startX);
              pos[j].scale *= textScale;
              pos[j].width *= textScale;
            }
          }
        } else {
          if (pos.length >= 2) {
            let spaceDiff = (length - (endX - startX)) / (pos.length - 1);
            for (let j = 0; j < pos.length; j++) {
              pos[j].continuous = false;
              pos[j].x += j * spaceDiff;
            }
          }
        }
        currentX += length - (endX - startX);
      }
      function recursive(currentElem, parentElem) {
        currentElem._x = combineArrays(currentElem.getLengthList('x', currentElem.getVWidth()), parentElem ? parentElem._x.slice(parentElem._pos.length) : []);
        currentElem._y = combineArrays(currentElem.getLengthList('y', currentElem.getVHeight()), parentElem ? parentElem._y.slice(parentElem._pos.length) : []);
        currentElem._dx = combineArrays(currentElem.getLengthList('dx', currentElem.getVWidth()), parentElem ? parentElem._dx.slice(parentElem._pos.length) : []);
        currentElem._dy = combineArrays(currentElem.getLengthList('dy', currentElem.getVHeight()), parentElem ? parentElem._dy.slice(parentElem._pos.length) : []);
        currentElem._rot = combineArrays(currentElem.getNumberList('rotate'), parentElem ? parentElem._rot.slice(parentElem._pos.length) : []);
        currentElem._defRot = currentElem.chooseValue(currentElem._rot[currentElem._rot.length - 1], parentElem && parentElem._defRot, 0);
        if (currentElem.name === 'textPath') {
          currentElem._y = [];
        }
        let fontOptions = {
            fauxItalic: false,
            fauxBold: false
          },
          fontNameorLink = fontCallback(currentElem.get('font-family'), currentElem.get('font-weight') === 'bold', currentElem.get('font-style') === 'italic', fontOptions);
        try {
          doc.font(fontNameorLink);
        } catch (e) {
          warningCallback('SVGElemText: failed to open font "' + fontNameorLink + '" in PDFKit: ' + e.message);
        }
        currentElem._pos = [];
        currentElem._index = 0;
        currentElem._font = {
          font: doc._font,
          size: currentElem.get('font-size'),
          fauxItalic: fontOptions.fauxItalic,
          fauxBold: fontOptions.fauxBold
        };
        let textLength = currentElem.getLength('textLength', currentElem.getVWidth(), undefined),
          spacingAndGlyphs = currentElem.attr('lengthAdjust') === 'spacingAndGlyphs',
          wordSpacing = currentElem.get('word-spacing'),
          letterSpacing = currentElem.get('letter-spacing'),
          textAnchor = currentElem.get('text-anchor'),
          textDirection = currentElem.get('direction'),
          // `alignment-baseline` and `baseline-shift` have no effect on
          // `<text>` elements according to the SVG spec. So, detect when
          // we're styling a `<text>` element and ignore
          // `alignment-baseline` (only factoring in `dominant-baseline`)
          // and `baseline-shift` (which can only have the default value of
          // `baseline`).
          //
          // Note that Chrome (as of v99) incorrectly factors in
          // `alignment-baseline` on `<text>` elements, while Firefox
          // correctly follows the spec and ignores it. This means that our
          // output will differ from Chrome's in these cases, but conform to
          // SVG specification.
          isTextElem = currentElem.name === 'text',
          baselineAttr = isTextElem ? currentElem.get('dominant-baseline') : currentElem.get('alignment-baseline') || currentElem.get('dominant-baseline'),
          baselineShiftAttr = isTextElem ? 'baseline' : currentElem.get('baseline-shift'),
          baseline = getBaseline(currentElem._font.font, currentElem._font.size, baselineAttr, baselineShiftAttr);
        if (currentElem.name === 'textPath') {
          doAnchoring();
          currentX = currentY = 0;
        }
        let children = currentElem.getChildren();
        for (let i = 0; i < children.length; i++) {
          let childElem = children[i];
          switch (childElem.name) {
            case 'tspan':
            case 'textPath':
            case 'a':
              recursive(childElem, currentElem);
              break;
            case '#text':
            case '#cdata-section':
              let rawText = childElem.textContent,
                renderedText = rawText,
                words;
              childElem._font = currentElem._font;
              childElem._pos = [];
              remainingText = remainingText.substring(rawText.length);
              if (currentElem.get('xml:space') === 'preserve') {
                renderedText = renderedText.replace(/[\s]/g, ' ');
              } else {
                renderedText = renderedText.replace(/[\s]+/g, ' ');
                if (processedText.match(/[\s]$|^$/)) {
                  renderedText = renderedText.replace(/^[\s]/, '');
                }
                if (remainingText.match(/^[\s]*$/)) {
                  renderedText = renderedText.replace(/[\s]$/, '');
                }
              }
              processedText += rawText;
              if (wordSpacing === 0) {
                words = [renderedText];
              } else {
                words = renderedText.split(/(\s)/);
              }
              for (let w = 0; w < words.length; w++) {
                let pos = getTextPos(currentElem._font.font, currentElem._font.size, words[w]);
                for (let j = 0; j < pos.length; j++) {
                  let index = currentElem._index,
                    xAttr = currentElem._x[index],
                    yAttr = currentElem._y[index],
                    dxAttr = currentElem._dx[index],
                    dyAttr = currentElem._dy[index],
                    rotAttr = currentElem._rot[index],
                    continuous = !(w === 0 && j === 0);
                  if (letterSpacing !== 0) {
                    continuous = false;
                  }
                  if (wordSpacing !== 0) {
                    continuous = false;
                  }
                  if (xAttr !== undefined) {
                    continuous = false;
                    doAnchoring();
                    currentX = xAttr;
                  }
                  if (yAttr !== undefined) {
                    continuous = false;
                    doAnchoring();
                    currentY = yAttr;
                  }
                  if (dxAttr !== undefined) {
                    continuous = false;
                    currentX += dxAttr;
                  }
                  if (dyAttr !== undefined) {
                    continuous = false;
                    currentY += dyAttr;
                  }
                  if (rotAttr !== undefined || currentElem._defRot !== 0) {
                    continuous = false;
                  }
                  let position = {
                    glyph: pos[j].glyph,
                    rotate: Math.PI / 180 * currentElem.chooseValue(rotAttr, currentElem._defRot),
                    x: currentX + pos[j].xOffset,
                    kern: pos[j].kern,
                    y: currentY + baseline + pos[j].yOffset,
                    width: pos[j].width,
                    ascent: getAscent(currentElem._font.font, currentElem._font.size),
                    descent: getDescent(currentElem._font.font, currentElem._font.size),
                    scale: 1,
                    hidden: false,
                    continuous: continuous
                  };
                  currentChunk.push(position);
                  childElem._pos.push(position);
                  currentElem._pos.push(position);
                  currentElem._index += pos[j].unicode.length;
                  if (currentChunk.length === 1) {
                    currentAnchor = textAnchor;
                    currentDirection = textDirection;
                  }
                  currentX += pos[j].xAdvance + letterSpacing;
                  currentY += pos[j].yAdvance;
                }
                if (words[w] === ' ') {
                  currentX += wordSpacing;
                }
              }
              break;
            default:
              remainingText = remainingText.substring(childElem.textContent.length);
          }
        }
        if (textLength && currentElem._pos.length) {
          adjustLength(currentElem._pos, textLength, spacingAndGlyphs);
        }
        if (currentElem.name === 'textPath' || currentElem.name === 'text') {
          doAnchoring();
        }
        if (currentElem.name === 'textPath') {
          textPaths.push(currentElem);
          let pathObject = currentElem.pathObject;
          if (pathObject) {
            currentX = pathObject.endPoint[0];
            currentY = pathObject.endPoint[1];
          }
        }
        if (parentElem) {
          parentElem._pos = parentElem._pos.concat(currentElem._pos);
          parentElem._index += currentElem._index;
        }
      }
      function textOnPath(currentElem) {
        let pathObject = currentElem.pathObject,
          pathLength = currentElem.pathLength,
          pathScale = currentElem.pathScale;
        if (pathObject) {
          let textOffset = currentElem.getLength('startOffset', pathLength, 0);
          for (let j = 0; j < currentElem._pos.length; j++) {
            let charMidX = textOffset + currentElem._pos[j].x + 0.5 * currentElem._pos[j].width;
            if (charMidX > pathLength || charMidX < 0) {
              currentElem._pos[j].hidden = true;
            } else {
              let pointOnPath = pathObject.getPointAtLength(charMidX * pathScale);
              if (isNotEqual(pathScale, 1)) {
                currentElem._pos[j].scale *= pathScale;
                currentElem._pos[j].width *= pathScale;
              }
              currentElem._pos[j].x = pointOnPath[0] - 0.5 * currentElem._pos[j].width * Math.cos(pointOnPath[2]) - currentElem._pos[j].y * Math.sin(pointOnPath[2]);
              currentElem._pos[j].y = pointOnPath[1] - 0.5 * currentElem._pos[j].width * Math.sin(pointOnPath[2]) + currentElem._pos[j].y * Math.cos(pointOnPath[2]);
              currentElem._pos[j].rotate = pointOnPath[2] + currentElem._pos[j].rotate;
              currentElem._pos[j].continuous = false;
            }
          }
        } else {
          for (let j = 0; j < currentElem._pos.length; j++) {
            currentElem._pos[j].hidden = true;
          }
        }
      }
      recursive(textParentElem, null);
      for (let i = 0; i < textPaths.length; i++) {
        textOnPath(textPaths[i]);
      }
    })(this);
    this.getTransformation = function () {
      return this.get('transform');
    };
    this.drawInDocument = function (isClip, isMask) {
      doc.save();
      this.transform();
      this.clip();
      let masked = this.mask(),
        group;
      if (masked) {
        group = docBeginGroup(getPageBBox());
      }
      this.drawTextInDocument(isClip, isMask);
      if (group) {
        docEndGroup(group);
        docInsertGroup(group);
      }
      doc.restore();
    };
  };
  options = options || {};
  var pxToPt = options.assumePt ? 1 : 72 / 96,
    // 1px = 72/96pt, but only if assumePt is false
    viewportWidth = (options.width || doc.page.width) / pxToPt,
    viewportHeight = (options.height || doc.page.height) / pxToPt,
    preserveAspectRatio = options.preserveAspectRatio || null,
    // default to null so that the attr can override if not passed
    useCSS = options.useCSS && typeof SVGElement !== 'undefined' && svg instanceof SVGElement && typeof getComputedStyle === 'function',
    warningCallback = options.warningCallback,
    fontCallback = options.fontCallback,
    imageCallback = options.imageCallback,
    colorCallback = options.colorCallback,
    documentCallback = options.documentCallback,
    precision = Math.ceil(Math.max(1, options.precision)) || 3,
    groupStack = [],
    documentCache = {},
    links = [],
    styleRules = [];
  if (typeof warningCallback !== 'function') {
    warningCallback = function (str) {
      if (typeof console !== undefined && typeof console.warn === 'function') {
        console.warn(str);
      }
    };
  }
  if (typeof fontCallback !== 'function') {
    fontCallback = function (family, bold, italic, fontOptions) {
      // Check if the font is already registered in the document
      if (bold && italic) {
        if (doc._registeredFonts.hasOwnProperty(family + '-BoldItalic')) {
          return family + '-BoldItalic';
        } else if (doc._registeredFonts.hasOwnProperty(family + '-Italic')) {
          fontOptions.fauxBold = true;
          return family + '-Italic';
        } else if (doc._registeredFonts.hasOwnProperty(family + '-Bold')) {
          fontOptions.fauxItalic = true;
          return family + '-Bold';
        } else if (doc._registeredFonts.hasOwnProperty(family)) {
          fontOptions.fauxBold = true;
          fontOptions.fauxItalic = true;
          return family;
        }
      }
      if (bold && !italic) {
        if (doc._registeredFonts.hasOwnProperty(family + '-Bold')) {
          return family + '-Bold';
        } else if (doc._registeredFonts.hasOwnProperty(family)) {
          fontOptions.fauxBold = true;
          return family;
        }
      }
      if (!bold && italic) {
        if (doc._registeredFonts.hasOwnProperty(family + '-Italic')) {
          return family + '-Italic';
        } else if (doc._registeredFonts.hasOwnProperty(family)) {
          fontOptions.fauxItalic = true;
          return family;
        }
      }
      if (!bold && !italic) {
        if (doc._registeredFonts.hasOwnProperty(family)) {
          return family;
        }
      }
      // Use standard fonts as fallback
      if (family.match(/(?:^|,)\s*serif\s*$/)) {
        if (bold && italic) {
          return 'Times-BoldItalic';
        }
        if (bold && !italic) {
          return 'Times-Bold';
        }
        if (!bold && italic) {
          return 'Times-Italic';
        }
        if (!bold && !italic) {
          return 'Times-Roman';
        }
      } else if (family.match(/(?:^|,)\s*monospace\s*$/)) {
        if (bold && italic) {
          return 'Courier-BoldOblique';
        }
        if (bold && !italic) {
          return 'Courier-Bold';
        }
        if (!bold && italic) {
          return 'Courier-Oblique';
        }
        if (!bold && !italic) {
          return 'Courier';
        }
      } else if (family.match(/(?:^|,)\s*sans-serif\s*$/) || true) {
        if (bold && italic) {
          return 'Helvetica-BoldOblique';
        }
        if (bold && !italic) {
          return 'Helvetica-Bold';
        }
        if (!bold && italic) {
          return 'Helvetica-Oblique';
        }
        if (!bold && !italic) {
          return 'Helvetica';
        }
      }
    };
  }
  if (typeof imageCallback !== 'function') {
    imageCallback = function (link) {
      return link.replace(/\s+/g, '');
    };
  }
  if (typeof colorCallback !== 'function') {
    colorCallback = null;
  } else {
    for (let color in DefaultColors) {
      let newColor = colorCallback(DefaultColors[color]);
      DefaultColors[color][0] = newColor[0];
      DefaultColors[color][1] = newColor[1];
    }
  }
  if (typeof documentCallback !== 'function') {
    documentCallback = null;
  }
  if (typeof svg === 'string') {
    svg = parseXml(svg);
  }
  if (svg) {
    let styles = svg.getElementsByTagName('style');
    for (let i = 0; i < styles.length; i++) {
      styleRules = styleRules.concat(parseStyleSheet(styles[i].textContent));
    }
    let elem = createSVGElement(svg, null);
    if (typeof elem.drawInDocument === 'function') {
      if (options.useCSS && !useCSS) {
        warningCallback('SVGtoPDF: useCSS option can only be used for SVG *elements* in compatible browsers');
      }
      let savedFillColor = doc._fillColor;
      doc.save().translate(x || 0, y || 0).scale(pxToPt);
      elem.drawInDocument();
      for (let i = 0; i < links.length; i++) {
        doc.page.annotations.push(links[i]);
      }
      doc.restore();
      doc._fillColor = savedFillColor;
    } else {
      warningCallback('SVGtoPDF: this element can\'t be rendered directly: ' + svg.nodeName);
    }
  } else {
    warningCallback('SVGtoPDF: the input does not look like a valid SVG');
  }
};
if (typeof module !== 'undefined' && module && typeof module.exports !== 'undefined') {
  module.exports = SVGtoPDF;
}