visual-qrcode 0.1.1 → 0.1.2
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 +4 -4
 - data/.rubocop.yml +3 -0
 - data/Guardfile +33 -0
 - data/README.md +57 -8
 - data/lib/visual_qrcode/export.rb +41 -0
 - data/lib/visual_qrcode/module_filler.rb +72 -0
 - data/lib/visual_qrcode/pixel_tools.rb +26 -0
 - data/lib/visual_qrcode/pixels_handler.rb +62 -0
 - data/lib/visual_qrcode/qrcode.rb +83 -0
 - data/lib/visual_qrcode/version.rb +1 -1
 - data/lib/visual_qrcode.rb +1 -1
 - metadata +37 -4
 - data/lib/visual_qrcode/test.rb +0 -11
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: f61f0ec6cbd27dd795252d141fb7dd3b6b9cfb98efe35e73d19176505d3e1c0e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5aeb4d2931b1b574d2824dbcfe8d0c03480e30f3395dcc39ec5b262fa61b27ac
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 5f3588aa015afac87a8ef7375b25c033d8a3a39071941ed9e4ffe6dd35ba3e7039c8c05ec1fca29c5b7fc43348a39ab7d209273aff9c9a92f949f809a6d4d6fe
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 1af2b54970af438dae3c551cf7e0f5126723dff1873f0ea0ca53e0f298e1b2c6948ad93ec7cb832801ad783bf209a61a503987dd12bd01d6ec2820e3717c8c71
         
     | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/Guardfile
    ADDED
    
    | 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # A sample Guardfile
         
     | 
| 
      
 4 
     | 
    
         
            +
            # More info at https://github.com/guard/guard#readme
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            ## Uncomment and set this to only include directories you want to watch
         
     | 
| 
      
 7 
     | 
    
         
            +
            # directories %w(lib spec) \
         
     | 
| 
      
 8 
     | 
    
         
            +
            #  .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ## Note: if you are using the `directories` clause above and you are not
         
     | 
| 
      
 11 
     | 
    
         
            +
            ## watching the project directory ('.'), then you will want to move
         
     | 
| 
      
 12 
     | 
    
         
            +
            ## the Guardfile to a watched dir and symlink it back, e.g.
         
     | 
| 
      
 13 
     | 
    
         
            +
            #
         
     | 
| 
      
 14 
     | 
    
         
            +
            #  $ mkdir config
         
     | 
| 
      
 15 
     | 
    
         
            +
            #  $ mv Guardfile config/
         
     | 
| 
      
 16 
     | 
    
         
            +
            #  $ ln -s config/Guardfile .
         
     | 
| 
      
 17 
     | 
    
         
            +
            #
         
     | 
| 
      
 18 
     | 
    
         
            +
            # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            # NOTE: The cmd option is now required due to the increasing number of ways
         
     | 
| 
      
 21 
     | 
    
         
            +
            #       rspec may be run, below are examples of the most common uses.
         
     | 
| 
      
 22 
     | 
    
         
            +
            #  * bundler: 'bundle exec rspec'
         
     | 
| 
      
 23 
     | 
    
         
            +
            #  * bundler binstubs: 'bin/rspec'
         
     | 
| 
      
 24 
     | 
    
         
            +
            #  * spring: 'bin/rspec' (This will use spring if running and you have
         
     | 
| 
      
 25 
     | 
    
         
            +
            #                          installed the spring binstubs per the docs)
         
     | 
| 
      
 26 
     | 
    
         
            +
            #  * zeus: 'zeus rspec' (requires the server to be started separately)
         
     | 
| 
      
 27 
     | 
    
         
            +
            #  * 'just' rspec: 'rspec'
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            guard :rspec, cmd: "bundle exec rspec" do
         
     | 
| 
      
 30 
     | 
    
         
            +
              watch(%r{^spec/.+_spec\.rb$})
         
     | 
| 
      
 31 
     | 
    
         
            +
              watch(%r{^lib/(.+)\.rb$})     { |m| "spec/#{m[1]}_spec.rb" }
         
     | 
| 
      
 32 
     | 
    
         
            +
              watch("spec/spec_helper.rb")  { "spec" }
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,24 +1,67 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Visual::Qrcode
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            `Visual::Qrcode` gives you various tools to generate working QRCodes with images in their backgrounds, based on [this research](https://cgv.cs.nthu.edu.tw/Projects/Recreational_Graphics/Halftone_QRCodes/).
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            Example of the marianne Visual QRCode generated by the tests :
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
            
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## Installation
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            Install the gem and add to the application's Gemfile by executing:
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                $ bundle add  
     | 
| 
      
 13 
     | 
    
         
            +
                $ bundle add visual-qrcode
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
            If bundler is not being used to manage dependencies, install the gem by executing:
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                $ gem install  
     | 
| 
      
 17 
     | 
    
         
            +
                $ gem install visual-qrcode
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            ## Dependencies
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            `visual-qrcode` depends on two gems :
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            - [rqrcode_core](https://github.com/whomwah/rqrcode_core)
         
     | 
| 
      
 25 
     | 
    
         
            +
            - [mini_magick](https://github.com/minimagick/minimagick)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            > Make sur that you have the `ImageMagick` CLI installed on your computer to use `mini_magick`. See its [requirements](https://github.com/minimagick/minimagick?tab=readme-ov-file#requirements)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       18 
29 
     | 
    
         | 
| 
       19 
30 
     | 
    
         
             
            ## Usage
         
     | 
| 
       20 
31 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
            The basic usage requires a string for the QRCode content and an `image_path` (or `image_url`).
         
     | 
| 
      
 33 
     | 
    
         
            +
            **Default size** : 3x the basic QRCode size generated with a high level of error correction.
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 36 
     | 
    
         
            +
            visual_qr_code = VisualQrcode::Qrcode.new("bonjour", "spec/images/marianne.png")
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            # Returns a MiniMagick::Image
         
     | 
| 
      
 39 
     | 
    
         
            +
            image = visual_qr_code.as_png
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            # MiniMagick::Image has a write method to create an image file
         
     | 
| 
      
 42 
     | 
    
         
            +
            image.write("./marianne_visual_qrcode.png")
         
     | 
| 
      
 43 
     | 
    
         
            +
            ```
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            You can also add a size parameter, in pixels. This size can't be smaller than the **default  size**.
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 48 
     | 
    
         
            +
            visual_qr_code = VisualQrcode::Qrcode.new("bonjour by 280", "spec/images/marianne.png", size: 280)
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            visual_qrcode.as_png.write("./marianne_visual_qrcode_280x280.png")
         
     | 
| 
      
 51 
     | 
    
         
            +
            ```
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            ## Design choices
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            ### Padding
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            In order to have a nice visual, a padding is added on the image to keep it inside of the QRCode guide patterns. Also it helps to reckognize that the image _is_ a scannable QRCode and not just some random image.
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            ### Resize method
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            The Visual QRCode will be generated at a mutiple of the **default size**, and then reduced to the expected size to maintain a good background image quality.
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            > For example, if the Default size is 140px, and you want a 230px image, it will generate a 280px Visual QRCode and then reduce it to 230px.
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
       22 
65 
     | 
    
         | 
| 
       23 
66 
     | 
    
         
             
            ## Development
         
     | 
| 
       24 
67 
     | 
    
         | 
| 
         @@ -26,9 +69,15 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run 
     | 
|
| 
       26 
69 
     | 
    
         | 
| 
       27 
70 
     | 
    
         
             
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         
     | 
| 
       28 
71 
     | 
    
         | 
| 
      
 72 
     | 
    
         
            +
            ### Testing
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            Run `bundle exec rake` to run tests and lint. This is what runs in the CI.
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
            You can also use `bundle exec guard` to run gard and listen to modified files to run the test
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
       29 
78 
     | 
    
         
             
            ## Contributing
         
     | 
| 
       30 
79 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
            Bug reports and pull requests are welcome on GitHub at https://github.com/ 
     | 
| 
      
 80 
     | 
    
         
            +
            Bug reports and pull requests are welcome on GitHub at https://github.com/JeSuisUnCaillou/visual-qrcode/issues.
         
     | 
| 
       32 
81 
     | 
    
         | 
| 
       33 
82 
     | 
    
         
             
            ## License
         
     | 
| 
       34 
83 
     | 
    
         | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "mini_magick"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "pixel_tools"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "pixels_handler"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module VisualQrcode
         
     | 
| 
      
 8 
     | 
    
         
            +
              class Export
         
     | 
| 
      
 9 
     | 
    
         
            +
                include PixelTools
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(pixels)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @pixels_handler = PixelsHandler.new(pixels: pixels)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def as_png(size: nil, margin: 6)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @pixels_handler.add_margin(margin, color: :white)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  image = MiniMagick::Image.get_image_from_pixels(pixels, dimensions, "RGBA", PIXEL_DEPTH, "png")
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  image.resize "#{size}x#{size}" if size
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  image
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def as_text(dark: "x", light: " ")
         
     | 
| 
      
 25 
     | 
    
         
            +
                  pixels.map do |row|
         
     | 
| 
      
 26 
     | 
    
         
            +
                    row.map do |pixel|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      is_light = pixel.nil? || pixel.sum < max_depth * 3
         
     | 
| 
      
 28 
     | 
    
         
            +
                      is_light ? light : dark
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end.join
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end.join("\n")
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def pixels
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @pixels_handler.pixels
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def dimensions
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @pixels_handler.dimensions
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,72 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ModuleFiller
         
     | 
| 
      
 4 
     | 
    
         
            +
              PIXELS_PER_MODULE = 3
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def fill_vqr_module(x_index, y_index)
         
     | 
| 
      
 7 
     | 
    
         
            +
                if @common_patterns[x_index][y_index].nil?
         
     | 
| 
      
 8 
     | 
    
         
            +
                  fill_vqr_module_with_image(x_index, y_index)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  fill_vqr_module_with_basic_qrcode(x_index, y_index)
         
     | 
| 
      
 10 
     | 
    
         
            +
                else
         
     | 
| 
      
 11 
     | 
    
         
            +
                  fill_vqr_module_with_pattern(x_index, y_index)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              protected
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def fill_vqr_module_with_basic_qrcode(x_index, y_index)
         
     | 
| 
      
 18 
     | 
    
         
            +
                multiplied_range_each(x_index, y_index) do |new_x, new_y, x_offset, y_offset|
         
     | 
| 
      
 19 
     | 
    
         
            +
                  if central_pixel?(x_offset, y_offset) || @image_handler.pixels[new_x][new_y][3].zero?
         
     | 
| 
      
 20 
     | 
    
         
            +
                    value = @basic_qrcode.modules[x_index][y_index]
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @vqr_pixels[new_x][new_y] = pixel_of(value)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def fill_vqr_module_with_image(x_index, y_index)
         
     | 
| 
      
 27 
     | 
    
         
            +
                multiplied_range_each(x_index, y_index) do |new_x, new_y, x_offset, y_offset|
         
     | 
| 
      
 28 
     | 
    
         
            +
                  next if central_pixel?(x_offset, y_offset)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  pixel = @image_handler.pixels[new_x][new_y]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @vqr_pixels[new_x][new_y] = pixel
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def fill_vqr_module_with_pattern(x_index, y_index)
         
     | 
| 
      
 36 
     | 
    
         
            +
                multiplied_range_each(x_index, y_index) do |new_x, new_y|
         
     | 
| 
      
 37 
     | 
    
         
            +
                  value = @common_patterns[x_index][y_index]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @vqr_pixels[new_x][new_y] = pixel_of(value)
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def multiplied_range_each(x_index, y_index, &block)
         
     | 
| 
      
 43 
     | 
    
         
            +
                module_range.each do |x_offset|
         
     | 
| 
      
 44 
     | 
    
         
            +
                  module_range.each do |y_offset|
         
     | 
| 
      
 45 
     | 
    
         
            +
                    new_x = (PIXELS_PER_MODULE * x_index * size_multiplier) + x_offset
         
     | 
| 
      
 46 
     | 
    
         
            +
                    new_y = (PIXELS_PER_MODULE * y_index * size_multiplier) + y_offset
         
     | 
| 
      
 47 
     | 
    
         
            +
                    block.call(new_x, new_y, x_offset, y_offset)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              def module_size
         
     | 
| 
      
 53 
     | 
    
         
            +
                @module_size ||= PIXELS_PER_MODULE * size_multiplier
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              # [0, 1, 2] : all pixels of a module
         
     | 
| 
      
 57 
     | 
    
         
            +
              def module_range
         
     | 
| 
      
 58 
     | 
    
         
            +
                @module_range ||= 0..(module_size - 1)
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              # 1 : the central pixels of a module
         
     | 
| 
      
 62 
     | 
    
         
            +
              def central_range
         
     | 
| 
      
 63 
     | 
    
         
            +
                @central_range ||= begin
         
     | 
| 
      
 64 
     | 
    
         
            +
                  third_of_range = module_range.max / 3
         
     | 
| 
      
 65 
     | 
    
         
            +
                  (third_of_range + 1)..((third_of_range * 2) + 1)
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              def central_pixel?(x_offset, y_offset)
         
     | 
| 
      
 70 
     | 
    
         
            +
                central_range.include?(x_offset) && central_range.include?(y_offset)
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module PixelTools
         
     | 
| 
      
 4 
     | 
    
         
            +
              PIXEL_DEPTH = 8
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def max_depth
         
     | 
| 
      
 7 
     | 
    
         
            +
                @max_depth ||= (2**PIXEL_DEPTH) - 1
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def pixel_of(value)
         
     | 
| 
      
 11 
     | 
    
         
            +
                pixel_value = value ? 0 : max_depth
         
     | 
| 
      
 12 
     | 
    
         
            +
                [pixel_value, pixel_value, pixel_value, max_depth]
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def transparent_pixel
         
     | 
| 
      
 16 
     | 
    
         
            +
                @transparent_pixel ||= [0] * 4
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def white_pixel
         
     | 
| 
      
 20 
     | 
    
         
            +
                @white_pixel ||= [255] * 4
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def pixel_of_color(color)
         
     | 
| 
      
 24 
     | 
    
         
            +
                send(:"#{color}_pixel")
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "mini_magick"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "pixel_tools"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module VisualQrcode
         
     | 
| 
      
 7 
     | 
    
         
            +
              class PixelsHandler
         
     | 
| 
      
 8 
     | 
    
         
            +
                include PixelTools
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                attr_accessor :pixels
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(pixels: nil, image_path: nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if pixels
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @pixels = pixels
         
     | 
| 
      
 15 
     | 
    
         
            +
                  else
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @image = MiniMagick::Image.open(image_path)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    set_pixels_from_image
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def size
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @pixels.length
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def dimensions
         
     | 
| 
      
 26 
     | 
    
         
            +
                  [size] * 2
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def resize_with_padding(new_size, padding)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  padded_size = new_size - (2 * padding)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  resize(padded_size)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  add_margin(padding)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def resize(new_size)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  image.resize "#{new_size}x#{new_size}"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  set_pixels_from_image
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def add_margin(margin, color: :transparent)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  pixel = pixel_of_color(color)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  row_margin = [pixel] * margin
         
     | 
| 
      
 43 
     | 
    
         
            +
                  col_margin = [Array.new(size + (2 * margin), pixel)] * margin
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  margined_rows = @pixels.map do |row|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    row_margin + row + row_margin
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  @pixels = col_margin + margined_rows + col_margin
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                private
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def set_pixels_from_image
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @pixels = @image.get_pixels("RGBA")
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def image
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @image ||= MiniMagick::Image.get_image_from_pixels(@pixels, [size, size], "RGBA", PIXEL_DEPTH, "png")
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "rqrcode_core"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "pixels_handler"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "export"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "pixel_tools"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative "module_filler"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module VisualQrcode
         
     | 
| 
      
 10 
     | 
    
         
            +
              class Qrcode
         
     | 
| 
      
 11 
     | 
    
         
            +
                include PixelTools
         
     | 
| 
      
 12 
     | 
    
         
            +
                include ModuleFiller
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                PADDING_MODULES = 7
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                attr_reader :content, :basic_qrcode, :image_handler, :vqr_pixels
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def initialize(content, image_path, size: nil)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @content = content
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @size = size
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  @basic_qrcode = RQRCodeCore::QRCode.new(content, level: :h)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @image_handler = VisualQrcode::PixelsHandler.new(image_path: image_path)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @common_patterns = @basic_qrcode.instance_variable_get(:@common_patterns)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def as_png(margin: default_margin)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  make
         
     | 
| 
      
 29 
     | 
    
         
            +
                  VisualQrcode::Export.new(@vqr_pixels).as_png(size: @size, margin: margin)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def basic_qrcode_as_png(margin: default_margin)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  make
         
     | 
| 
      
 34 
     | 
    
         
            +
                  basic_qrcode_pixels = @basic_qrcode.modules.map do |module_row|
         
     | 
| 
      
 35 
     | 
    
         
            +
                    pixels_row = module_row.map { |value| [pixel_of(value)] * PIXELS_PER_MODULE }.flatten
         
     | 
| 
      
 36 
     | 
    
         
            +
                    ([pixels_row] * PIXELS_PER_MODULE)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end.flatten(1)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  VisualQrcode::Export.new(basic_qrcode_pixels).as_png(size: @size, margin: margin)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def make
         
     | 
| 
      
 43 
     | 
    
         
            +
                  intit_vqr_pixels
         
     | 
| 
      
 44 
     | 
    
         
            +
                  resize_image
         
     | 
| 
      
 45 
     | 
    
         
            +
                  fill_vqr_pixels
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def intit_vqr_pixels
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @vqr_length = min_length * size_multiplier
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @vqr_pixels = Array.new(@vqr_length) { Array.new(@vqr_length) }
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def resize_image
         
     | 
| 
      
 54 
     | 
    
         
            +
                  padding_size = PADDING_MODULES * module_size
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @image_handler.resize_with_padding(@vqr_length, padding_size)
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def fill_vqr_pixels
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @basic_qrcode.modules.each_with_index do |module_row, x_index|
         
     | 
| 
      
 60 
     | 
    
         
            +
                    module_row.each_index do |y_index|
         
     | 
| 
      
 61 
     | 
    
         
            +
                      fill_vqr_module(x_index, y_index)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                private
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def default_margin
         
     | 
| 
      
 69 
     | 
    
         
            +
                  module_size * 2
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def size_multiplier
         
     | 
| 
      
 73 
     | 
    
         
            +
                  return 1 if @size.nil?
         
     | 
| 
      
 74 
     | 
    
         
            +
                  raise "Minimum size for your content is #{min_length}px" if @size < min_length
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  @size_multiplier ||= 1 + (@size / min_length)
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def min_length
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @min_length ||= @basic_qrcode.modules.length * PIXELS_PER_MODULE
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/visual_qrcode.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,15 +1,43 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: visual-qrcode
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.2
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Caillou
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2024-07- 
     | 
| 
       12 
     | 
    
         
            -
            dependencies: 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2024-07-12 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: mini_magick
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '5'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '5'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: rqrcode_core
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '1.2'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '1.2'
         
     | 
| 
       13 
41 
     | 
    
         
             
            description: A gem to generate working QRCodes with images in their background
         
     | 
| 
       14 
42 
     | 
    
         
             
            email:
         
     | 
| 
       15 
43 
     | 
    
         
             
            - 6117264+JeSuisUnCaillou@users.noreply.github.com
         
     | 
| 
         @@ -21,11 +49,16 @@ files: 
     | 
|
| 
       21 
49 
     | 
    
         
             
            - ".rubocop.yml"
         
     | 
| 
       22 
50 
     | 
    
         
             
            - ".ruby-version"
         
     | 
| 
       23 
51 
     | 
    
         
             
            - CHANGELOG.md
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Guardfile
         
     | 
| 
       24 
53 
     | 
    
         
             
            - LICENSE.txt
         
     | 
| 
       25 
54 
     | 
    
         
             
            - README.md
         
     | 
| 
       26 
55 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       27 
56 
     | 
    
         
             
            - lib/visual_qrcode.rb
         
     | 
| 
       28 
     | 
    
         
            -
            - lib/visual_qrcode/ 
     | 
| 
      
 57 
     | 
    
         
            +
            - lib/visual_qrcode/export.rb
         
     | 
| 
      
 58 
     | 
    
         
            +
            - lib/visual_qrcode/module_filler.rb
         
     | 
| 
      
 59 
     | 
    
         
            +
            - lib/visual_qrcode/pixel_tools.rb
         
     | 
| 
      
 60 
     | 
    
         
            +
            - lib/visual_qrcode/pixels_handler.rb
         
     | 
| 
      
 61 
     | 
    
         
            +
            - lib/visual_qrcode/qrcode.rb
         
     | 
| 
       29 
62 
     | 
    
         
             
            - lib/visual_qrcode/version.rb
         
     | 
| 
       30 
63 
     | 
    
         
             
            - sig/visual_qrcode.rbs
         
     | 
| 
       31 
64 
     | 
    
         
             
            homepage: https://github.com/JeSuisUnCaillou/visual-qrcode
         
     |