跳转到内容

模組:QR

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书

local p={}
local encode_table={}

local kanji_data = 'Module:QR/kanji'
local libencode = require('Module:EncoderUtil')
local yesno=require("Module:Yesno")
local libqrcode = libencode._get_libqrcode()
local lib_arg={}
local ec_level_list={l=1,m=2,q=3,h=4,[0]=1,[1]=1,[2]=2,[3]=3,[4]=4,
	low=1,medium=2,quartile=3,high=4
}
local ec_level_table={'low','medium','quartile','high'}
local en_mode_list={n=1,a=2,b=4,k=8,j=8,[0]=0,[1]=1,[2]=2,[4]=4,[4]=8,
	numeric=1,alphanumeric=2,byte=4,kanji=8,auto=0
}
local en_mode_table={'numeric','alphanumeric',[4]='byte',[8]='kanji'}
function _kanjiLen(str,mode)
	if mode == 1 then -- numeric
		return math.ceil(#str * 10 / 39)
	elseif mode == 2 then -- alphanumeric
		return math.ceil(#str * 11 / 26)
	elseif mode == 4 then -- binary
		return math.ceil(#str * 8 / 13)
	else
		return mw.ustring.len(str)
	end
end

--line-height: 12px;
--p.logQR(({p._CreateQRcode("維基百科,自由的百科全書")})[2])
local text_QR_char_list={
	[-2]='▁',--' '
	[-1]='▁',
	[0]='▁',
	[1]='▇',
	[2]='▇',
}
function p.logQR(qrTable)
	local result = ''
	for i=1,#qrTable do
		result=result..'\n'
		for j=1,#(qrTable[i]) do
			result=result..text_QR_char_list[qrTable[j][i]]
		end
	end
	mw.log(result)
end

function p.textQR(frame)
	local args
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame, {parentFirst=true})
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
    local input_text=args.text or args.message or args.msg or args[1] or args['1']
    local ec_level_list={l=1,m=2,q=3,h=4,[0]=1,[1]=1,[2]=2,[3]=3,[4]=4,
		low=1,medium=2,quartile=3,high=4
    }
    local ec_level = ec_level_list[(args.eclevel or''):lower()]or 1

    local input_pre_data = input_text
    if yesno(args.bitstream or false) then input_pre_data = mw.text.split(input_text or '',',')end
    local ok, qrTable = p._CreateQRcode(input_pre_data,ec_level,args.mode,tonumber(args.version or args.ver),tonumber(args.mask))
    if not ok then return "" end
	local result = ''
	for i=0,#qrTable+1 do
		if result ~= '' then result=result..'<br/>' end
		result=result..'\n'
		for j=0,#(qrTable[i]or qrTable[#qrTable])+1 do
			result=result..text_QR_char_list[((qrTable[j] or {})[i]) or 0]
		end
	end
	result=result..'<br/>\n'
	return '<div style="line-height: 0.857em;">'..result..'</div>'
end

function p._CreateQRcode(str, input_ec_level, input_mode, version_input, mask_input)
	local mode = en_mode_list[input_mode or 0]
	local encode_mode, mode_flag = p.checkMode(str,mode)
	local encode_len = libencode._get_string_length(str)
	local encode_data = {{
		mode=encode_mode,
		text=str,
		len=encode_len
	}}
	if encode_mode==8 then --kanji
		encode_data, encode_len = _data_spilter(str)
		encode_len = encode_len + #encode_data
	end
	local requested_ec_level = ec_level_list[input_ec_level or 0]
	local version, ec_level = libqrcode.get_version_eclevel(encode_len,encode_mode,requested_ec_level)
	local check_ver = tonumber(version_input or 0)
	if check_ver ~= 0 and check_ver < version then mw.addWarning( string.format(
		"Version %d 且容錯等級為 %s 的QR碼不足以容納 \"%s\" 。",check_ver,ec_level_table[requested_ec_level],str) ) end
	if not mode_flag and mode~=nil and mode~=0 then mw.addWarning(string.format(
		"無法將 \"%s\" 以編碼模式 %s 進行編碼。",str, en_mode_table[mode] or tostring(input_mode) )) end
	if check_ver > version then version = check_ver end
	local data_raw=''
	for i=1,#encode_data do
		local item = encode_data[i]
		data_raw = data_raw .. _encodeQRdata(item,version)
	end
	data_raw = libqrcode.add_pad_data(version,ec_level,data_raw)
	arranged_data = libqrcode.arrange_codewords_and_calculate_ec(version,ec_level,data_raw)
	if math.fmod(#arranged_data,8) ~= 0 then
		return false, string.format("Arranged data %% 8 != 0: data length = %d, mod 8 = %d",#arranged_data, math.fmod(#arranged_data,8))
	end
	arranged_data = arranged_data .. string.rep("0",libqrcode.remainder[version])
	local tab
	local mask = tonumber(mask_input) or -1
	if mask>=0 and mask<8 then tab = libqrcode.get_matrix_and_penalty(version,ec_level,arranged_data,mask) 
	else tab = libqrcode.get_matrix_with_lowest_penalty(version,ec_level,arranged_data) end
	return true, tab, ec_level, encode_mode, version
end

function p.checkMode(str,mode)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
        mode = args[2] or args['2'] or args.mode
    end

	local calc_mode=p.getMode(str)
	if mode == 1 then -- numeric
		return calc_mode, calc_mode == mode 
	elseif mode == 2 then -- alphanumeric
		if mode >= calc_mode then return mode, true end
		return calc_mode, false
	elseif mode == 4 or mode == 8 then -- binary or kanji
		return mode, true
	else --default
		return calc_mode, false
	end
end
function p.getMode(str)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
    end
	if type(str) ~= type("string") then --不能string:match的東西。
		return 4
	elseif mw.ustring.match(str,"^[0-9]+$") then
		return 1
	elseif mw.ustring.match(str,"^[0-9A-Z $%%*./:+-]+$") then
		return 2
	else
		if p.checkJISX0208(str)then
			return 8
		else 
			return 4
		end
	end
end
function _data_spilter(str)
	local total_len=0
	local code_len=mw.ustring.len(str)
	local spilt_result = {}
	local buffer, str_mode, check_mode='',0,0
	for i=1,code_len do
		local unicode,char_it=mw.ustring.codepoint(str,i),mw.ustring.sub(str,i,i)
		if not encode_table.shiftJIS then encode_table = mw.loadData(kanji_data)end
		if encode_table.shiftJIS[unicode] then check_mode=8
		elseif mw.ustring.match(char_it,'[0-9]+') then check_mode=1
		elseif mw.ustring.match(char_it,'[0-9A-Z $%%*./:+-]+') then check_mode=2
		else check_mode=4 end
		if check_mode ~= str_mode then
			if buffer~=''then
				local buffer_len = _kanjiLen(buffer,str_mode)
				total_len = total_len + buffer_len
				spilt_result[#spilt_result + 1]={
					mode=str_mode,
					text=buffer,
					len=buffer_len
				}
			end
			buffer=''
		end
		str_mode = check_mode
		buffer = buffer .. char_it
	end
	if buffer~=''then
		local buffer_len = _kanjiLen(buffer,str_mode)
		total_len = total_len + buffer_len
		spilt_result[#spilt_result + 1]={
			mode=str_mode,
			text=buffer,
			len=buffer_len
		}
	end
	spilt_result[#spilt_result + 1]={mode=0,len=0}
	
	return spilt_result, total_len
end

function p.check_str_mode(str,mode)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
        mode = args[2] or args['2'] or args.mode
    end
    
	if string.match(str,"^[0-9]+$") then
		return 1
	elseif string.match(str,"^[0-9A-Z $%%*./:+-]+$") then
		return 2
	else
		if p.checkJISX0208(str)then
			return 8
		else 
			return 4
		end
	end
end
function _length_binary(lenth,version,mode)
	local i = mode
	if mode == 4 then i = 3
	elseif mode == 8 then i = 4
	end
	assert( i <= 4 )
	local tab = { {10,9,8,8},{12,11,16,10},{14,13,16,12} }
	local digits
	if version < 10 then
		digits = tab[1][i]
	elseif version < 27 then
		digits = tab[2][i]
	elseif version <= 40 then
		digits = tab[3][i]
	else
		assert(false, "get_length, version > 40 not supported")
	end
	return libencode._toBinary(lenth,digits)
end

function _encodeQRdata(item, version)
	local data_raw=libencode._toBinary((type(item.text)==type(nil))and 0 or item.mode,4)
	if type(item.text)~=type(nil) then
		if item.mode==8 then
			local input_bytes=p.unicode2shiftJISbytes(item.text)
			data_raw = data_raw .. _length_binary(#input_bytes,version,item.mode)
			for i=1,#input_bytes do data_raw = data_raw .. libencode._toBinary(input_bytes[i],13)end
		else
			data_raw = data_raw .. libqrcode.get_length((type(item.text)==type(nil))and '' or item.text, version, item.mode)
			data_raw = data_raw .. libqrcode.encode_data(item.text,item.mode)
		end
	else
		data_raw = data_raw .. libencode._toBinary(0,4)
	end
	return data_raw
end

function p._kanjiQR(str,requested_ec_level)
		local version, ec_level
		local input_bytes, local_mode=p.unicode2shiftJISbytes(str), 8
		version, ec_level = libqrcode.get_version_eclevel(#input_bytes,8,requested_ec_level)
		local length_string = libqrcode.get_length(input_bytes,version,local_mode)
		local arranged_data, data_raw, mode, len_bitstring
		version, ec_level, data_raw, mode, len_bitstring = version,ec_level,libencode._toBinary(local_mode,4),local_mode,length_string
		
		data_raw = data_raw .. len_bitstring
		for i=1,#input_bytes do data_raw = data_raw .. libencode._toBinary(input_bytes[i],13)end
		data_raw = libqrcode.add_pad_data(version,ec_level,data_raw)
		arranged_data = libqrcode.arrange_codewords_and_calculate_ec(version,ec_level,data_raw)
		if math.fmod(#arranged_data,8) ~= 0 then
			return false, string.format("Arranged data %% 8 != 0: data length = %d, mod 8 = %d",#arranged_data, math.fmod(#arranged_data,8))
		end
		arranged_data = arranged_data .. string.rep("0",libqrcode.remainder[version])
		local tab = libqrcode.get_matrix_with_lowest_penalty(version,ec_level,arranged_data)
		return true, tab
end
function p.unicode2shiftJISbytes(str)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
    end
	
	local code_len=mw.ustring.len(str)
	local result={}
	local flag=true
	for i=1,code_len do
		local codebyte=p._byteJISX0208(str,i)
		local unicode=mw.ustring.codepoint(str,i)
		if codebyte then result[#result+1]=codebyte
		else
			if (unicode>47 and unicode<58) or (unicode>64 and unicode<91) or (unicode>96 and unicode<123) then
				codebyte = unicode + 159 + (unicode>96 and 1 or 0)
				
			end
			if codebyte then result[#result+1]=codebyte
			else result[#result+1]=8 end
			flag = false
			--local unistr = '&#'..unicode..';'
			--for j=1,#unistr do result[#result+1]=unistr:byte(j)end
		end
	end
	return result
end
function p.checkJISX0208(str)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
    end
	
	local code_len=mw.ustring.len(str)
	for i=1,code_len do
		local unicode=mw.ustring.codepoint(str,i)
		if not encode_table.shiftJIS then encode_table = mw.loadData(kanji_data)end
		if not encode_table.shiftJIS[unicode] then return false end
	end
	return true
end
function p.JISX0208(str)
    if str == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        local args = lib_arg.getArgs(str, {parentFirst=true}) --frame
        str = args[1] or args['1'] or args.str
    end
	local result=''
	local code_len=mw.ustring.len(str)
	for i=1,code_len do
		local unicode=mw.ustring.codepoint(str,i)
		if not encode_table.shiftJIS then encode_table = mw.loadData(kanji_data)end
		if encode_table.shiftJIS[unicode] then result=result..mw.ustring.sub(str,i,i) end
	end
	return result
end
function p._byteJISX0208(str,offsetbyte)
	if not encode_table.shiftJIS then encode_table = mw.loadData(kanji_data)end
	local str_byte=mw.ustring.codepoint(str,offsetbyte or 1)
	local shiftJIS_byte = encode_table.shiftJIS[str_byte]
	local qr_byte=shiftJIS_byte
	if shiftJIS_byte then
		local bitwise=require('bit32')
		local high_b=bitwise.rshift(bitwise.band(shiftJIS_byte,0xFF00),8)
		local low_b=bitwise.band(shiftJIS_byte,0xFF)
		local offset_data=((high_b>=0x81 and high_b<=0x9F) and 1 or((high_b>=0xE0 and high_b<=0xEB) and 2 or 0))
		local offset_map = {0x8140,0xC140,[0]=0}
		local byte_m = shiftJIS_byte - offset_map[offset_data]
		local high_m=bitwise.rshift(bitwise.band(byte_m,0xFF00),8)
		local low_m=bitwise.band(byte_m,0xFF)
		qr_byte = (high_m * 0xC0) + low_m
	end
	return qr_byte
end
return p