├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── bcp ├── __init__.py ├── fonts │ ├── OCRA.ttf │ └── README ├── models.py ├── settings.py ├── static │ └── js │ │ └── pdfobject.min.js ├── templates │ ├── embed_example.html │ └── print.html ├── tests.py ├── urls.py └── views.py ├── docs └── example.png └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | *.pyc 4 | *.egg-info 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014, Adlibre Pty Ltd. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Adlibre Pty Ltd nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Barcode Printer for Django 2 | 3 | This is a reusable Django application which generates PDF barcodes in various formats with a human and machine (OCR-A) readable textual label. 4 | 5 | These barcodes are rendered as a PDF with embedded JavaScript which prompts the PDF to be immediately printed. 6 | 7 | Currently this is used by [Adlibre DMS](http://www.adlibre.com.au/adlibre-dms/) for document barcode generation, 8 | but this application is generic and can be reused in any Django app that requires barcode printing. 9 | 10 | [ReportLab](http://www.reportlab.com) is used for the barcode generation. And the following formats are supported: 11 | 12 | * Code39 (3 of 9), 13 | * Code128. 14 | 15 | However it would be trivial to add support for any format that ReportLab supports. 16 | 17 | ## Example 18 | 19 | Barcode automatically printing from URL: 20 | 21 | ![Barcode Printer for Django - Automatic Printing](https://github.com/adlibre/django-bcp/raw/master/docs/example.png) 22 | 23 | ## Installation 24 | 25 | If you use pip then the dependencies and requirements will be taken care of: 26 | 27 | Prod: 28 | 29 | pip install git+git://github.com/adlibre/django-bcp.git 30 | 31 | Dev: 32 | 33 | pip install -e git+git@github.com:adlibre/django-bcp.git#egg=bcp 34 | 35 | ## N.B. ReportLab Bug 36 | 37 | ReportLab 2.5/2.6 has a typo. "OpenActions" should be "OpenAction" as per ISO 32000-1:2008. 38 | 39 |
40 | diff -r broken/lib/python2.7/site-packages/reportlab/pdfbase/pdfdoc.py fixed/lib/python2.7/site-packages/reportlab/pdfbase/pdfdoc.py
41 | 1022c1022
42 | <         Dests Outlines Pages Threads AcroForm Names OpenActions PageMode URI
43 | ---
44 | >         Dests Outlines Pages Threads AcroForm Names OpenAction PageMode URI
45 | 
46 | 47 | You will need to manually patch this, otherwise the barcode will not automatically print: 48 | 49 | sed -i -e 's@OpenActions@OpenAction@g' lib/python*/site-packages/reportlab/pdfbase/pdfdoc.py 50 | -------------------------------------------------------------------------------- /bcp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adlibre/django-bcp/6ce7dd35cfc9c09961b363bddd1e89a35d6196ae/bcp/__init__.py -------------------------------------------------------------------------------- /bcp/fonts/OCRA.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adlibre/django-bcp/6ce7dd35cfc9c09961b363bddd1e89a35d6196ae/bcp/fonts/OCRA.ttf -------------------------------------------------------------------------------- /bcp/fonts/README: -------------------------------------------------------------------------------- 1 | OCR-A Font from http://sourceforge.net/projects/ocr-a-font/ 2 | 3 | Provided under Public Domain license. 4 | -------------------------------------------------------------------------------- /bcp/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adlibre/django-bcp/6ce7dd35cfc9c09961b363bddd1e89a35d6196ae/bcp/models.py -------------------------------------------------------------------------------- /bcp/settings.py: -------------------------------------------------------------------------------- 1 | # Settings for barcode printer 2 | 3 | import os 4 | 5 | # Barcode 6 | BAR_HEIGHT = 30 7 | BAR_WIDTH = 0.75 # default is 0.54 8 | BAR_QUIET = False # include l/r whitespace padding. 9 | BAR_CHECKSUM = True 10 | 11 | # Label Config 12 | FONT_SIZE = 10 13 | FONT_NAME = 'OCRA' 14 | FONT_PATH = os.path.join(os.path.split(__file__)[0], 'fonts', 'OCRA.ttf',) 15 | 16 | # NB It's a better idea to put your settings in a local_settings.py overrides file. 17 | try: 18 | from local_settings import * 19 | except ImportError: 20 | pass -------------------------------------------------------------------------------- /bcp/static/js/pdfobject.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | PDFObject v1.2.20111123 3 | https://github.com/pipwerks/PDFObject 4 | Copyright (c) Philip Hutchison 5 | MIT-style license: http://pipwerks.mit-license.org/ 6 | */ 7 | var PDFObject=function(h){if(!h||!h.url){return false}var e="1.2",c=h.id||false,d=h.width||"100%",p=h.height||"100%",g=h.pdfOpenParams,a,m,l,b,j,i,n,o,q,f,k;l=function(r){var s;try{s=new ActiveXObject(r)}catch(t){s=null}return s};b=function(){var r=null;if(window.ActiveXObject){r=l("AcroPDF.PDF");if(!r){r=l("PDF.PdfCtrl")}if(r!==null){return true}}return false};j=function(){var r,u=navigator.plugins,s=u.length,t=/Adobe Reader|Adobe PDF|Acrobat/gi;for(r=0;r';return s.getElementsByTagName("object")[0]};a=encodeURI(h.url)+"#"+q(g);m=n();this.get=function(r){return f(r)};this.embed=function(r){return k(r)};this.pdfobjectversion=e;return this}; -------------------------------------------------------------------------------- /bcp/templates/embed_example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Print PDF 6 | 7 | 8 | 15 | 16 | 17 | 18 |

This is a test showing how we can dynamically request a barcode to be printed from our page.

19 |

URL: {{ bcp_url }}

20 | 21 |
22 | This content will be replaced 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /bcp/templates/print.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Print PDF 6 | {# TODO: FIXME This is still not working right in non chrome browsers... #} 7 | {# however probably not needed anymore, as we can force print the PDF using embedded JavaScript #} 8 | {% comment %} 9 | 10 | 11 | 12 | 35 | {% endcomment %} 36 | 37 | 38 | 39 | 40 | {% comment %} 41 | 42 | {% endcomment %} 43 | 44 |
45 | 46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /bcp/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module: Bar Code Printer Unit Tests 3 | Project: Django BCP 4 | Copyright: Adlibre Pty Ltd 2012 5 | License: See LICENSE for license information 6 | """ 7 | 8 | from django.core.urlresolvers import reverse 9 | from django.test import TestCase 10 | 11 | 12 | class BarCodeTest(TestCase): 13 | 14 | def setUp(self): 15 | """Test data""" 16 | # auth user 17 | self.username = 'admin' 18 | self.password = 'admin' 19 | 20 | self.barcode_test_data = [ 21 | # ('type', 'barcode'), 22 | ('Standard39', 'ABC1234'), 23 | ('Standard39', 'ABC-1234'), 24 | ('Standard39', '123456789'), 25 | ('Code128', 'ABC1234'), 26 | ('Code128', 'ABC-1234'), 27 | ('Code128', '123456789'), 28 | ] 29 | 30 | def test_generate_barcode(self): 31 | for barcode in self.barcode_test_data: 32 | url = reverse('bcp-generate', kwargs={'barcode_type': barcode[0], 'code': barcode[1], }) 33 | response = self.client.get(url) 34 | self.assertEqual(response.status_code, 200) 35 | 36 | def test_print_barcode(self): 37 | for barcode in self.barcode_test_data: 38 | url = reverse('bcp-print', kwargs={'barcode_type': barcode[0], 'code': barcode[1], }) 39 | response = self.client.get(url) 40 | self.assertEqual(response.status_code, 200) 41 | 42 | def test_embed_example(self): 43 | for barcode in self.barcode_test_data: 44 | url = reverse('bcp-embed-example', kwargs={'barcode_type': barcode[0], 'code': barcode[1], }) 45 | response = self.client.get(url) 46 | self.assertEqual(response.status_code, 200) 47 | self.assertContains(response, barcode[1]) -------------------------------------------------------------------------------- /bcp/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module: Barcode Printer URLS 3 | Project: Django BCP 4 | Copyright: Adlibre Pty Ltd 2012 5 | License: See LICENSE for license information 6 | """ 7 | try: 8 | from django.conf.urls.defaults import * 9 | except: 10 | from django.conf.urls import patterns, url 11 | 12 | #import mdtui.views 13 | 14 | urlpatterns = patterns('bcp.views', 15 | url(r'^(?P(Standard39|Code128))/(?P[\w-]+)$', 'generate', name='bcp-generate'), 16 | url(r'^(?P(Standard39|Code128))/(?P[\w-]+)/print$', 'print_barcode', name='bcp-print'), 17 | url(r'^(?P(Standard39|Code128))/(?P[\w-]+)/test', 'print_barcode_embed_example', name='bcp-embed-example'), 18 | 19 | ) -------------------------------------------------------------------------------- /bcp/views.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module: Barcode Printer Views 3 | Project: Django BCP 4 | Copyright: Adlibre Pty Ltd 2012 5 | License: See LICENSE for license information 6 | """ 7 | 8 | from django.http import HttpResponse 9 | from django.http import HttpResponseBadRequest 10 | from django.core.urlresolvers import reverse 11 | from django.shortcuts import render 12 | 13 | from django.conf import settings 14 | 15 | try: 16 | from cStringIO import StringIO 17 | except ImportError: 18 | from StringIO import StringIO 19 | 20 | 21 | def print_barcode_embed_example(request, code, barcode_type, template='embed_example.html'): 22 | """ 23 | This is a test page showing how you can embed a request to print a barcode 24 | """ 25 | bcp_url = reverse('bcp-print', kwargs = {'barcode_type': barcode_type, 'code': code,}) 26 | context = { 'bcp_url': bcp_url, } 27 | return render(request, template, context) 28 | 29 | 30 | def print_barcode(request, code, barcode_type, template='print.html'): 31 | """ 32 | This page causes the browser to request the barcode be printed 33 | """ 34 | pdf_url = reverse('bcp-generate', kwargs = {'barcode_type': barcode_type, 'code': code,}) 35 | context = { 'pdf_url': pdf_url, } 36 | return render(request, template, context) 37 | 38 | 39 | def generate(request, code, barcode_type='Standard39', auto_print=True): 40 | """ 41 | Returns a PDF Barcode using ReportLab 42 | """ 43 | 44 | from reportlab.graphics.shapes import String 45 | from reportlab.graphics import renderPDF 46 | from reportlab.graphics.barcode import createBarcodeDrawing 47 | from reportlab.pdfbase import pdfdoc 48 | from reportlab.pdfbase import pdfmetrics 49 | from reportlab.pdfbase.ttfonts import TTFont 50 | 51 | response = HttpResponse(mimetype='application/pdf') 52 | response['Content-Disposition'] = 'inline; filename=%s.pdf' % (code,) 53 | 54 | # Config 55 | import bcp.settings as bcp_settings 56 | font_size = bcp_settings.FONT_SIZE 57 | bar_height = bcp_settings.BAR_HEIGHT 58 | bar_width = bcp_settings.BAR_WIDTH 59 | font_name = bcp_settings.FONT_NAME 60 | font_path = bcp_settings.FONT_PATH 61 | try: 62 | # If this is extended to different barcode types, then these options will need to be specified differently, eg not all formats support checksum. 63 | bc = createBarcodeDrawing(barcode_type, barHeight=bar_height, barWidth=bar_width, value=str(code), isoScale=True, quiet=bcp_settings.BAR_QUIET, checksum=bcp_settings.BAR_CHECKSUM,) 64 | except KeyError, e: 65 | return HttpResponseBadRequest('Barcode Generation Failed: %s' % (e)) 66 | 67 | # Register the font 68 | pdfmetrics.registerFont(TTFont(font_name, font_path)) 69 | 70 | # Set JS to Autoprint document 71 | if auto_print: 72 | pdfdoc.PDFCatalog.OpenAction = '<>' 73 | pdfdoc.PDFInfo.title = code # nicety :) 74 | 75 | # Position for our text label 76 | x = bc.width / 2 77 | y = - font_size # or (bar_height + font_size) if placing on top 78 | # The textual barcode 79 | text = String(x, y, code, textAnchor='middle', fontName=font_name, fontSize=font_size) 80 | bc.add(text) 81 | bc = bc.resized() # resize barcode drawing object to accommodate text added 82 | 83 | buffer = StringIO() # buffer for the output 84 | renderPDF.drawToFile(bc, buffer, autoSize=1) # write PDF to buffer 85 | 86 | # Get the value of the StringIO buffer and write it to the response. 87 | pdf = buffer.getvalue() 88 | buffer.close() 89 | response.write(pdf) 90 | 91 | return response -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adlibre/django-bcp/6ce7dd35cfc9c09961b363bddd1e89a35d6196ae/docs/example.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | VERSION = '0.2.2' 6 | 7 | setup(name='django-bcp', 8 | version=VERSION, 9 | author="Andrew Cutler", 10 | author_email="andrew@adlibre.com.au", 11 | description="Barcode Printer for Django", 12 | license="BSD", 13 | long_description=open('README.md').read(), 14 | url='https://github.com/adlibre/django-bcp', 15 | packages=['bcp',], 16 | download_url='https://github.com/adlibre/python-bureaucrat/archive/v%s.tar.gz' % VERSION, 17 | classifiers=[ 18 | "Development Status :: 5 - Production/Stable", 19 | "Environment :: Console", 20 | "Intended Audience :: Developers", 21 | "License :: OSI Approved :: BSD License", 22 | "Operating System :: Unix", 23 | "Programming Language :: Python", 24 | "Topic :: Utilities", 25 | ], 26 | install_requires=[ 27 | 'django', 28 | 'reportlab>=2.6', 29 | ], 30 | package_data={'bcp': ['fonts/*', 'templates/*', ]}, 31 | ) 32 | --------------------------------------------------------------------------------