pythonpython-3.xmatplotlibbarcodecode128

Plotting barcode displays differently in plot window than in saved .pdf


I am having some trouble saving my pdf properly. I am trying to plot a barcode label and subsequently save it as a pdf, as in the following code. I have installed the code128.ttf font on my windows. Also, I have tried setting the .savefig dpi argument to fig.dpi, as argued in this post.

import os

import matplotlib.pyplot as plt

from matplotlib import font_manager as fm


def draw_label(label, label_dimensions_x=3.8189, label_dimensions_y=1.41732):

    # import barcode code128 font
    fpath = os.path.join("path", "to", "font", "code128.ttf")

    prop = fm.FontProperties(fname=fpath, size=58)

    fig, ax = plt.subplots(1, figsize=(label_dimensions_x,
                                       label_dimensions_y))

    plt.axis('off')
    plt.xticks([], [])
    plt.yticks([], [])
    plt.tight_layout()
    plt.xlim(0, label_dimensions_x)
    plt.ylim(0, label_dimensions_y)

    # plot barcode
    plt.text(label_dimensions_x / 2, label_dimensions_y / 2, label,
             ha='center', va='bottom',
             fontproperties=prop)

    plt.show()

    try:
        plt.savefig(os.path.join("path", "to", "output", label + '.pdf'),
                    dpi=plt.gcf().dpi)
    except PermissionError:
        logging.warning("Close the current label pdf's before running this script.")

    plt.close()

    return

draw_label('123456789')

This is what is output in the plot window.

This is what is output in the .pdf saved file, and this happens for all kinds of labels - it's not as if the numbers 1 to 9 except 8 are not printable. EDIT: If I substitute a normal text font (in this case Frutiger Roman) for the code128.ttf, and set plt.axis('on') the text is not clipped, see this. Admitted, it's not pretty and doesn't fit too well, but it should be readable still.


Solution

  • Sam,

    First, your barcode won't scan, as is. The string requires a start character, a checksum and a stop character to be added for Code128B. So, there's that.

    enter image description here

    I recommend changing to Code 39 font (which, doesn't require checksum, and start and stop characters are the same: "*") or writing the code to produce the checksum and learning a little more about Code 128 at Code 128 Wiki.

    Second, I suspect there are issues with the bounding box for the graphic during the conversion to PDF. That small section of barcode being converted looks more like a piece of the number nine in the string. I suspect there is some image clipping going on. enter image description here

    Try substituting a regular text font to make sure the barcode image isn't being lost in the conversion.

    Edited answer to include suggestion to use PNG instead of PDF.

    I managed to get the software to work if you output to PNG format. I know, now the problem becomes how to convert PNG to PDF. You can start by investigating some of the libraries mentioned here: Create PDF from a list of images

    In short I recommend you create graphics files and then embed them in document files.

    I also added the code you need to build the barcode with the start, checksum and stop characters:

    import os
    
    import matplotlib.pyplot as plt
    
    from matplotlib import font_manager as fm
    
    def draw_label(label, label_dimensions_x=3.8189, label_dimensions_y=1.41732):
    
        # import barcode code128 font
        fpath = os.path.join("./", "code128.ttf")
    
        prop = fm.FontProperties(fname=fpath, size=32)
    
        fig, ax = plt.subplots(1, figsize=(label_dimensions_x,
                                           label_dimensions_y))
    
        plt.axis('off')
        plt.xticks([], [])
        plt.yticks([], [])
        plt.tight_layout()
        plt.xlim(0, label_dimensions_x)
        plt.ylim(0, label_dimensions_y)
    
        # calc checksum THEN plot barcode
        weight = 1
        chksum = 104
        for x in label:
            chksum = chksum + weight*(ord(x)-32)
            weight = weight + 1
        chksum = chksum % 103
        chkchar = chr(chksum+32)
        label128 = "%s%s%s%s" % ('Ñ', label, chkchar, 'Ó')
        plt.text(label_dimensions_x / 2, label_dimensions_y / 2, label128,
                 ha='center', va='bottom',
                 fontproperties=prop)
        try:
            plt.savefig(os.path.join("./", label + '.png'))
        except PermissionError:
            logging.warning("Close the current label pdf's before running this script.")
    
        return
    
    draw_label('123456789')
    draw_label('987654321')
    draw_label('Test&Show')