#! /bin/sh # # hc - hard copy helper # # SYNOPSIS # hc {-e|--encode} [ENCODE_OPTION...] < FILE # hc {-d|--decode} IMAGE_FILE... # # DESCRIPTION # hc encodes arbitrary data for printing, and decodes scanned images back # into data. # # ENCODING # When invoked with the --encode flag, then hc will read data from stdin, and # write a PDF file to stdout. The data will be encoded using QR codes. # # Following options are available to customize the encoding process: # # --lhead=TeX (default: empty) # --rhead=TeX (default: empty) # --lfoot=TeX (default: current date in ISO 8601 format. # --rfoot=TeX (default: page numbering) # TeX code to use for headers and footers. # # --show-texlog # Print TeX log. # # DECODING # When invoked with the --decode flag, then hc will read the specified # (image) files, and write the decoded data to stdout. Images can be # specified in any order, and might be listed multiple times. # # Sometimes hc won't be able to locate and/or decode all QR codes found in # the images. Problematic QR codes can be identified with # # zbarimg --display IMAGE_FILE... # # Following techniques might help to make problematic QR codes readable: # # - rescan the image # # - rotate the image (try various degrees) # example: convert old.tif -rotate 5 new.tif # # - increase brightness and/or contrast # example: convert old.tif -brightness-contrast 10x20 new.tif # # - extract the QR code manually # e.g. by opening the image containing the problematic QR code with some # image viewer, zoom in, and take a screenshot (e.g. using scrot -s) # # EXAMPLES # echo secret | gpg -c | hc -e --lhead=mypass > /tmp/mypass.pdf # convert -density 288 /tmp/mypass.pdf tif:- | hc -d /dev/stdin | gpg -d # set -efu main() {( _args=$(getopt -n "$0" -s sh \ -o de \ -l decode,encode,lhead:,rhead:,lfoot:,rfoot:,show-texlog \ -- "$@") if \test $? != 0; then exit 1; fi eval set -- "$_args" unset _args unset command lhead= rhead= lfoot=$(date -Id) rfoot='\thepage/\pageref{LastPage}' show_texlog=false set_command() { if test -z "${command+1}"; then command=$1 else echo "hc: error: only one command can be used" >&2 exit 1 fi } while :; do case $1 in -d|--decode) set_command decode; shift;; -e|--encode) set_command encode; shift;; --lhead) lhead=$2; shift 2;; --rhead) rhead=$2; shift 2;; --lfoot) lfoot=$2; shift 2;; --rfoot) rfoot=$2; shift 2;; --show-texlog) show_texlog=true; shift;; --) shift; break;; esac; done # Separates a part's head from its body. body_separator=' ' # Arbitrary limit. This number is really only needed for its width, to # calculate max_body_size. The value could be made configurable. max_part_number=9999 # QR Code version 40, ECC Level H, alphanumeric capacity: 1852 max_body_size=$(expr \ 1852 \ - ${#body_separator} \ - ${#max_part_number} ) split_parts() { base32 -w"$max_body_size" | nl -s"$body_separator" -w1 } join_parts() { sort -t"$body_separator" -nu -k1 | awk --field-separator "$body_separator" ' $1 ~ /^[1-9][0-9]*$/{ print "found part " $1 > "/dev/stderr" for (i=2; i<=NF; i++) print $i } ' | base32 -d } cmd_"$command" "$@" )} cmd_encode() {( unset workdir trap cleanup EXIT cleanup () { { echo "$part_files" | xargs --no-run-if-empty rm -v || : rm -v "$workdir/out.aux" || : rm -v "$workdir/out.log" || : rm -v "$workdir/out.pdf" || : rm -v "$workdir/out.tex" || : rmdir -v "$workdir" || : trap - EXIT } >&2 } workdir=$(mktemp --tmpdir -d hc.decode.XXXXXXXX) cd "$workdir" part_files=$(split_parts | nl -w1 | { while read -r i part; do f=$workdir/part-$i.png printf %s "$part" | qrencode \ --level=H \ --margin=5 \ --output="$f" \ --verbose echo "$f" done }) { echo '\documentclass[paper=a4]{scrartcl}' echo '\usepackage{lastpage}' echo '\usepackage{graphicx}' echo '\setlength{\textheight}{640pt}' echo '\setlength{\parindent}{0mm}' echo '\usepackage[headsepline,footsepline,plainfootsepline]{scrlayer-scrpage}' echo '\def\chead#1{\cohead{#1}\cehead{#1}}' echo '\def\lhead#1{\lohead{#1}\lehead{#1}}' echo '\def\rhead#1{\rohead{#1}\rehead{#1}}' echo '\def\cfoot#1{\cofoot{#1}\cefoot{#1}}' echo '\def\lfoot#1{\lofoot{#1}\lefoot{#1}}' echo '\def\rfoot#1{\rofoot{#1}\refoot{#1}}' echo '\chead{}' echo '\lhead{\texttt{'"$lhead"'}}' echo '\rhead{\texttt{'"$rhead"'}}' echo '\cfoot{}' echo '\lfoot{\texttt{'"$lfoot"'}}' echo '\rfoot{\texttt{'"$rfoot"'}}' echo '\begin{document}' echo '\begin{flushleft}' echo "$part_files" | while read -r f; do echo '\includegraphics[scale=.368]{'"$f"'}' done echo '\end{flushleft}' echo '\end{document}' } > out.tex while :; do pdflatex \ -no-shell-escape \ -interaction=batchmode \ out.tex \ >&2 if test "$show_texlog" = true; then cat out.log >&2 fi if grep -q "LaTeX Warning: Reference \`LastPage' on page 1 undefined on input line \\([0-9]*\\)." out.log; then continue fi break done cat out.pdf )} cmd_decode() {( zbarimg --raw "$@" | join_parts )} main "$@"