cleanupListOfValues.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 'use strict';
  2. const { removeLeadingZero } = require('../lib/svgo/tools.js');
  3. exports.name = 'cleanupListOfValues';
  4. exports.type = 'visitor';
  5. exports.active = false;
  6. exports.description = 'rounds list of values to the fixed precision';
  7. const regNumericValues =
  8. /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
  9. const regSeparator = /\s+,?\s*|,\s*/;
  10. const absoluteLengths = {
  11. // relative to px
  12. cm: 96 / 2.54,
  13. mm: 96 / 25.4,
  14. in: 96,
  15. pt: 4 / 3,
  16. pc: 16,
  17. px: 1,
  18. };
  19. /**
  20. * Round list of values to the fixed precision.
  21. *
  22. * @example
  23. * <svg viewBox="0 0 200.28423 200.28423" enable-background="new 0 0 200.28423 200.28423">
  24. * ⬇
  25. * <svg viewBox="0 0 200.284 200.284" enable-background="new 0 0 200.284 200.284">
  26. *
  27. * <polygon points="208.250977 77.1308594 223.069336 ... "/>
  28. * ⬇
  29. * <polygon points="208.251 77.131 223.069 ... "/>
  30. *
  31. * @author kiyopikko
  32. *
  33. * @type {import('../lib/types').Plugin<{
  34. * floatPrecision?: number,
  35. * leadingZero?: boolean,
  36. * defaultPx?: boolean,
  37. * convertToPx?: boolean
  38. * }>}
  39. */
  40. exports.fn = (_root, params) => {
  41. const {
  42. floatPrecision = 3,
  43. leadingZero = true,
  44. defaultPx = true,
  45. convertToPx = true,
  46. } = params;
  47. /**
  48. * @type {(lists: string) => string}
  49. */
  50. const roundValues = (lists) => {
  51. const roundedList = [];
  52. for (const elem of lists.split(regSeparator)) {
  53. const match = elem.match(regNumericValues);
  54. const matchNew = elem.match(/new/);
  55. // if attribute value matches regNumericValues
  56. if (match) {
  57. // round it to the fixed precision
  58. let num = Number(Number(match[1]).toFixed(floatPrecision));
  59. /**
  60. * @type {any}
  61. */
  62. let matchedUnit = match[3] || '';
  63. /**
  64. * @type{'' | keyof typeof absoluteLengths}
  65. */
  66. let units = matchedUnit;
  67. // convert absolute values to pixels
  68. if (convertToPx && units && units in absoluteLengths) {
  69. const pxNum = Number(
  70. (absoluteLengths[units] * Number(match[1])).toFixed(floatPrecision)
  71. );
  72. if (pxNum.toString().length < match[0].length) {
  73. num = pxNum;
  74. units = 'px';
  75. }
  76. }
  77. // and remove leading zero
  78. let str;
  79. if (leadingZero) {
  80. str = removeLeadingZero(num);
  81. } else {
  82. str = num.toString();
  83. }
  84. // remove default 'px' units
  85. if (defaultPx && units === 'px') {
  86. units = '';
  87. }
  88. roundedList.push(str + units);
  89. }
  90. // if attribute value is "new"(only enable-background).
  91. else if (matchNew) {
  92. roundedList.push('new');
  93. } else if (elem) {
  94. roundedList.push(elem);
  95. }
  96. }
  97. return roundedList.join(' ');
  98. };
  99. return {
  100. element: {
  101. enter: (node) => {
  102. if (node.attributes.points != null) {
  103. node.attributes.points = roundValues(node.attributes.points);
  104. }
  105. if (node.attributes['enable-background'] != null) {
  106. node.attributes['enable-background'] = roundValues(
  107. node.attributes['enable-background']
  108. );
  109. }
  110. if (node.attributes.viewBox != null) {
  111. node.attributes.viewBox = roundValues(node.attributes.viewBox);
  112. }
  113. if (node.attributes['stroke-dasharray'] != null) {
  114. node.attributes['stroke-dasharray'] = roundValues(
  115. node.attributes['stroke-dasharray']
  116. );
  117. }
  118. if (node.attributes.dx != null) {
  119. node.attributes.dx = roundValues(node.attributes.dx);
  120. }
  121. if (node.attributes.dy != null) {
  122. node.attributes.dy = roundValues(node.attributes.dy);
  123. }
  124. if (node.attributes.x != null) {
  125. node.attributes.x = roundValues(node.attributes.x);
  126. }
  127. if (node.attributes.y != null) {
  128. node.attributes.y = roundValues(node.attributes.y);
  129. }
  130. },
  131. },
  132. };
  133. };