a1b7e6b04351eec8928e4f0c8ed0f4c6dedb7602e8ef435772fff9bbca42ffe522d98a861c3856864e79df61eab4953eeb0a7c50bc7026f965923af619c342 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. 'use strict';
  2. /**
  3. * @typedef {import('../types').PathDataCommand} PathDataCommand
  4. */
  5. /**
  6. * Encode plain SVG data string into Data URI string.
  7. *
  8. * @type {(str: string, type?: 'base64' | 'enc' | 'unenc') => string}
  9. */
  10. exports.encodeSVGDatauri = (str, type) => {
  11. var prefix = 'data:image/svg+xml';
  12. if (!type || type === 'base64') {
  13. // base64
  14. prefix += ';base64,';
  15. str = prefix + Buffer.from(str).toString('base64');
  16. } else if (type === 'enc') {
  17. // URI encoded
  18. str = prefix + ',' + encodeURIComponent(str);
  19. } else if (type === 'unenc') {
  20. // unencoded
  21. str = prefix + ',' + str;
  22. }
  23. return str;
  24. };
  25. /**
  26. * Decode SVG Data URI string into plain SVG string.
  27. *
  28. * @type {(str: string) => string}
  29. */
  30. exports.decodeSVGDatauri = (str) => {
  31. var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
  32. var match = regexp.exec(str);
  33. // plain string
  34. if (!match) return str;
  35. var data = match[3];
  36. if (match[2]) {
  37. // base64
  38. str = Buffer.from(data, 'base64').toString('utf8');
  39. } else if (data.charAt(0) === '%') {
  40. // URI encoded
  41. str = decodeURIComponent(data);
  42. } else if (data.charAt(0) === '<') {
  43. // unencoded
  44. str = data;
  45. }
  46. return str;
  47. };
  48. /**
  49. * @typedef {{
  50. * noSpaceAfterFlags?: boolean,
  51. * leadingZero?: boolean,
  52. * negativeExtraSpace?: boolean
  53. * }} CleanupOutDataParams
  54. */
  55. /**
  56. * Convert a row of numbers to an optimized string view.
  57. *
  58. * @example
  59. * [0, -1, .5, .5] → "0-1 .5.5"
  60. *
  61. * @type {(data: Array<number>, params: CleanupOutDataParams, command?: PathDataCommand) => string}
  62. */
  63. exports.cleanupOutData = (data, params, command) => {
  64. let str = '';
  65. let delimiter;
  66. /**
  67. * @type {number}
  68. */
  69. let prev;
  70. data.forEach((item, i) => {
  71. // space delimiter by default
  72. delimiter = ' ';
  73. // no extra space in front of first number
  74. if (i == 0) delimiter = '';
  75. // no extra space after 'arcto' command flags(large-arc and sweep flags)
  76. // a20 60 45 0 1 30 20 → a20 60 45 0130 20
  77. if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
  78. var pos = i % 7;
  79. if (pos == 4 || pos == 5) delimiter = '';
  80. }
  81. // remove floating-point numbers leading zeros
  82. // 0.5 → .5
  83. // -0.5 → -.5
  84. const itemStr = params.leadingZero
  85. ? removeLeadingZero(item)
  86. : item.toString();
  87. // no extra space in front of negative number or
  88. // in front of a floating number if a previous number is floating too
  89. if (
  90. params.negativeExtraSpace &&
  91. delimiter != '' &&
  92. (item < 0 || (itemStr.charAt(0) === '.' && prev % 1 !== 0))
  93. ) {
  94. delimiter = '';
  95. }
  96. // save prev item value
  97. prev = item;
  98. str += delimiter + itemStr;
  99. });
  100. return str;
  101. };
  102. /**
  103. * Remove floating-point numbers leading zero.
  104. *
  105. * @example
  106. * 0.5 → .5
  107. *
  108. * @example
  109. * -0.5 → -.5
  110. *
  111. * @type {(num: number) => string}
  112. */
  113. const removeLeadingZero = (num) => {
  114. var strNum = num.toString();
  115. if (0 < num && num < 1 && strNum.charAt(0) === '0') {
  116. strNum = strNum.slice(1);
  117. } else if (-1 < num && num < 0 && strNum.charAt(1) === '0') {
  118. strNum = strNum.charAt(0) + strNum.slice(2);
  119. }
  120. return strNum;
  121. };
  122. exports.removeLeadingZero = removeLeadingZero;