Newer
Older
skylabel / skylabel.py
#!/usr/bin/env python3

import argparse
import urllib.parse
import qrcode
import qrcode.image.svg
import os
import sys
import shutil
import csv
from subprocess import run


urlPrefix = 'https://wiki.thu-skyworks.org/'

texPreamable = '''
\\documentclass{minimal}
\\usepackage[UTF8,fontset=founder]{ctex}
\\usepackage{hyperref}
\\usepackage{graphicx}
\\usepackage{tikz}
\\usepackage{svg}
\\usetikzlibrary{fit,calc}
\\usepackage{qrcode}
'''

texEnd = '\\end{tikzpicture}\n\\end{document}'


def runtex(jobname):
    p = run(['xelatex', '-shell-escape', '-interaction=nonstopmode',
             '-output-directory=./temp', '-halt-on-error',
             './temp/' + jobname + '.tex'], stdout=sys.stdout,
            stderr=sys.stderr, encoding='utf-8')
    assert(p.returncode == 0)


class skylabel:
    NEW_CELL = 0
    NEW_PAGE = 1
    NEW_ROW = 2

    def new(self):
        self.counter += 1
        # Time to start a new row
        if self.currentcol % self.matrix[0] == 0:
            # to start a new page
            if self.currentrow % self.matrix[1] == 0:
                self.currentrow = 1
                self.currentcol = 1
                if self.counter != 1:
                    return self.NEW_PAGE
                else:
                    return self.NEW_CELL
            else:
                self.currentcol = 1
                self.currentrow += 1
                return self.NEW_ROW
        else:
            self.currentcol += 1
            return self.NEW_CELL

    def __init__(
            self, pagesize, qrsize, layout, logowidth, logooffset, textoffset,
            textsize, labelsize, matrix=(1, 1),
            cellsep=(0, 0), example=False):
        self.pagesize = pagesize
        self.qrsize = qrsize
        self.layout = layout
        self.logowidth = logowidth
        self.logooffset = logooffset
        self.textoffset = textoffset
        self.textsize = textsize
        self.matrix = matrix
        self.cellsep = cellsep
        self.labelsize = labelsize
        self.example = example
        self.counter = 0
        self.currentrow = 0
        self.currentcol = 0

    def genQRImg(self, qrstr):
        if not self.noenc:
            qrstr = urllib.parse.quote(qrstr)
        if not self.noUrlPrefix:
            qrstr = urlPrefix + qrstr
        factory = qrcode.image.svg.SvgPathImage
        qrcode.make(qrstr, image_factory=factory).save(
            './temp/qr{}.svg'.format(self.counter))

    def genTexPreamable(self):
        if self.layout == 'B':
            linespread = '\\linespread{0.9}\n'
        else:
            linespread = ''
        return texPreamable + '''\\usepackage[papersize={{{s[0]}mm, {s[1]}mm\
}}]{{geometry}}\n'''.format(s=self.pagesize) + linespread + '''\\begin{document}
\\begin{tikzpicture}[remember picture, overlay, shift=(current page.north west)]
'''

    def genCell(self, para):
        res = self.new()
        if res == self.NEW_PAGE:
            ret = '\\end{tikzpicture}\\newpage' + \
                '\\begin{tikzpicture}[remember picture, overlay, shift=' + \
                '(current page.north west)]\n'
        else:
            ret = ''
        if self.layout == 'A':
            self.genQRImg(para[1])
            ret += '\\node (qrcode{}) at ({}mm, -{}mm) [anchor=center] '.\
                format(
                    self.counter,
                    0.5 * self.labelsize[0] +
                    (self.currentcol-1)*self.cellsep[0],
                    0.5 * self.labelsize[0] +
                    (self.currentrow-1)*self.cellsep[1]
                )
            ret += '{{\\includesvg[width={}mm]{{./temp/qr{}}}}};\n'.\
                format(self.qrsize, self.counter)
            ret += '''\\node[anchor=north,inner sep=0,shift={{({s[0]}mm,\
{s[1]}mm)}}] (logo{c}) at (qrcode{c}.south) {{\\includegraphics[width={w}mm]\
{{20110623skyworkslogo}}}};\n'''.format(s=self.logooffset,
                                        c=self.counter,
                                        w=self.logowidth)
            ret += '''\\node[anchor=north,inner sep=0,shift={{({s[0]}mm,\
{s[1]}mm)}}] (text{c}) at (logo{c}.south) {{\\{size}\\sffamily {text}}};
'''.format(s=self.textoffset, c=self.counter, size=self.textsize, text=para[0])
            pass
        elif self.layout == 'B':
            self.genQRImg(para[1])
            ret += '''\\node (qrcode{}) at ({}mm, -{}mm) \
[inner sep=0,anchor=center] '''.format(
                    self.counter,
                    0.5 * self.labelsize[0] +
                    (self.currentcol-1)*self.cellsep[0],
                    0.5 * self.labelsize[0] +
                    (self.currentrow-1)*self.cellsep[1]
                )
            ret += '{{\\includesvg[width={}mm]{{./temp/qr{}}}}};\n'.\
                format(self.qrsize, self.counter)
            ret += '''\\path (qrcode{c}.south west) -- \
node[inner sep=0,midway,anchor=west,align=center,font=\\sffamily\\{size}] \
(logo{c}) {{{logoText}}} (qrcode{c}.south west |- (0mm,-{s}mm);
'''.format(c=self.counter,
                size=self.textsize,
                logoText=self.logoText,
                s=self.labelsize[1] + (self.currentrow-1) * self.cellsep[1])
            ret += '''\\path (logo{c}.east) -- node\
[midway,anchor=center,font=\\{size}\\sffamily,align=center] (text{c}) \
{{{text}}} (logo{c}.east -| qrcode{c}.east);
'''.format(c=self.counter,
                size=self.textsize,
                text=para[0])
            pass
        elif self.layout == 'PASSSEAL':
            if self.example:
                ret += '\\tikzset{example/.style={fill=black!25}}\n'
            else:
                ret += '\\tikzset{example/.style={}}\n'
            with open('./passwordseal.tex', 'r') as f:
                ret += f.read().\
                    replace('realname', para[0]).\
                    replace('studentid', para[1]).\
                    replace('username', para[2]).\
                    replace('password', para[3]).\
                    replace('wifipw', para[4])
            pass

        return ret
        pass

    def genOutput(self, input, outprefix):
        tex = self.genTexPreamable()
        with open(input, 'r') as f:
            r = csv.reader(filter(lambda row: row[0] != '#', f))
            for i in r:
                print(i)
                tex += self.genCell(i)
        tex += texEnd
        with open('./temp/' + outprefix + '.tex', 'w') as f:
            f.write(tex)
        # Run twice
        runtex(outprefix)
        runtex(outprefix)


types = {
    '8050A':
    skylabel(
        pagesize=(50, 80),
        qrsize=45, layout='A', logowidth=35, logooffset=(0, -1),
        textsize='LARGE', textoffset=(0, -3.5), labelsize=(50, 80)),
    '5030A':
    skylabel(
        pagesize=(30, 50),
        qrsize=26, layout='A', logowidth=23, logooffset=(0, -1),
        textsize='large', textoffset=(0, -2.5), labelsize=(30, 50)),
    '2015TB':  # 20mm x 15mm Triple
    skylabel(
        pagesize=(15, 64),
        qrsize=13, layout='B', logowidth=5, logooffset=(0, 0),
        textsize='tiny', textoffset=(0, 0), matrix=(1, 3), labelsize=(15, 20),
        cellsep=(0, 22)),
    'PASSSEAL':  # New member's password seal
    skylabel(
        pagesize=(190, 76.2),
        qrsize=None, layout='PASSSEAL', logowidth=None, logooffset=None,
        textsize=None, textoffset=None, labelsize=(190, 76.2))
}

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Generate a PDF file for printing on adhesive labels with '
        'thermal printer.')
    parser.add_argument(
        '-i',
        metavar='INPUT',
        dest='infile',
        help='Input file name.',
        default='input.csv')
    parser.add_argument(
        '-o',
        metavar='OUTPUT',
        dest='outfile',
        help='Output PDF file name prefix.',
        default='output')
    parser.add_argument(
        '-t',
        metavar='TYPE',
        dest='typeSelected',
        default='8050A',
        choices=types,
        help='Label size and layout.')
    parser.add_argument(
        '-u',
        metavar='TEXT',
        dest='logoText',
        default='天空\\\\工场',
        help='Custom logo text, only applicable to certain layouts.')
    parser.add_argument('--no-url-prefix', action='store_true')
    parser.add_argument('--noenc',
                        action='store_true',
                        help="Don't encode URL with urllib.parse.quote")
    parser.add_argument('--generate-examples', action='store_true')
    parser.add_argument('--debug', action='store_true')

    args = parser.parse_args()

    shutil.rmtree('./temp')
    os.makedirs('./temp')
    if args.generate_examples:
        for k, v in types.items():
            v.example = True
            v.logoText = args.logoText
            v.noenc = args.noenc
            v.noUrlPrefix = args.no_url_prefix
            v.genOutput('./examples/' + k + '-example.csv', k)
            p = run(['pdftopng', './temp/' + k + '.pdf', './examples/' + k],
                    stdout=sys.stdout, stderr=sys.stderr)
            assert(p.returncode == 0)
    else:
        v = types[args.typeSelected]
        v.logoText = args.logoText
        v.noenc = args.noenc
        v.noUrlPrefix = args.no_url_prefix
        v.genOutput(args.infile, args.outfile)
        shutil.copy('./temp/' + args.outfile + '.pdf', './')