pangrid 0.3.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a5a1bfbede1e22eda584f80088055a2da23e973a
4
- data.tar.gz: 287b4baf8fe41be20437f6c16ad9fd4eca3dbf64
2
+ SHA256:
3
+ metadata.gz: 0ec0d0d4bf728c0021f9db679a2c0b3208b7a5971888ef4de9490e8dc07037b1
4
+ data.tar.gz: eb305d1960b5876c0d31d562a0734cd61f9c7683f0bed5cf5af2c3fb2f8b2f99
5
5
  SHA512:
6
- metadata.gz: 987f63c04f51c1575d2d47a82f9fdcafd2645656e5a6facc5ea77e8e02fff8216ac2d591c52e23909e00a5c149a67bb80937c936b2b7b8d8328a34f3d4751446
7
- data.tar.gz: 721b3f3ea706c965ca1a2fbd04d098a55c88aa97478dc2773c7faf03c46bc4461454000577117c7d3d53b8862ebc6ea9f706800be176731e7c33a0c9f4a1402c
6
+ metadata.gz: 2a8cc63d397ce5047e9e85b0665f7436af2aff9f59a8dec8d8b9f6ed9abb83b333d0f8f348fc3d460c908dcb6bc6b11338cba2ed7ad8d7af8bb29680dc0ad901
7
+ data.tar.gz: d9b5dec6380b0bcd6429e7a61f085330c329f70e75518a7c4471e4c5abab2c7d570c3696ef4034028627b8bb329d3b0aa4a861257d11adeedcaa8d3594a76b0c
@@ -26,11 +26,30 @@ class Servlet < WEBrick::HTTPServlet::AbstractServlet
26
26
  rescue Exception => e
27
27
  out = e.inspect
28
28
  end
29
-
30
- template = IO.read(TEMPLATE)
31
- response.status = 200
32
- response.content_type = "text/html"
33
- response.body = template % out
29
+
30
+ response.header['Access-Control-Allow-Origin'] = '*'
31
+
32
+ case request.path
33
+ when "/"
34
+ template = IO.read(TEMPLATE)
35
+ response.status = 200
36
+ response.content_type = "text/html"
37
+ response.body = template % out
38
+ when "/blob"
39
+ response.status = 200
40
+ response.content_type = "application/octet-stream"
41
+ response.body = out
42
+ when "/json"
43
+ if request.query["to"] == "json"
44
+ response.status = 200
45
+ response.content_type = "text/json"
46
+ response.body = out
47
+ else
48
+ response.status = 200
49
+ response.content_type = "text/json"
50
+ response.body = '{ "error" : "non-json format requested" }'
51
+ end
52
+ end
34
53
  end
35
54
  end
36
55
 
@@ -35,6 +35,8 @@ class Plugin
35
35
  FAILED = []
36
36
  MISSING_DEPS = {}
37
37
 
38
+ DESCRIPTION = ""
39
+
38
40
  def self.inherited(subclass)
39
41
  name = class_to_name(subclass.name)
40
42
  #puts "Registered #{subclass} as #{name}"
@@ -62,13 +64,13 @@ class Plugin
62
64
 
63
65
  def self.list_all
64
66
  puts "-------------------------------------------------------"
65
- puts "Available plugins:"
67
+ puts "Available plugins (F = from, T = to):"
66
68
  puts "-------------------------------------------------------"
67
69
  REGISTRY.keys.sort.each do |name|
68
70
  plugin = REGISTRY[name]
69
71
  provides = [:read, :write].select {|m| plugin.method_defined? m}
70
- provides = provides.map {|m| {read: 'from', write: 'to'}[m]}
71
- puts " " + name + " [" + provides.join(", ") + "]"
72
+ provides = provides.map {|m| {read: 'F', write: 'T'}[m]}.join
73
+ puts " #{name} [#{provides}]".ljust(30) + plugin.const_get(:DESCRIPTION)
72
74
  end
73
75
  if !MISSING_DEPS.empty?
74
76
  puts
@@ -75,6 +75,8 @@ class AcrossLiteBinary < Plugin
75
75
  # crossword, checksums
76
76
  attr_accessor :xw, :cs
77
77
 
78
+ DESCRIPTION = "AcrossLite binary format (.puz)"
79
+
78
80
  HEADER_FORMAT = "v A12 v V2 A4 v2 A12 c2 v3"
79
81
  HEADER_CHECKSUM_FORMAT = "c2 v3"
80
82
  EXT_HEADER_FORMAT = "A4 v2"
@@ -359,6 +361,8 @@ class AcrossLiteText < Plugin
359
361
 
360
362
  attr_accessor :xw, :rebus
361
363
 
364
+ DESCRIPTION = "AcrossLite text format"
365
+
362
366
  def initialize
363
367
  @xw = XWord.new
364
368
  end
@@ -26,6 +26,9 @@ require 'csv'
26
26
  module Pangrid
27
27
 
28
28
  class CSV < Plugin
29
+
30
+ DESCRIPTION = "CSV reader (see source comments for format)"
31
+
29
32
  def read(data)
30
33
  s = ::CSV.parse(data)
31
34
  s.reject! {|row| row.compact.empty?}
@@ -37,7 +40,7 @@ class CSV < Plugin
37
40
  h = s.shift.map {|c| c.split(/:\s*/)}
38
41
  header = OpenStruct.new
39
42
  h.each do |k, v|
40
- header[k.downcase] = v
43
+ header[k.downcase.strip] = v
41
44
  end
42
45
 
43
46
  header.clues = header.clues.to_i
@@ -11,6 +11,9 @@ module Pangrid
11
11
  require_for_plugin 'excel', ['axlsx']
12
12
 
13
13
  class ExcelXSLX < Plugin
14
+
15
+ DESCRIPTION = "Excel writer. Useful to upload to google sheets."
16
+
14
17
  # styles
15
18
  STYLES = {
16
19
  :black => {:bg_color => "00"},
@@ -0,0 +1,128 @@
1
+ # Exolve (https://github.com/viresh-ratnakar/exolve) is a browser-based
2
+ # crossword solver that allows you to embed the crossword and solving software
3
+ # in a single HTML file.
4
+
5
+ module Pangrid
6
+
7
+ module ExolveWriter
8
+ def write(xw)
9
+ headers = format_headers(xw)
10
+ across, down = xw.number
11
+ grid = format_grid(xw)
12
+ ac = format_clues(across, xw.across_clues)
13
+ dn = format_clues(down, xw.down_clues)
14
+ across = ["exolve-across:"] + indent(ac)
15
+ down = ["exolve-down:"] + indent(dn)
16
+ grid = ["exolve-grid:"] + indent(grid)
17
+ body = headers + grid + across + down
18
+ out = ["exolve-begin"] + indent(body) + ["exolve-end"]
19
+ out.join("\n")
20
+ end
21
+
22
+ def format_headers(xw)
23
+ headers = [
24
+ 'id', 'replace-with-unique-id',
25
+ 'title', xw.title,
26
+ 'setter', xw.author,
27
+ 'width', xw.width,
28
+ 'height', xw.height,
29
+ 'copyright', xw.copyright,
30
+ 'prelude', xw.preamble
31
+ ]
32
+ headers.each_slice(2).select {|k, v| v}.map {|k, v| "exolve-#{k}: #{v}"}
33
+ end
34
+
35
+ def format_clues(numbers, clues)
36
+ numbers.zip(clues).map {|n, c| "#{n.to_s.rjust(2)} #{c}"}
37
+ end
38
+
39
+ def indent(lines)
40
+ lines.map {|x| " " + x}
41
+ end
42
+ end
43
+
44
+ module ExolveReader
45
+ def read(data)
46
+ s = data.each_line.map(&:rstrip)
47
+ first = s.index('exolve-begin')
48
+ check("exolve-begin missing") { first }
49
+ last = s.index('exolve-end')
50
+ check("exolve-end missing") { last }
51
+ lines = s[(first + 1)...last]
52
+ xw = XWord.new
53
+ s = sections(lines)
54
+ s.each do |_, field, data|
55
+ if %w(title setter copyright prelude).include? field
56
+ xw[field] = data
57
+ elsif %w(height width).include? field
58
+ xw[field] = data.to_i
59
+ elsif %(across down).include? field
60
+ xw["#{field}_clues"] = data
61
+ elsif field == "grid"
62
+ xw.solution = parse_grid(data)
63
+ end
64
+ end
65
+ xw
66
+ end
67
+
68
+ def sections(lines)
69
+ headers = lines.each.with_index.map do |l, i|
70
+ m = l.match(/^(\s+)exolve-(\w+):(.*)$/)
71
+ if m
72
+ _, indent, field, data = m.to_a
73
+ [i, field, data.strip]
74
+ else
75
+ nil
76
+ end
77
+ end
78
+ headers.compact!
79
+ headers.push([lines.length, "", ""])
80
+ headers.each_cons(2) do |i, j|
81
+ if i[2].empty?
82
+ i[2] = lines[(i[0] + 1)...j[0]].map(&:strip)
83
+ end
84
+ end
85
+ headers.pop
86
+ headers
87
+ end
88
+
89
+ def parse_grid_char(char)
90
+ case char
91
+ when '0'; :null
92
+ when '.'; :black
93
+ else; char
94
+ end
95
+ end
96
+
97
+ def parse_grid(lines)
98
+ grid = lines.map(&:strip).map {|x| x.split(//)}
99
+ grid.map do |col|
100
+ col.map do |c|
101
+ Cell.new(:solution => parse_grid_char(c))
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ class ExolveFilled < Plugin
108
+ include ExolveReader
109
+ include ExolveWriter
110
+
111
+ DESCRIPTION = "Exolve writer with solutions"
112
+
113
+ def format_grid(xw)
114
+ xw.to_array(:black => '.', :null => '0').map(&:join)
115
+ end
116
+ end
117
+
118
+ class ExolveBlank < Plugin
119
+ include ExolveWriter
120
+
121
+ DESCRIPTION = "Exolve writer without solutions"
122
+
123
+ def format_grid(xw)
124
+ xw.to_array(:black => '.', :null => '0') {|c| 0}.map(&:join)
125
+ end
126
+ end
127
+
128
+ end # module Pangrid
@@ -0,0 +1,78 @@
1
+ # Json
2
+ #
3
+ # cell = { x : int, y : int, contents : string }
4
+ #
5
+ # xword = { rows : int,
6
+ # cols : int,
7
+ # cells : [cell],
8
+ # across : [string]
9
+ # down : [string]
10
+ # }
11
+
12
+ require 'json'
13
+
14
+ module Pangrid
15
+
16
+ class Json < Plugin
17
+
18
+ DESCRIPTION = "Simple JSON format"
19
+
20
+ def write(xw)
21
+ cells = []
22
+ (0 ... xw.height).each do |y|
23
+ (0 ... xw.width).each do |x|
24
+ cell = xw.solution[y][x]
25
+ s = case cell.solution
26
+ when :black; '#'
27
+ when :null; ''
28
+ when Rebus; cell.solution.inspect
29
+ else; cell.solution
30
+ end
31
+
32
+ cells.push({ x: x, y: y, contents: s })
33
+ end
34
+ end
35
+
36
+ h = {
37
+ rows: xw.height,
38
+ cols: xw.width,
39
+ cells: cells,
40
+ across: xw.across_clues,
41
+ down: xw.down_clues
42
+ }
43
+
44
+ ::JSON.generate(h)
45
+ end
46
+
47
+ def read(data)
48
+ json = ::JSON.parse(data)
49
+ xw = XWord.new
50
+
51
+ xw.height = json['rows']
52
+ xw.width = json['cols']
53
+ xw.solution = Array.new(xw.height) { Array.new(xw.width) }
54
+ json['cells'].each do |c|
55
+ cell = Cell.new
56
+ s = c['contents']
57
+ cell.solution =
58
+ case s
59
+ when ""; :null
60
+ when "#"; :black
61
+ else
62
+ if s.length == 1
63
+ s
64
+ else
65
+ Rebus.new s
66
+ end
67
+ end
68
+ x, y = c['x'], c['y']
69
+ xw.solution[y][x] = cell
70
+ end
71
+ xw.across_clues = json['across']
72
+ xw.down_clues = json['down']
73
+ xw
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,25 @@
1
+ require 'chunky_png'
2
+
3
+ module Pangrid
4
+
5
+ class PNGThumbnail < Plugin
6
+
7
+ def write(xw)
8
+ black = ChunkyPNG::Color::BLACK
9
+ white = ChunkyPNG::Color::WHITE
10
+ red = ChunkyPNG::Color::rgb(255, 0, 0)
11
+ grey = ChunkyPNG::Color::rgb(128, 128, 128)
12
+ scale = 4
13
+ png = ChunkyPNG::Image.new(64, 64, ChunkyPNG::Color::TRANSPARENT)
14
+ xw.each_cell_with_coords do |x, y, cell|
15
+ c = cell.black? ? black : white
16
+ png.rect(
17
+ x * scale, y * scale, (x + 1) * scale, (y + 1) * scale,
18
+ stroke_color = grey, fill_color = c)
19
+ end
20
+ png.rect(0, 0, xw.width * scale, xw.height * scale, stroke_color = red)
21
+ png.to_blob
22
+ end
23
+ end
24
+
25
+ end # module Pangrid
@@ -5,6 +5,9 @@ module Pangrid
5
5
  QXW_GRID_ERROR = "Could not read grid from .qxw file"
6
6
 
7
7
  class Qxw < Plugin
8
+
9
+ DESCRIPTION = "QXW grid reader (rectangular grids only)"
10
+
8
11
  def read(data)
9
12
  xw = XWord.new
10
13
  lines = data.lines.map(&:chomp)
@@ -34,6 +34,8 @@ end
34
34
  class RedditFilled < Plugin
35
35
  include RedditWriter
36
36
 
37
+ DESCRIPTION = "Reddit format (with filled squares)"
38
+
37
39
  def write(xw)
38
40
  write_xw(xw)
39
41
  end
@@ -48,6 +50,8 @@ end
48
50
  class RedditBlank < Plugin
49
51
  include RedditWriter
50
52
 
53
+ DESCRIPTION = "Reddit format (with blank squares)"
54
+
51
55
  def write(xw)
52
56
  write_xw(xw)
53
57
  end
@@ -88,6 +92,7 @@ class RedditBlank < Plugin
88
92
  #
89
93
  # we have to be careful about leading/trailing |s
90
94
  ix = lines.find_index {|row| row =~ /^[|\s-]+$/}
95
+ check("Could not find grid") { not ix.nil? }
91
96
  width = lines[ix].gsub(/\s/, '').split('|').reject(&:empty?).length
92
97
  lines = [lines[(ix - 1)]] + lines[(ix + 1) .. -1]
93
98
  grid = lines.take_while {|i| is_grid_row(i)}
@@ -9,6 +9,9 @@
9
9
  module Pangrid
10
10
 
11
11
  class Text < Plugin
12
+
13
+ DESCRIPTION = "Basic text format (mostly for debugging)"
14
+
12
15
  def write(xw)
13
16
  across, down = xw.number
14
17
  rows = xw.to_array(:black => '#', :null => '.')
@@ -1,3 +1,3 @@
1
1
  module Pangrid
2
- VERSION='0.3.1'
2
+ VERSION='0.5.1'
3
3
  end
data/lib/pangrid/xw.rb CHANGED
@@ -139,6 +139,14 @@ class XWord < OpenStruct
139
139
  end
140
140
  end
141
141
 
142
+ def each_cell_with_coords
143
+ (0 ... height).each do |y|
144
+ (0 ... width).each do |x|
145
+ yield [x, y, solution[y][x]]
146
+ end
147
+ end
148
+ end
149
+
142
150
  # {:black => char, :null => char} -> Any[][]
143
151
  def to_array(opts = {})
144
152
  opts = {:black => '#', :null => ' '}.merge(opts)
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pangrid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin DeMello
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-19 00:00:00.000000000 Z
12
- dependencies: []
13
- description:
11
+ date: 2022-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: webrick
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.0
27
+ description:
14
28
  email: martindemello@gmail.com
15
29
  executables:
16
30
  - pangrid
@@ -29,6 +43,9 @@ files:
29
43
  - lib/pangrid/plugins/acrosslite.rb
30
44
  - lib/pangrid/plugins/csv.rb
31
45
  - lib/pangrid/plugins/excel.rb
46
+ - lib/pangrid/plugins/exolve.rb
47
+ - lib/pangrid/plugins/json.rb
48
+ - lib/pangrid/plugins/png.rb
32
49
  - lib/pangrid/plugins/qxw.rb
33
50
  - lib/pangrid/plugins/reddit.rb
34
51
  - lib/pangrid/plugins/text.rb
@@ -39,7 +56,7 @@ homepage: https://github.com/martindemello/pangrid
39
56
  licenses:
40
57
  - MIT
41
58
  metadata: {}
42
- post_install_message:
59
+ post_install_message:
43
60
  rdoc_options:
44
61
  - "--main"
45
62
  - README
@@ -56,9 +73,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
73
  - !ruby/object:Gem::Version
57
74
  version: '0'
58
75
  requirements: []
59
- rubyforge_project:
60
- rubygems_version: 2.5.1
61
- signing_key:
76
+ rubygems_version: 3.2.22
77
+ signing_key:
62
78
  specification_version: 4
63
79
  summary: A crossword file format converter
64
80
  test_files: []