j_signature_base30.rb 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. ### @license
  2. # jSignature v2 SVG export plugin.
  3. # Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
  4. # MIT License <http://www.opensource.org/licenses/mit-license.php
  5. #
  6. # Ruby convertion by AlexVangelov
  7. class JSignatureBase30
  8. PLUS = 'Y'.ord
  9. MINUS = 'Z'.ord
  10. SEPARATOR = '_'
  11. BITNESS = 30
  12. PADDING = 1
  13. def initialize(datastring)
  14. @charmap, @charmap_reverse = [], []
  15. @allchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX'.bytes.to_a
  16. (BITNESS-1).downto(0) do |i|
  17. @charmap[@allchars[i]] = @allchars[i+BITNESS]
  18. @charmap_reverse[@allchars[i+BITNESS]] = @allchars[i]
  19. end
  20. @base30 = datastring
  21. end
  22. def to_native
  23. b64_to_native(@base30)
  24. end
  25. def to_svg
  26. native = b64_to_native(@base30)
  27. "".tap() do |svg|
  28. svg << '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
  29. svg << '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
  30. shiftx, shifty = nil, nil
  31. sizex,sizey = 0, 0
  32. xlimits, ylimits = [], []
  33. if native.size > 0
  34. native.each do |stroke|
  35. xlimits |= stroke[:x]
  36. ylimits |= stroke[:y]
  37. end
  38. minx = xlimits.min - PADDING
  39. maxx = xlimits.max + PADDING
  40. miny = ylimits.min - PADDING
  41. maxy = ylimits.max + PADDING
  42. shiftx = minx < 0 ? 0 : minx
  43. shifty = miny < 0 ? 0 : miny
  44. sizex = maxx - minx
  45. sizey = maxy - miny
  46. end
  47. svg << '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'
  48. svg << sizex.to_s
  49. svg << '" height="'
  50. svg << sizey.to_s
  51. svg << '">'
  52. native.each do |stroke|
  53. svg << '<path fill="none" stroke="#000000" stroke-width="2"'
  54. svg << ' stroke-linecap="round" stroke-linejoin="round" d="'
  55. svg << add_stroke(stroke, shiftx, shifty)
  56. svg << '"/>'
  57. end
  58. svg << '</svg>'
  59. end
  60. end
  61. private
  62. def add_stroke(stroke, shiftx, shifty)
  63. lastx = stroke[:x][0]
  64. lasty = stroke[:y][0]
  65. [].tap do |answer|
  66. answer << 'M'
  67. answer << (lastx - shiftx)
  68. answer << (lasty - shifty)
  69. answer << 'l'
  70. l = stroke[:x].size
  71. if l == 1
  72. answer << 1
  73. answer << -1
  74. else
  75. (1...l).each do |i|
  76. answer << stroke[:x][i] - lastx
  77. answer << stroke[:y][i] - lasty
  78. lastx = stroke[:x][i]
  79. lasty = stroke[:y][i]
  80. end
  81. end
  82. end.each(&:to_s).join(" ")
  83. end
  84. def b64_to_native(datastring)
  85. chunks = datastring.split('_')
  86. l = chunks.size / 2
  87. [].tap() do |data|
  88. l.times do |i|
  89. data << {
  90. x: uncompress_stroke_leg(chunks[i*2]),
  91. y: uncompress_stroke_leg(chunks[i*2+1])
  92. }
  93. end
  94. end
  95. end
  96. def uncompress_stroke_leg(datastring)
  97. chars = datastring.bytes
  98. polarity = 1
  99. partial = []
  100. preprewhole = 0
  101. prewhole = 0
  102. [].tap() do |answer|
  103. chars.each do |ch|
  104. if @charmap[ch] || ch == MINUS || ch == PLUS
  105. if partial.any?
  106. prewhole = partial.collect(&:chr).join.to_i(BITNESS) * polarity + preprewhole
  107. answer << prewhole
  108. preprewhole = prewhole
  109. end
  110. if ch == MINUS
  111. polarity = -1
  112. partial = []
  113. elsif ch == PLUS
  114. polarity = 1
  115. partial = []
  116. else
  117. partial = [ch]
  118. end
  119. else
  120. partial << @charmap_reverse[ch]
  121. end
  122. end
  123. answer << partial.collect(&:chr).join.to_i(BITNESS) * polarity + preprewhole
  124. end
  125. end
  126. end