diff --git a/examples/upc-httpd.py b/examples/upc-httpd.py
index dfe80808..6d3aa59e 100755
--- a/examples/upc-httpd.py
+++ b/examples/upc-httpd.py
@@ -1,170 +1,203 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
- This program is free software; you can redistribute it and/or modify
- it under the terms of the Revised BSD License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Revised BSD License for more details.
-
- Copyright 2011-2023 Game Maker 2k - https://github.com/GameMaker2k
- Copyright 2011-2023 Kazuki Przyborowski - https://github.com/KazukiPrzyborowski
-
- $FileInfo: httpd.py - Last Update: 11/15/2024 Ver. 2.12.0 RC 1 - Author: cooldude2k $
-'''
-
+from __future__ import print_function, unicode_literals
import tempfile
import uuid
-import re
import os
import sys
import cherrypy
import upcean
import argparse
-import time
-import datetime
-from PIL import Image, ImageDraw, ImageFont
+from PIL import Image
+
+# Handle StringIO for Python 2 and 3
try:
- from io import StringIO, BytesIO
+ from io import BytesIO
except ImportError:
- try:
- from cStringIO import StringIO
- from cStringIO import StringIO as BytesIO
- except ImportError:
- from StringIO import StringIO
- from StringIO import StringIO as BytesIO
+ from StringIO import StringIO as BytesIO
+
+# Dynamically gather supported image types
+supported_image_formats = [
+ ext.lstrip('.') for ext, fmt in Image.registered_extensions().items()
+]
+
+# Enhanced argument parser
parser = argparse.ArgumentParser(
- description="A web server that draws barcodes with PyUPC-EAN powered by CherryPy web server.")
-parser.add_argument("--port", "--port-number", default=8080,
- help="port number to use for server.")
-parser.add_argument("--host", "--host-name",
- default="127.0.0.1", help="host name to use for server.")
-parser.add_argument("--verbose", "--verbose-mode",
- help="show log on terminal screen.", action="store_true")
-parser.add_argument("--gzip", "--gzip-mode",
- help="enable gzip http requests.", action="store_true")
-parser.add_argument("--gzipfilter", "--gzipfilter-mode",
- help="enable gzipfilter mode.", action="store_true")
-parser.add_argument("--accesslog", "--accesslog-file",
- help="location to store access log file.")
-parser.add_argument("--errorlog", "--errorlog-file",
- help="location to store error log file.")
-parser.add_argument("--timeout", "--response-timeout", default=6000,
- help="the number of seconds to allow responses to run.")
-parser.add_argument("--environment", "--server-environment", default="production",
- help="The server.environment entry controls how CherryPy should run.")
+ description="PyUPC-EAN Barcode Generator Web Server using CherryPy."
+)
+parser.add_argument(
+ "--port", "-p", default=8080, type=int,
+ help="Port number to use for the server. Default is 8080."
+)
+parser.add_argument(
+ "--host", "-H", default="127.0.0.1",
+ help="Host name or IP address to use for the server. Default is 127.0.0.1."
+)
+parser.add_argument(
+ "--timeout", "-t", default=6000, type=int,
+ help="Response timeout in seconds. Default is 6000."
+)
+parser.add_argument(
+ "--gzip", "-g", action="store_true",
+ help="Enable gzip compression for HTTP responses."
+)
+parser.add_argument(
+ "--log-access", "-a", default="./access.log",
+ help="File to store access logs. Default is './access.log'."
+)
+parser.add_argument(
+ "--log-error", "-e", default="./error.log",
+ help="File to store error logs. Default is './error.log'."
+)
+parser.add_argument(
+ "--environment", "-E", default="production",
+ choices=["production", "development"],
+ help="Set the server environment mode. Default is 'production'."
+)
+parser.add_argument(
+ "--verbose", "-v", action="store_true",
+ help="Enable verbose logging to the terminal."
+)
+parser.add_argument(
+ "--debug", "-d", action="store_true",
+ help="Enable debug mode for troubleshooting."
+)
+parser.add_argument(
+ "--output-dir", "-o", default=tempfile.gettempdir(),
+ help="Directory to store temporary output files. Default is the system's temp directory."
+)
+parser.add_argument(
+ "--max-size", "-m", type=int, default=5,
+ help="Maximum size multiplier for barcode generation (e.g., 5x). Default is 5."
+)
+parser.add_argument(
+ "--default-format", "-f", default="png",
+ choices=supported_image_formats,
+ help="Default output image format if none is specified. Default is 'png'."
+)
+parser.add_argument(
+ "--rotate-only", "-r", action="store_true",
+ help="Restrict barcode generation to only apply rotation."
+)
+
getargs = parser.parse_args()
-if(getargs.port is not None):
- port = int(getargs.port)
-else:
- port = 8080
-if(getargs.host is not None):
- host = str(getargs.host)
-else:
- host = "127.0.0.1"
-if(getargs.timeout is not None):
- timeout = int(getargs.timeout)
-else:
- timeout = 6000
-if(getargs.accesslog is not None):
- accesslog = str(getargs.accesslog)
-else:
- accesslog = "./access.log"
-if(getargs.errorlog is not None):
- errorlog = str(getargs.errorlog)
-else:
- errorlog = "./errors.log"
-if(getargs.environment is not None):
- serv_environ = str(getargs.environment)
-else:
- serv_environ = "production"
-pro_app_name = "Barcode Generator 2k"
-pro_app_subname = "(PyUPC-EAN)"
-pro_app_version = upcean.__version__
-radsta = 0
-radmax = 360
-radinc = 5
-radout = "\n"
-while(radsta <= radmax):
- if(radsta == 0):
- radout += "\n"
- if(radsta > 0):
- radout += "\n"
- radsta = radsta + radinc
-ServerSignature = "
PyUPC-EAN/%s (CherryPy/%s)" % (
- upcean.__version__, cherrypy.__version__)
-IndexHTMLCode = "\n" \
- "\n" \
- "\n" \
- " "+pro_app_name+" "+pro_app_subname+" " \
- "\n" \
- "\n" \
- "\n" \
- "\n" \
- "" \
- "\n" \
- "\n" \
- "\n" \
- "\n" \
- "\n" \
- "
\n" \
- ""+ServerSignature+"\n" \
- "\n" \
- ""
+# Set server configurations
+port = getargs.port
+host = getargs.host
+timeout = getargs.timeout
+accesslog = getargs.log_access
+errorlog = getargs.log_error
+serv_environ = getargs.environment
+debug_mode = getargs.debug
+output_dir = getargs.output_dir
+max_size = getargs.max_size
+default_format = getargs.default_format
+rotate_only = getargs.rotate_only
+
+# Generate dynamic options for the form
+size_options = "".join(
+ ''.format(i) for i in range(1, max_size + 1)
+)
+image_type_options = "".join(
+ ''.format(fmt, fmt.upper()) for fmt in supported_image_formats
+)
+
+IndexHTMLCode = """
+
+
+ Barcode Generator
+
+
+
+
+
+
+
+
+
+
Barcode Generator
+
+
+
+
+""".format(size_options, 'readonly' if rotate_only else '', image_type_options)
class GenerateIndexPage(object):
def index(self):
@@ -172,193 +205,58 @@ def index(self):
return IndexHTMLCode
index.exposed = True
- def generate(self, bctype, bcsize, bcrotate, imgtype):
- if(sys.version[0] == "2"):
- imgdata = StringIO()
- if(sys.version[0] >= "3"):
- imgdata = BytesIO()
- try:
- bctype
- except KeyError:
- bctype = "upca"
- try:
- bcsize
- except KeyError:
- bcsize = 1
- try:
- imgtype
- except KeyError:
- imgtype = "png"
- upc = os.path.splitext(imgtype)[0]
- try:
- bcrotate
- except KeyError:
- bcrotate = 0
- try:
- upc
- except KeyError:
- upc = None
- file_ext = upcean.predraw.getsfname.get_save_filename(tempfile.gettempdir(
- )+os.sep+"temp_"+str(uuid.uuid4()).replace("-", "")+imgtype.lower())
- if(file_ext[1] == "PNG"):
- cherrypy.response.headers['Content-Type'] = "image/png"
- if(file_ext[1] == "GIF"):
- cherrypy.response.headers['Content-Type'] = "image/gif"
- if(file_ext[1] == "JPEG"):
- cherrypy.response.headers['Content-Type'] = "image/jpeg"
- if(file_ext[1] == "BMP"):
- cherrypy.response.headers['Content-Type'] = "image/bmp"
- if(file_ext[1] == "PS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "EPS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "PPM"):
- cherrypy.response.headers['Content-Type'] = "image/x-portable-pixmap"
- if(file_ext[1] == "TIFF"):
- cherrypy.response.headers['Content-Type'] = "image/tiff"
- if(upc is not None and (int(bcrotate) == 0 or bcrotate is None)):
- if(bctype.lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(
- bctype.lower(), upc, int(bcsize))[1].save(imgdata, file_ext[1])
- if(upc is not None and (int(bcrotate) > 0 or int(bcrotate) < 0)):
- if(bctype.lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(bctype.lower(), upc, int(bcsize))[1].rotate(
- int(bcrotate), Image.BICUBIC, True).save(imgdata, file_ext[1])
- if(upc is not None):
- imgdata.seek(0)
- return imgdata.read()
- generate.exposed = True
+ def generate(self, bctype, bcsize, bcrotate, upc_imgtype):
+ imgdata = BytesIO()
+ # Split UPC and image type
+ if '.' in upc_imgtype:
+ upc, imgtype = upc_imgtype.rsplit('.', 1)
+ else:
+ raise cherrypy.HTTPRedirect("/", 303)
-class GenerateBarcodes(object):
- def index(self, **params):
- if(sys.version[0] == "2"):
- imgdata = StringIO()
- if(sys.version[0] >= "3"):
- imgdata = BytesIO()
- try:
- params['bctype']
- except KeyError:
- params['bctype'] = "upca"
- try:
- params['size']
- except KeyError:
- params['size'] = 1
- try:
- params['imgtype']
- except KeyError:
- params['imgtype'] = "png"
- try:
- params['rotate']
- except KeyError:
- params['rotate'] = 0
- try:
- params['upc']
- except KeyError:
- params['upc'] = None
- file_ext = upcean.predraw.getsfname.get_save_filename(tempfile.gettempdir(
- )+os.sep+"temp_"+str(uuid.uuid4()).replace("-", "")+params['imgtype'].lower())
- if(file_ext[1] == "PNG"):
- cherrypy.response.headers['Content-Type'] = "image/png"
- if(file_ext[1] == "GIF"):
- cherrypy.response.headers['Content-Type'] = "image/gif"
- if(file_ext[1] == "JPEG"):
- cherrypy.response.headers['Content-Type'] = "image/jpeg"
- if(file_ext[1] == "BMP"):
- cherrypy.response.headers['Content-Type'] = "image/bmp"
- if(file_ext[1] == "PS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "EPS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "PPM"):
- cherrypy.response.headers['Content-Type'] = "image/x-portable-pixmap"
- if(file_ext[1] == "TIFF"):
- cherrypy.response.headers['Content-Type'] = "image/tiff"
- if(params['upc'] is not None and (int(params['rotate']) == 0 or params['rotate'] is None)):
- if(params['bctype'].lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(params['bctype'].lower(
- ), params['upc'], int(params['size']))[1].save(imgdata, file_ext[1])
- if(params['upc'] is not None and (int(params['rotate']) > 0 or int(params['rotate']) < 0)):
- if(params['bctype'].lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(params['bctype'].lower(), params['upc'], int(
- params['size']))[1].rotate(int(params['rotate']), Image.BICUBIC, True).save(imgdata, file_ext[1])
- if(params['upc'] is not None):
- imgdata.seek(0)
- return imgdata.read()
- if(params['upc'] is None):
- cherrypy.response.headers['Content-Type'] = 'text/html; charset=UTF-8'
- return IndexHTMLCode
- index.exposed = True
+ # Default values and validations
+ bcsize = int(bcsize) if bcsize.isdigit() and int(bcsize) <= max_size else 1
+ bcrotate = int(bcrotate) if bcrotate.isdigit() else 0
+
+ # Restrict to rotation-only mode if enabled
+ if rotate_only and bcrotate == 0:
+ raise cherrypy.HTTPRedirect("/", 303)
+
+ # Check if barcode type is supported
+ if bctype not in upcean.support.supported_barcodes("tuple"):
+ raise cherrypy.HTTPRedirect("/", 303)
- def generate(self, bctype, bcsize, bcrotate, imgtype):
- if(sys.version[0] == "2"):
- imgdata = StringIO()
- if(sys.version[0] >= "3"):
- imgdata = BytesIO()
- try:
- bctype
- except KeyError:
- bctype = "upca"
- try:
- bcsize
- except KeyError:
- bcsize = 1
- try:
- imgtype
- except KeyError:
- imgtype = "png"
- upc = os.path.splitext(imgtype)[0]
- try:
- bcrotate
- except KeyError:
- bcrotate = 0
- try:
- upc
- except KeyError:
- upc = None
- file_ext = upcean.getsfname.get_save_filename(tempfile.gettempdir(
- )+os.sep+"temp_"+str(uuid.uuid4()).replace("-", "")+imgtype.lower())
- if(file_ext[1] == "PNG"):
- cherrypy.response.headers['Content-Type'] = "image/png"
- if(file_ext[1] == "GIF"):
- cherrypy.response.headers['Content-Type'] = "image/gif"
- if(file_ext[1] == "JPEG"):
- cherrypy.response.headers['Content-Type'] = "image/jpeg"
- if(file_ext[1] == "BMP"):
- cherrypy.response.headers['Content-Type'] = "image/bmp"
- if(file_ext[1] == "PS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "EPS"):
- cherrypy.response.headers['Content-Type'] = "application/postscript"
- if(file_ext[1] == "PPM"):
- cherrypy.response.headers['Content-Type'] = "image/x-portable-pixmap"
- if(file_ext[1] == "TIFF"):
- cherrypy.response.headers['Content-Type'] = "image/tiff"
- if(upc is not None and (int(bcrotate) == 0 or bcrotate is None)):
- if(params['bctype'].lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(
- params['bctype'].lower(), upc, int(bcsize))[1].save(imgdata, file_ext[1])
- if(upc is not None and (int(bcrotate) > 0 or int(bcrotate) < 0)):
- if(params['bctype'].lower() in upcean.support.supported_barcodes("tuple")):
- upcean.encode.validate_draw_barcode(params['bctype'].lower(), upc, int(
- bcsize))[1].rotate(int(bcrotate), Image.BICUBIC, True).save(imgdata, file_ext[1])
- if(upc is not None):
- imgdata.seek(0)
- return imgdata.read()
+ # Check if image format is supported
+ if imgtype.lower() not in supported_image_formats:
+ raise cherrypy.HTTPRedirect("/", 303)
+
+ # Generate the barcode
+ imgdraw = upcean.encode.validate_draw_barcode(bctype, upc, bcsize)
+
+ # Apply rotation if required
+ if bcrotate != 0:
+ imgdraw[1].rotate(bcrotate, Image.BICUBIC, expand=True).save(imgdata, imgtype.lower())
+ else:
+ imgdraw[1].save(imgdata, imgtype.lower())
+
+ # Set content type based on file extension
+ cherrypy.response.headers['Content-Type'] = Image.MIME.get(imgtype.lower(), 'image/png')
+ imgdata.seek(0)
+ return imgdata.read()
generate.exposed = True
-cherrypy.config.update({"environment": serv_environ,
- "log.error_file": errorlog,
- "log.access_file": accesslog,
- "log.screen": getargs.verbose,
- "gzipfilter.on": getargs.gzipfilter,
- "tools.gzip.on": getargs.gzip,
- "tools.gzip.mime_types": ['text/*'],
- "server.socket_host": host,
- "server.socket_port": port,
- "response.timeout": timeout,
- })
+# CherryPy configuration
+cherrypy.config.update({
+ "environment": serv_environ,
+ "log.error_file": errorlog,
+ "log.access_file": accesslog,
+ "log.screen": getargs.verbose,
+ "tools.gzip.on": getargs.gzip,
+ "server.socket_host": host,
+ "server.socket_port": port,
+ "response.timeout": timeout,
+})
+
cherrypy.root = GenerateIndexPage()
-cherrypy.root.upcean = GenerateBarcodes()
cherrypy.quickstart(cherrypy.root)