adamchainz / treepoem

Barcode rendering for Python supporting QRcode, Aztec, PDF417, I25, Code128, Code39 and many more types.
MIT License
129 stars 26 forks source link

Generating multiple barcodes without reopening the gs subprocess? #145

Open dlenski opened 5 years ago

dlenski commented 5 years ago

I'm trying to figure out whether there's a reasonable way to generate multiple barcode images without reopening the gs subprocess and reinterpreting the BWIPP source — which are rather "heavyweight" and slow operations.

It seems like this should be eminently possible. See below: ./multi_barcode.py (run as multi_barcode.py "bc1" "bc2" ....

Problems I'm having:

  1. I can't figure out how to make this work with EPS, because the eps2write driver doesn't like switching output files.
  2. I can't figure out how to measure the bounding box and output the correct-sized image in a single pass.
#!/usr/bin/python3

from treepoem import BWIPP
from binascii import hexlify
import os, sys, subprocess, time

ghostscript = 'gs'
barcode_ps = '/home/dlenski/build/treepoem/treepoem/postscriptbarcode/barcode.ps'

gs_process = subprocess.Popen(
    [ghostscript,  '-dNOPAUSE', '-sDEVICE=pngmono', '-q'],
    universal_newlines=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
gs_process.stdin.write('<{}> run\n'.format(hexlify(barcode_ps.encode()).decode()))

for ii, code in enumerate(sys.argv[1:]):
    ofn = '/tmp/test-%d.png' % ii
    options = 'includetext'
    symbology = 'code128'

    print("Generating barcode for %s to file %s" % (code, ofn))
    try: os.unlink(ofn)
    except: pass

    fields = {k:hexlify(v.encode()).decode() for k,v in {'ofn':ofn, 'code':code, 'options':options, 'symbology':symbology}.items()}
    gs_process.stdin.write('''
        << /OutputFile <{ofn}> >> setpagedevice
        /Helvetica findfont 10 scalefont setfont
        gsave
        2 2 scale
        10 10 moveto

        <{code}> <{options}> <{symbology}> cvn
        /uk.co.terryburton.bwipp findresource exec
        grestore

        showpage
        '''.format(**fields))
    try:
        gs_process.stdin.flush()
    except:
        print(gs_process.communicate())

    st = None
    for ii in range(4):
        try:
            st = os.stat(ofn)
        except FileNotFoundError:
            time.sleep(1)
        else:
            break
    if not st:
        print("ERROR: File %s not created after %d seconds." % (ofn, ii))
    else:
        print("OKAY: File %s (%dB) created after %d seconds." % (ofn, st.st_size, ii))

else:
    stdout, stderr = gs_process.communicate()
    print("STDOUT: %s" % stdout)
    print("STDERR: %s" % stderr)
adamchainz commented 5 years ago

Hi Dan

I am afraid I can't really help with this. I don't actively use treepoem any more. Would you like to become a maintainer?

Adam

dlenski commented 5 years ago

Ah, too bad! Yes, that would be useful… though it also means I'll have to figure out all the problems on my own :joy:

adamchainz commented 5 years ago

I've invited you as a collaborator on GitHub. If you don't mind I'll still keep doing maintenance work like updating the requirements and doing releases, but other than that feel free to make code changes, I'll try make myself available to review PR's.

But yes enjoy trying to make the optimization of a single ghostscript, it would be a big performance boost.

adamchainz commented 1 year ago

I'm more actively maintaining treepoem now. I just merged #499 which removes using the PIL EPS plugin to run Ghostscript, so now treepoem is the only thing running Ghostscript.

From here I think we may be able to move to a single process model. There may be some difficulty as the bounding box template and rendering postscript templates use different headers.