绘制条形码在绘图窗口中的显示方式与在保存的 .pdf 中不同

Posted

技术标签:

【中文标题】绘制条形码在绘图窗口中的显示方式与在保存的 .pdf 中不同【英文标题】:Plotting barcode displays differently in plot window than in saved .pdf 【发布时间】:2019-03-28 19:30:22 【问题描述】:

我无法正确保存我的 pdf。我正在尝试绘制条形码标签,然后将其另存为 pdf,如下面的代码所示。我已经在我的 Windows 上安装了 code128.ttf 字体。此外,我已尝试将 .savefig dpi 参数设置为 fig.dpi,如 this 帖子中所述。

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 是绘图窗口中的输出。

This 是 .pdf 保存文件中的输出内容,所有类型的标签都会发生这种情况 - 除了 8 之外的数字 1 到 9 并不是不可打印的。 编辑:如果我用普通文本字体(在本例中为 Frutiger Roman)代替 code128.ttf,并设置 plt.axis('on') 文本不会被剪裁,请参阅this。承认,它不漂亮,也不太适合,但它应该仍然可读。

【问题讨论】:

由于您使用的是字体,是否有理由将条形码转换为图像并嵌入图像,而不是直接将文本嵌入到 PDF 中? 并非如此。我没有看到我如何首先将条形码转换为图像,我直接使用 plt.text 编写并将 fontproperties 设置为条形码属性,对吧?我不是这样直接嵌入文本的吗? 【参考方案1】:

山姆,

首先,您的条形码不会按原样扫描。该字符串需要为 Code128B 添加一个开始字符、一个校验和和一个停止字符。所以,就是这样。

我建议更改为 Code 39 字体(不需要校验和,并且开始和停止字符相同:“*”)或编写代码来生成校验和并在 @ 学习更多关于 Code 128 的信息987654322@.

其次,我怀疑在转换为 PDF 期间图形的边界框存在问题。被转换的那一小段条形码看起来更像是字符串中数字 9 的一部分。我怀疑有一些图像剪辑正在进行。

尝试替换常规文本字体以确保条形码图像在转换过程中不会丢失。

已编辑答案以包含使用 PNG 而不是 PDF 的建议。

如果您输出为 PNG 格式,我设法让软件正常工作。我知道,现在问题变成了如何将 PNG 转换为 PDF。您可以从调查这里提到的一些库开始:Create PDF from a list of images

简而言之,我建议您创建图形文件,然后将它们嵌入到文档文件中。

我还添加了构建带有开始、校验和和停止字符的条形码所需的代码:

导入操作系统 将 matplotlib.pyplot 导入为 plt 从 matplotlib 导入 font_manager 作为 fm def draw_label(标签,label_dimensions_x=3.8189,label_dimensions_y=1.41732): # 导入条码code128字体 fpath = os.path.join("./", "code128.ttf") prop = fm.FontProperties(fname=fpath, size=32) 无花果,ax = plt.subplots(1, figsize=(label_dimensions_x, label_dimensions_y)) plt.axis('关闭') plt.xticks([], []) plt.yticks([], []) plt.tight_layout() plt.xlim(0, label_dimensions_x) plt.ylim(0, label_dimensions_y) # 计算校验和 THEN 绘制条形码 重量 = 1 校验和 = 104 对于标签中的 x: 校验和 = 校验和 + 重量*(ord(x)-32) 重量 = 重量 + 1 校验和 = 校验和 % 103 chkchar = chr(chksum+32) label128 = "%s%s%s%s" % ('Ñ', label, chkchar, 'Ó') plt.text(label_dimensions_x / 2, label_dimensions_y / 2, label128, ha='中心', va='底部', 字体属性=道具) 尝试: plt.savefig(os.path.join("./", label + '.png')) 除了 PermissionError: logging.warning("在运行此脚本之前关闭当前标签 pdf。") 返回 draw_label('123456789') draw_label('987654321') draw_label('测试&显示')

【讨论】:

不幸的是,因为我正在为现有应用程序编写这段(简化的)代码,所以我无法转换为不同的代码类型。我认为我检查过的开始和结束字符,我认为我需要替换的条形码标签上没有。将仔细检查。在我的条形码中替换文本字体没有帮助,它只表明只有条形码没有正确呈现,尽管其余的文本是。 因此,条形码并不是从“画布”上剪下来的,请参阅后期编辑。 您是否尝试将 'plt.show()' 移到 'plt.savefig(...)' 之后的末尾? @Sam 你一定漏掉了什么,因为我的代码和 Brian 都为那个字符串返回了 '<' 我通常也不使用条形码字体,我对生成自己的图像很满意。使用 matplotlib 只是因为它能够输出 PDF 是一个有趣的 hack。【参考方案2】:

使用 matplotlib 和字体会使事情变得过于复杂。直接生成图像并将其保存为 PDF 文件并不复杂,而且更加可靠。

正如Brian Anderson 所述,仅对字符串中的字符进行编码是不够的。您需要添加起始码、校验和和终止码来制作完整的条形码。下面的函数code128_codes 执行此操作,将转换为图像作为单独的步骤。

from PIL import Image

def list_join(seq):
    ''' Join a sequence of lists into a single list, much like str.join
        will join a sequence of strings into a single string.
    '''
    return [x for sub in seq for x in sub]

_code128B_mapping = dict((chr(c), [98, c+64] if c < 32 else [c-32]) for c in range(128))
_code128C_mapping = dict([(u'%02d' % i, [i]) for i in range(100)] + [(u'%d' % i, [100, 16+i]) for i in range(10)])

def code128_codes(s):
    ''' Code 128 conversion to a list of raw integer codes.
        Only encodes ASCII characters, does not take advantage of
        FNC4 for bytes with the upper bit set. Control characters
        are not optimized and expand to 2 characters each.
        Coded for https://***.com/q/52710760/5987
    '''
    if s.isdigit() and len(s) >= 2:
        # use Code 128C, pairs of digits
        codes = [105] + list_join(_code128C_mapping[s[i:i+2]] for i in range(0, len(s), 2))
    else:
        # use Code 128B and shift for Code 128A
        codes = [104] + list_join(_code128B_mapping[c] for c in s)
    check_digit = (codes[0] + sum(i * x for i,x in enumerate(codes))) % 103
    codes.append(check_digit)
    codes.append(106) # stop code
    return codes

_code128_patterns = '''
    11011001100 11001101100 11001100110 10010011000 10010001100 10001001100
    10011001000 10011000100 10001100100 11001001000 11001000100 11000100100
    10110011100 10011011100 10011001110 10111001100 10011101100 10011100110
    11001110010 11001011100 11001001110 11011100100 11001110100 11101101110
    11101001100 11100101100 11100100110 11101100100 11100110100 11100110010
    11011011000 11011000110 11000110110 10100011000 10001011000 10001000110
    10110001000 10001101000 10001100010 11010001000 11000101000 11000100010
    10110111000 10110001110 10001101110 10111011000 10111000110 10001110110
    11101110110 11010001110 11000101110 11011101000 11011100010 11011101110
    11101011000 11101000110 11100010110 11101101000 11101100010 11100011010
    11101111010 11001000010 11110001010 10100110000 10100001100 10010110000
    10010000110 10000101100 10000100110 10110010000 10110000100 10011010000
    10011000010 10000110100 10000110010 11000010010 11001010000 11110111010
    11000010100 10001111010 10100111100 10010111100 10010011110 10111100100
    10011110100 10011110010 11110100100 11110010100 11110010010 11011011110
    11011110110 11110110110 10101111000 10100011110 10001011110 10111101000
    10111100010 11110101000 11110100010 10111011110 10111101110 11101011110
    11110101110 11010000100 11010010000 11010011100 1100011101011'''.split()

def code128_img(s, height=100, bar_width=1):
    ''' Generate a Code 128 barcode image.
        Coded for https://***.com/q/52968042/5987
    '''
    codes = code128_codes(s)
    pattern = ''.join(_code128_patterns[c] for c in codes)
    pattern = '00000000000' + pattern + '00000000000'
    width = bar_width * len(pattern)
    color, bg = (0, 0, 0), (255, 255, 255)
    im = Image.new('RGB', (width, height), bg)
    ld = im.load()
    for i, bar in enumerate(pattern):
        if bar == '1':
            for y in range(height):
                for x in range(i * bar_width, (i + 1) * bar_width):
                    ld[x, y] = color
    return im

>>> im = code128_img('AM-H-10-01-1')
>>> im.save(r'c:\temp\temp.pdf')

【讨论】:

我没有使用 PIL 库的原因是因为我想生成除条形码之外的文本。我觉得好像 matplotlib 库会更适合“绘图”,就像在画布上绘图一样。首先,我想生成这种画布,然后用条形码和随附的文本等填充它。或者这也可以用 PIL 来实现? @Sam 显然我不知道你想要做什么,所以我不能对你的问题的其他部分说任何有用的东西。我知道 PIL 可以将文本放入图像中,但这不是一回事,例如,您将无法选择 PDF 中的文本。我会根据您的评论查看更多面向文档的内容,但我不知道那会是什么。

以上是关于绘制条形码在绘图窗口中的显示方式与在保存的 .pdf 中不同的主要内容,如果未能解决你的问题,请参考以下文章

应用 pandas groupby 后,在绘图中添加条形以显示平均值

用户在 UIImageView 中绘制内容后,如何将绘图保存在 tableview 中?

python绘图篇

如何以熊猫方式绘制堆叠条形图?

保存绘图时更改Matplotlib图窗口的分辨率?

gjs cairo 上下文绘图未显示在杂乱的窗口中