convertColors.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. 'use strict';
  2. const collections = require('./_collections.js');
  3. exports.type = 'visitor';
  4. exports.name = 'convertColors';
  5. exports.active = true;
  6. exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
  7. const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)';
  8. const rComma = '\\s*,\\s*';
  9. const regRGB = new RegExp(
  10. '^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'
  11. );
  12. const regHEX = /^#(([a-fA-F0-9])\2){3}$/;
  13. /**
  14. * Convert [r, g, b] to #rrggbb.
  15. *
  16. * @see https://gist.github.com/983535
  17. *
  18. * @example
  19. * rgb2hex([255, 255, 255]) // '#ffffff'
  20. *
  21. * @author Jed Schmidt
  22. *
  23. * @type {(rgb: Array<number>) => string}
  24. */
  25. const convertRgbToHex = ([r, g, b]) => {
  26. // combine the octets into a 32-bit integer as: [1][r][g][b]
  27. const hexNumber =
  28. // operator precedence is (+) > (<<) > (|)
  29. ((((256 + // [1][0]
  30. r) << // [1][r]
  31. 8) | // [1][r][0]
  32. g) << // [1][r][g]
  33. 8) | // [1][r][g][0]
  34. b;
  35. // serialize [1][r][g][b] to a hex string, and
  36. // remove the 1 to get the number with 0s intact
  37. return '#' + hexNumber.toString(16).slice(1).toUpperCase();
  38. };
  39. /**
  40. * Convert different colors formats in element attributes to hex.
  41. *
  42. * @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor
  43. * @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
  44. *
  45. * @example
  46. * Convert color name keyword to long hex:
  47. * fuchsia ➡ #ff00ff
  48. *
  49. * Convert rgb() to long hex:
  50. * rgb(255, 0, 255) ➡ #ff00ff
  51. * rgb(50%, 100, 100%) ➡ #7f64ff
  52. *
  53. * Convert long hex to short hex:
  54. * #aabbcc ➡ #abc
  55. *
  56. * Convert hex to short name
  57. * #000080 ➡ navy
  58. *
  59. * @author Kir Belevich
  60. *
  61. * @type {import('../lib/types').Plugin<{
  62. * currentColor?: boolean | string | RegExp,
  63. * names2hex?: boolean,
  64. * rgb2hex?: boolean,
  65. * shorthex?: boolean,
  66. * shortname?: boolean,
  67. * }>}
  68. */
  69. exports.fn = (_root, params) => {
  70. const {
  71. currentColor = false,
  72. names2hex = true,
  73. rgb2hex = true,
  74. shorthex = true,
  75. shortname = true,
  76. } = params;
  77. return {
  78. element: {
  79. enter: (node) => {
  80. for (const [name, value] of Object.entries(node.attributes)) {
  81. if (collections.colorsProps.includes(name)) {
  82. let val = value;
  83. // convert colors to currentColor
  84. if (currentColor) {
  85. let matched;
  86. if (typeof currentColor === 'string') {
  87. matched = val === currentColor;
  88. } else if (currentColor instanceof RegExp) {
  89. matched = currentColor.exec(val) != null;
  90. } else {
  91. matched = val !== 'none';
  92. }
  93. if (matched) {
  94. val = 'currentColor';
  95. }
  96. }
  97. // convert color name keyword to long hex
  98. if (names2hex) {
  99. const colorName = val.toLowerCase();
  100. if (collections.colorsNames[colorName] != null) {
  101. val = collections.colorsNames[colorName];
  102. }
  103. }
  104. // convert rgb() to long hex
  105. if (rgb2hex) {
  106. let match = val.match(regRGB);
  107. if (match != null) {
  108. let nums = match.slice(1, 4).map((m) => {
  109. let n;
  110. if (m.indexOf('%') > -1) {
  111. n = Math.round(parseFloat(m) * 2.55);
  112. } else {
  113. n = Number(m);
  114. }
  115. return Math.max(0, Math.min(n, 255));
  116. });
  117. val = convertRgbToHex(nums);
  118. }
  119. }
  120. // convert long hex to short hex
  121. if (shorthex) {
  122. let match = val.match(regHEX);
  123. if (match != null) {
  124. val = '#' + match[0][1] + match[0][3] + match[0][5];
  125. }
  126. }
  127. // convert hex to short name
  128. if (shortname) {
  129. const colorName = val.toLowerCase();
  130. if (collections.colorsShortNames[colorName] != null) {
  131. val = collections.colorsShortNames[colorName];
  132. }
  133. }
  134. node.attributes[name] = val;
  135. }
  136. }
  137. },
  138. },
  139. };
  140. };