--As I wrote in the knowledge section, this time I will generate a concatenated QR code in the 8bit_byte
mode used in the QR code of the prescription.
--The actual data to be written in the prescription is a CSV data file with Shift JIS encoding. (Line break = CR / LF / EOF available) (Defined at here)
QRBitBuffer
, which writes the header part.rqrcode_core/qrcode/qr_bit_buffer.rb
module RQRCodeCore
class QRBitBuffer
#Omission
def byte_with_connected_encoding_start(length, page_number, last_page_number, parity)
put(3, 4) #MODE indicating the concatenated QR code= 0b0011
put(page_number, 4) #Sequence number(0〜15)
put(last_page_number, 4) #Last sequence number(0〜15)
put(parity, 8) #Parity value(XOR value of all data)
put(QRMODE[:mode_8bit_byte], 4) # 8bit_byte mode value
put(length, QRUtil.get_length_in_bits(QRMODE[:mode_8bit_byte], @version)) #Data length
end
QR8bitByte
class for 8byte_byte
mode, but create a QR8BitByteWithConnected
class that uses this for the concatenated QR code.rqrcode_core/qrcode/qr_8bit_byte_with_connected.rb
# frozen_string_literal: true
module RQRCodeCore
class QR8BitByteWithConnected
attr_reader :mode, :page_number, :last_page_number, :parity
def initialize(data, page_number, last_page_number, parity)
@mode = QRMODE[:mode_8bit_byte]
@data = data
@page_number = page_number
@last_page_number = last_page_number
@parity = parity
end
def get_length
@data.bytesize
end
def write(buffer)
buffer.byte_with_connected_encoding_start(get_length, @page_number, @last_page_number, @parity)
@data.each_byte do |b|
buffer.put(b, 8)
end
end
end
end
QRCode
class that generates a QR code, add a function that can output a concatenated QR code.
--In the case of a concatenated QR code, the capacity of the data that can be stored in one QR code is shortened by 2 bytes, but this point is not taken into consideration. (Since the original code also has a size check, you can easily implement it by adding the condition judgment in the case of the concatenated QR code.)
--Please note that the QR code version (= size) is the parameter name size: xx
of ʻoption`.rqrcode_core/qrcode/qr_code.rb
module RQRCodeCore
#Omission
class QRCode
attr_reader :modules, :module_count, :version
#Omission
def initialize( string, *args )
if !string.is_a? String
raise QRCodeArgumentError, "The passed data is #{string.class}, not String"
end
options = extract_options!(args)
level = (options[:level] || :h).to_sym
#Added option judgment for concatenated QR code
with_connected = options[:connected] || false
#Omission
# :byte_Error if a concatenated QR code is specified other than 8bit(Implementation is optional)
if with_connected && mode != QRMODE_NAME[:byte_8bit]
raise QRCodeArgumentError, 'Argument error.(Connected QRCode is byte_8bit mode only)'
end
max_size_array = QRMAXDIGITS[level][mode]
size = options[:size] || smallest_size_for(string, max_size_array)
if size > QRUtil.max_size
raise QRCodeArgumentError, "Given size greater than maximum possible size of #{QRUtil.max_size}"
end
#Omission
@data_list =
case mode
when :mode_number
QRNumeric.new( @data )
when :mode_alpha_numk
QRAlphanumeric.new( @data )
else
#If it is a concatenated QR code, change the generated class
if with_connected
page_number = options[:page_number]
last_page_number = options[:last_page_number]
parity = options[:parity]
QR8BitByteWithConnected.new(@data, page_number, last_page_number, parity)
else
QR8bitByte.new(@data)
end
end
@data_cache = nil
self.make
end
JAHIS7
1,1,1234567,13,Kita Internal Medicine Clinic
2,999-9999,9F, 99-999 Test Building, Kitashinagawa, Shinagawa-ku, Tokyo
3,03-9999-9991,03-9999-9992,
4,1,,Internal medicine
5,,,Physician Taro
11,,Patient Hanako,Kanja Hanako
12,2
13,20000103
21,1
22,01010016
23,symbol,number,2,
51,20200818
81,1,,
81,2,,This is a remarks column for prescriptions.
101,1,3,,1
111,1,1,,On the feet,
201,1,1,1,7,2619803XAZZZ,[General] External liquid containing alcohol for disinfection,4,1,mL
101,2,1,,5
111,2,1,,Once a day after dinner,
201,2,1,1,7,1124022F2ZZZ,[General] Lorazepam Tablets 1 mg,1,1,Lock
101,3,1,,28
111,3,1,,3 times a day after every meal,
201,3,1,1,7,3136004F2ZZZ,[General] Mecobalamin Tablets 0.5 mg,3,1,Lock
201,3,2,1,7,2171017F2ZZZ,[General] Nicorandil Tablets 5 mg,3,1,Lock
201,3,3,1,7,3962001F1ZZZ,[General] Buformin Hydrochloride Tablets 50 mg,3,1,Lock
201,3,4,1,7,1124022F1ZZZ,[General] Lorazepam Tablets 0.5 mg,3,1,Lock
201,3,5,1,7,1124026F1ZZZ,[General] Tofisopam Tablets 50 mg,3,1,Lock
201,3,6,1,2,612140503,,3,1,Lock
101,4,1,,28
111,4,1,,3 times a day after every meal,
201,4,1,1,7,2344009F2ZZZ,[General] Magnesium oxide tablets 330 mg,6,1,Lock
101,5,3,,1
111,5,1,,On the feet,
201,5,1,1,7,2619803XAZZZ,[General] External liquid containing alcohol for disinfection,4,1,mL
101,6,1,,28
111,6,1,,3 times a day after every meal,
201,6,1,1,7,3136004F2ZZZ,[General] Mecobalamin Tablets 0.5 mg,3,1,Lock
201,6,2,1,7,2171017F2ZZZ,[General] Nicorandil Tablets 5 mg,3,1,Lock
201,6,3,1,7,3962001F1ZZZ,[General] Buformin Hydrochloride Tablets 50 mg,3,1,Lock
201,6,4,1,7,1124022F1ZZZ,[General] Lorazepam Tablets 0.5 mg,3,1,Lock
201,6,5,1,7,1124026F1ZZZ,[General] Tofisopam Tablets 50 mg,3,1,Lock
201,6,6,1,2,612140503,,3,1,Lock
def create_total_text_parity(data)
data.each_byte.inject(0) { |parity, b| parity ^ b }
end
slice_data_for_connected_qrcode
method to divide the data into the required number. #Add a require for RQRCodeCore if needed.
LIMIT_OF_CONNECTED_QRCODE_LENGTH = 16
CAPACITY_GAP_FOR_CONNECTED = 2
def slice_data_for_connected_qrcode(data, options)
capacity = binary_qrcode_capacity_from(options[:level], options[:size]) - CAPACITY_GAP_FOR_CONNECTED
sliced =
if options[:adjust_for_sjis]
slice_sjis_data(data, capacity: capacity)
else
slice_data(data, capacity: capacity)
end
fail 'data for connect QRCode too many length.' if sliced.length > LIMIT_OF_CONNECTED_QRCODE_LENGTH
sliced
end
def binary_qrcode_capacity_from(level, size)
RQRCodeCore::QRMAXDIGITS[level][:mode_8bit_byte][size - 1]
end
def slice_sjis_data(sjis_data, capacity:)
is_first_char = ->(char) { (char >= 129 && char <= 159) || (char >= 224 && char <= 239) }
bytes = sjis_data.each_byte
sliced_list = []
while bytes.present?
next_bytes = bytes.take(capacity)
if is_first_char.call(next_bytes.last) && next_bytes.length != bytes
next_bytes = bytes.take(capacity - 1)
end
sliced_list << next_bytes.pack('c*')
bytes = bytes.drop(next_bytes.length)
end
sliced_list
end
def slice_data(data, capacity:)
data.each_byte.each_slice(capacity).map { |sliced| sliced.pack('c*') }
end
# Generate connected QRCode. (binary mode only)
#
# # data - the string you wish to encode
# # args
# # size - the size of the qrcode (default 4)
# # level - the error correction level
# # adjust_for_sjis - true: no split sjis multi byte character
#
# qrcode_list = RQRCode::ConnectedQRCodeUtil.generate_binary_connected_qrcodes('hello world', size: 1, level: :m)
def generate_binary_connected_qrcodes(data, *args)
options = extract_options!(args)
#If it fits in one QR code, it will be generated with a normal QR code.
return [RQRCode::QRCode.new(data, options)] if fits_in_single_qrcode?(data, options)
#Split data
sliced_text = slice_data_for_connected_qrcode(data, options)
#Generate parity
parity = create_total_text_parity(data)
#Generate a QR code based on the divided data
sliced_text.map.with_index do |text, number|
extend_args = { page_number: number, last_page_number: sliced_text.length - 1, connected: true, parity: parity }
RQRCode::QRCode.new(text, options.merge(extend_args))
end
end
def extract_options!(arr)
arr.last.is_a?(::Hash) ? arr.pop : {}
end
def fits_in_single_qrcode?(data, options)
data.each_byte.to_a.length <= binary_qrcode_capacity_from(options[:level], options[:size])
end
QRCODE_SIZE = 16 # Size of QRCODE
QRCODE_LEVEL = :l # Error correction level (L = 7%)
MODULE_PIXEL_SIZE = 2 # Size of Pixel
def create_qrcode_png_files_from(csv_data)
qrcodes = RQRCode::ConnectedQRCodeUtil.generate_binary_connected_qrcodes(
csv_data,
size: QRCODE_SIZE,
level: QRCODE_LEVEL,
adjust_for_sjis: true
)
qrcodes.map.with_index do |qrcode, index|
file_path = File.join('tmp', "qrcode_#{index}.png ")
qrcode.as_png(module_px_size: MODULE_PIXEL_SIZE, file: file_path)
end
end
A QR code divided into three was generated. ――When rendering side by side, it is necessary to secure a clear area (margin around the QR code), so be careful.
This article was written with reference to the following information.
byte_with_connected_encoding_start
from 4 to 3.Recommended Posts