Last active
April 5, 2024 15:13
-
-
Save ethan605/b6888f3c0e12e4f8168baf97f2164750 to your computer and use it in GitHub Desktop.
qrgpg - encode/decode ASCII armoured file to/from QRCode images
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -Eeuo pipefail | |
readonly RED=$(tput setaf 1) | |
readonly GREEN=$(tput setaf 2) | |
readonly YELLOW=$(tput setaf 3) | |
readonly NORMAL=$(tput sgr0) | |
readonly COMMAND="qrgpg" | |
SUB_COMMAND="" | |
INPUT_FILES=() | |
TOTAL_BLOCKS=7 | |
OUTPUT_FILE="qrgpg.asc" | |
OUTPUT_PREFIX="qrgpg" | |
FONT_FILE="" | |
function print_help_message() { | |
local error_message=${1-} | |
if [[ ! -z $error_message ]]; then | |
printf "${RED}Error:${NORMAL} $error_message\n\n" >&2 | |
fi | |
printf "$COMMAND - encode/decode ASCII armoured file to/from QRCode images | |
Usage: | |
$COMMAND encode [options] [flags] file | |
or $COMMAND decode [options] [flags] file... | |
Example: | |
$COMMAND encode -b 8 -p gpg private_key.asc | |
$COMMAND encode -f /path/to/custom/font private_key.asc | |
$COMMAND decode -o gpg.asc *.png | |
Available options for 'encode' mode: | |
-b, --blocks <string> Number of blocks to be encoded to, default to '7'. | |
-f, --font-file <file> Path to font file used for output image embedded caption. | |
-p, --output-prefix <string> String to be prepended to output image files, default to 'qrgpg'. | |
Available options for 'decode' mode: | |
-o, --output-file <string> Output file name, default to 'qrgpg.asc'. | |
Available flags | |
-h, --help Print this message | |
" | |
} | |
function parse_option_arg() { | |
if [[ -n "${2-}" ]] && [[ ${2:0:1} != "-" ]]; then | |
echo $2 | |
else | |
print_help_message "Argument for $1 is missing" | |
exit 1 | |
fi | |
} | |
function parse_args() { | |
while (( "$#" )); do | |
case "$1" in | |
encode|decode) | |
SUB_COMMAND=$1 | |
shift | |
;; | |
-b|--blocks) | |
TOTAL_BLOCKS=$(parse_option_arg "$@") | |
shift 2 | |
;; | |
-f|--font-file) | |
FONT_FILE=$(parse_option_arg "$@") | |
shift 2 | |
;; | |
-p|--output-prefix) | |
OUTPUT_PREFIX=$(parse_option_arg "$@") | |
shift 2 | |
;; | |
-o|--output-file) | |
OUTPUT_FILE=$(parse_option_arg "$@") | |
shift 2 | |
;; | |
-h|--help) | |
print_help_message | |
exit 1 | |
;; | |
-*|--*=) # unsupported args | |
print_help_message "Unsupported argument $1" | |
exit 1 | |
;; | |
*) # preserve positional args | |
INPUT_FILES+=($1) | |
shift | |
;; | |
esac | |
done | |
if [[ $SUB_COMMAND != "encode" ]] && [[ $SUB_COMMAND != "decode" ]]; then | |
print_help_message "Unsupported sub-command" | |
exit 1 | |
fi | |
if [[ ${#INPUT_FILES[@]} == 0 ]]; then | |
if [[ $SUB_COMMAND == "encode" ]]; then | |
print_help_message "Missing input file" | |
fi | |
if [[ $SUB_COMMAND == "decode" ]]; then | |
print_help_message "Missing files list" | |
fi | |
exit 1 | |
fi | |
} | |
function zero_padding() { | |
printf "%02d" $1 | |
} | |
function print_check_result() { | |
local result=${1:-""} | |
[[ ! -z $result ]] && echo "${GREEN}✔︎${NORMAL}" || echo "${RED}✗${NORMAL}" | |
} | |
function check_dependencies() { | |
local qrencode_check=$(command -v qrencode) | |
local zbarimg_check=$(command -v zbarimg) | |
local imagemagick_check=$(command -v convert) | |
if [[ -z $qrencode_check ||-z $zbarimg_check ||-z $imagemagick_check ]]; then | |
printf "${RED}Error:${NORMAL} following dependencies are required: | |
$(print_check_result $qrencode_check) qrencode (https://fukuchi.org/works/qrencode) | |
$(print_check_result $zbarimg_check) zbarimg (https://github.com/mchehab/zbar) | |
$(print_check_result $imagemagick_check) imagemagick (https://imagemagick.org) | |
" | |
exit 1 | |
fi | |
} | |
function generate_qr() { | |
local block_order=$(zero_padding $1) | |
local blocks_count=$(zero_padding $2) | |
local block_data=$3 | |
local prefix=$4 | |
local out_file="${prefix}_${block_order}.png" | |
echo -e "$block_data" | | |
qrencode \ | |
--dpi=300 \ | |
--level=H \ | |
--size=4 \ | |
--output="$out_file" | |
if [[ ! -z $FONT_FILE ]]; then | |
convert "$out_file" \ | |
-gravity South \ | |
-splice 0x15 \ | |
-font "$FONT_FILE" \ | |
-annotate +0+10 "$prefix ${block_order}/$blocks_count" "$out_file" | |
else | |
convert "$out_file" \ | |
-gravity South \ | |
-splice 0x15 \ | |
-annotate +0+10 "$prefix ${block_order}/$blocks_count" "$out_file" | |
fi | |
printf "${GREEN}Block $block_order${NORMAL} successfully encoded into image $out_file\n" | |
} | |
function calc_lines_per_block() { | |
local input_file=$1 | |
local blocks_count=$2 | |
local total_lines=$(wc -l < "$input_file" | grep --only-matching --extended-regexp "[0-9]+") | |
local lines_per_block=$(expr $total_lines / $blocks_count) | |
echo $lines_per_block | |
} | |
function encode() { | |
local input_file=${INPUT_FILES[0]} | |
local lines_per_block=$(calc_lines_per_block "$input_file" $TOTAL_BLOCKS) | |
local block_data="" | |
local block_order=1 | |
local line_number=1 | |
while IFS= read -r line_data | |
do | |
if [[ $line_number -eq 1 ]]; then | |
block_data=$line_data | |
else | |
block_data+="\n$line_data" | |
fi | |
let line_number++ | |
if [[ $line_number -gt $lines_per_block && $block_order -lt $TOTAL_BLOCKS ]]; then | |
generate_qr $block_order $TOTAL_BLOCKS "$block_data" "$OUTPUT_PREFIX" | |
let block_order++ | |
let line_number=1 | |
block_data="" | |
fi | |
done < $input_file | |
generate_qr $block_order $TOTAL_BLOCKS "$block_data" "$OUTPUT_PREFIX" | |
printf "\n${GREEN}Encoding done!${NORMAL}\n" | |
} | |
function decode() { | |
local data="" | |
for input_file in ${INPUT_FILES[@]}; do | |
printf "${GREEN}Decoding${NORMAL} $input_file\n" | |
local decoded_data=$(zbarimg --quiet --raw "$input_file") | |
data+="$decoded_data\n" | |
done | |
echo -e "$data" > "$OUTPUT_FILE" | |
printf "\n${GREEN}Decoding done!${NORMAL} Data written to $OUTPUT_FILE\n" | |
} | |
function main() { | |
check_dependencies | |
parse_args "$@" | |
case "$SUB_COMMAND" in | |
encode) encode ;; | |
decode) decode ;; | |
esac | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment