Since I posted this yesterday (in the wrong thread naturally…), I’ve been obsessing a little over trying to cram as much data and as best a photo as possible on a limited size NTAG. Classic hacker pursuit: there isn’t nearly enough enough space on an NFC tag for a decent vCard photo, so I just had to do it
I’ve been trying different JPEG encoders, different mugshot sizes, contrasts etc… I played the what-if game manually for a while - that is, change something, regenerate the vCard by hand, program it onto the tag by hand, try it on the cellphone, failure, rinse, repeat.
That got old pretty quickly, so I wrote a script to do everything in one go, test the size of the final NDEF and write it onto a tag. It makes trying to exploit as much of the tag’s memory space as possible quite easy and fast. So I figured I’d share it, if anybody’s interested.
The script is Linux-only. Sorry, if you have another OS, I can’t help you. But if you do run Linux, read on.
Before you use it, you need a few things installed:
-
Imagemagick: it should come installed by default in most Linux distros. If not, you can do something like
apt-get install imagemagick
as root (for Debian-based distros - your mileage may vary) -
Guetzli: this is an advanced JPEG encoder released by Google in 2017 that really cuts down on size and preserve the quality a lot better than libjpeg. It’s not as good as JPEG2000, but JPEG2000 files aren’t supported natively in Android, so it wasn’t an option anyway. Guetzli really is quite good, and by far the best JPEG encoder I’ve found.
There is a pre-built Guetzli package, but unfortunately, by default Guetzli refuses to lower the JPEG quality enough and tells you to get the source and hack it So what you need to do is this (as an ordinary user):
- Get the code from Github:
git clone https://github.com/google/guetzli.git
- Edit
guetzli/guetzli/processor.cc
, look for a line that saysif (params.butteraugli_target > 2.0f) {
and replace it withif (0) {
- Compile it normally with
make
. No need to install it, the script below assumes it’s in the current directory
- Get the code from Github:
-
ndeflib: it’s a Python module. Install it by doing
pip install ndeflib
andpip3 install ndeflib
as root (you need both the Python2 and Python3 versions). -
ndeftool: another Python utility. Install it by doing
pip3 install ndeftool
as root. -
nfcpy: do
pip3 install nfcpy
as root. -
tagtool.py: normally it’s part of nfcpy, but it’s not packaged in the pip distribution for some reason. You need to get it from the Github repo. Simply do
git clone https://github.com/nfcpy/nfcpy.git
in the same directory you pulled Guetzli in as a normal user.
Finally, to create the vCard, encode it into an NDEF record and write it to a tag, copy / paste the following script in the directory where Guetzli and nfcpy reside:
#!/bin/bash
### vCard information
FIRSTNAME=Joe
LASTNAME=Blow
TELEPHONE="(311) 555-1212"
EMAIL=joeblow@aol.com
PHOTO=./photo.jpg
NOTE=
### Final size of the photo on the tag
TAG_PHOTO_SIZE=50x50
### Quality of the compressed photo on the tag
JPEG_QUALITY=72
### Path to the various utilities we need
GUETZLI=./guetzli/bin/Release/guetzli
NDEFTOOL=ndeftool
TAGTOOL="python3 nfcpy/examples/tagtool.py"
# Resize / compress the original photo
echo "Resizing the photo"
convert $PHOTO -resize $TAG_PHOTO_SIZE ./photo_resized.png
echo "Compressing the photo"
$GUETZLI --quality $JPEG_QUALITY ./photo_resized.png ./photo_compressed.jpg
# Create the .vcf file
echo "Creating the vCard file"
echo "BEGIN:VCARD" > vcard.vcf
echo "VERSION:2.1" >> vcard.vcf
echo "N:$LASTNAME;$FIRSTNAME" >> vcard.vcf
echo "TEL:$TELEPHONE" >> vcard.vcf
echo "EMAIL:$EMAIL" >> vcard.vcf
B64ENCPHOTO=$(base64 -w0 ./photo_compressed.jpg)
B64ENCPHOTO="PHOTO;ENCODING=BASE64;TYPE=JPEG:$B64ENCPHOTO"
echo $B64ENCPHOTO | head -c 73 >> vcard.vcf
for L in $(echo $B64ENCPHOTO | tail -c +74 | sed -e 's/.\{72\}/&\n/g'); do
echo -en "\n $L" >> vcard.vcf
done
echo >> vcard.vcf
if [ "$NOTE" ];then
echo "NOTE:$NOTE" >> vcard.vcf
fi
echo "END:VCARD" >> vcard.vcf
# Dump the size of the .vcf file, for information
VCARDSIZE=$(wc -c < vcard.vcf)
echo "vCard size: $VCARDSIZE bytes"
# Create the NDEF record
echo "Encapsulating the vCard into an NDEF record"
$NDEFTOOL tn text/vcard pl "$(cat vcard.vcf)" > vcard.ndef
# Dump the size of the NDEF file, for information
NDEFSIZE=$(wc -c < vcard.ndef)
echo "NDEF size: $NDEFSIZE bytes"
# Read the tag
echo "Reading the tag"
CARD_INFO=$($TAGTOOL)
# Make sure the tag is writable
if [ ! "$(echo $CARD_INFO | grep -i 'NDEF Capabilities.*writeable *= *yes')" ]; then
echo "Tag is not writeable!"
exit -1
fi
# Make sure there's enough room on the tag for the NDEF record
TCAPA=$(echo $CARD_INFO | sed -r 's/^.*NDEF Capabilities.*capacity *= *([0-9]+) byte.*$/\1/')
if [ $TCAPA -lt $NDEFSIZE ]; then
echo "NDEF size exceeds the tag's capacity ($TCAPA bytes)!"
exit -1
fi
# Ask the user if they really want to write the vCard on the tag
read -p "Write NDEF onto the tag [Y/N]? " YN
if [ "$YN" != "y" ] && [ "$YN" != "Y" ]; then
echo "Aborting"
exit 0
fi
# Write the NDEF file onto the tag
echo "Writing NDEF onto the tag"
$TAGTOOL load vcard.ndef
echo "All done!"
Name it, say, write_vcard_to_tag.sh
, then make it executable by doing chmod +x write_vcard_to_tag.sh
.
To use it, supply a photo - preferably with a square aspect ratio - called photo.jpg
. Then edit your vCard information at the beginning of the script, and run it. It’ll generate the vCard, then the NDEF record, then try to write it onto the tag.
If the NDEF record ends up being too large, it’ll tell you and stop. If that happens, go back to the script and tweak the size of the photo and the JPEG quality until it fits and you’re satisfied with the resulting image.
The aforementioned utilities use libnfc to write to the tag. I’ve only tried it with an ACR122U, but I suppose it’ll work just as well with other readers supported by libnfc. In any case, if you don’t have that going yet, you’ll have to set it up first.
After playing with it for a while, I managed to come up with a vCard that contains my personal information, a 50x50 photograph in which I’m reasonably recognizable even after blowing it up, and my blood type in the vCard’s note field, with only 2 bytes to spare on an NTAG216
That’ll be the vCard I’ll be putting on my doNExT: it’ll serve for snazzy contact exchange of course, but also for first-aid professionals if I’m found unconscious somewhere and they happen to carry a cellphone (and can I convince myself to have an NFC logo tattoo done over the implant… Not too sure about that one yet).
I hope the above will be of interest.