-
-
Notifications
You must be signed in to change notification settings - Fork 65
Developing a Frontend to BWIPP
There are a number of frontends to BWIPP that vary in terms of the functionality that they expose and the way that they express this through their API or GUI, etc.
It would be nice to unify some of these projects but in the meantime this document attempts to provide some guidelines to apply when developing something that places BWIPP in the hands of developers and users.
The author would ideally like any language binding, library or graphical frontend to be representative of the complete functionality of the BWIPP resource and to be maintainable with minimal effort and these guideline help to achieve this goal.
Contact the author of BWIPP whilst you're still experimenting. I will try not to insist on my own way as it's you that will end up supporting your creation so I want you to be happy with it, but it will help everyone if there is some consistency between your code and the next person's.
Author's commitment: If I know about your project then I will make a best efforts commitment to assist with end user support and developer support for any library or application that makes a genuine attempt to adopt the principles given here.
Be aware that we have produced a C library and language-specific bindings with a common API to help with manipulating the BWIPP resources: https://github.com/bwipp/postscriptbarcode/tree/master/libs
You should attempt to use these where possible as it takes most of the pain out of working with the PostScript. If the API doesn't support something that you need then we can extend the interface as necessary.
If you choose to work directly with the PostScript then it is better to parse the inline metadata rather than embedding a load of static data in your code.
You should support new barcode formats automatically by scanning the barcode.ps metadata for BEGIN/END ENCODER
blocks. From these extract descriptions, example data, options, etc. by using the DESC
, EXAM
, EXOP
, ... stanzas within the BEGIN/ENCODER ENCODER
blocks.
Example BWIPP metadata for an encoder:
% --BEGIN ENCODER ean8--
% --REQUIRES preamble raiseerror renlinear ean5 ean2--
% --DESC: EAN-8
% --EXAM: 02345673
% --EXOP: includetext guardwhitespace
% --RNDR: renlinear
... PostScript resource definition here ...
% --END ENCODER ean8--
The best strategy is for libraries and graphical frontends to be light on compiled-in data and can therefore be enhanced by simply replacing the barcode.ps file.
To fully meet this objective may require extending the barcode.ps metadata to describe the individual options that are available for each encoder. The BWIPP author is certainly interested in having such a discussion so please make contact regarding your requirements.
Whether part of your design or as a fall back, allow advanced users to specify the data, options and encoder directly. This will allow them to access BWIPP functionality that you haven't anticipated or chosen to expose via your API or GUI.
Use the BWIPP error reporting mechanism to provide specific error messages to users so that they can understand why a given input is invalid.
The preferred way to do this is to wrap the BWIPP invocation in a "stopped context" which allows you to handle BWIPP-specific exceptions. For example, the following will invoke BWIPP and on error will emit formatted, descriptive text of the error (e.g. BWIPP ERROR: EAN-13 must be 12 or 13 digits
) to STDERR which the calling program can recognise as an error and prompt the user:
{ % "try" BWIPP invocation
0 0 moveto (ABC) () /code39 /uk.co.terryburton.bwipp findresource exec
showpage
} stopped { % "catch" all exceptions
$error /errorname get dup length string cvs 0 6 getinterval (bwipp.) ne {
stop % Rethrow non-BWIPP exceptions
} if
% Handle BWIPP exceptions, e.g. emit formatted error to stderr
(%stderr) (w) file
dup (\nBWIPP ERROR: ) writestring
dup $error /errorname get dup length string cvs writestring
dup ( ) writestring
dup $error /errorinfo get dup length string cvs writestring
dup (\n) writestring
dup flushfile
} if
Less advised, but which may be useful in some circumstances, it is possible to override the PostScript VM's default handleerror procedure to recognise and take some special action when handling BWIPP-specific exceptions. For example, the following will invoke barcode.ps
and on error will emit formatted, descriptive text of the error (e.g. BWIPP ERROR: EAN-13 must be 12 or 13 digits
) to STDERR which the calling program can recognise as an error and prompt the user:
%!PS
errordict begin
/handleerror {
$error begin
errorname dup length string cvs 0 6 getinterval (bwipp.) eq {
(%stderr) (w) file
dup (\nBWIPP ERROR: ) writestring
dup errorname dup length string cvs writestring
dup ( ) writestring
dup errorinfo dup length string cvs writestring
dup (\n) writestring
dup flushfile end quit
} if
end //handleerror exec
} bind def
end
% If necessary, set up anything else specific to the environment just here.
% Include the BWIPP resource, either directly or from PS
(barcode.ps) run
% Now make the calls to BWIPP
0 0 moveto (ABC) () /code39 /uk.co.terryburton.bwipp findresource exec
Allow the location of the barcode.ps
file to be configured by the user so that non-admins users can provide a local version and distributions that deprecate bundled libraries can provide a separately packaged version.
In any case, use the following search order to locate the barcode.ps
resource:
[%USER_SPECIFIED_LOCATION%]
-
~/.[%APP_RC_DIRECTORY%]
(a user's own replacement) -
[%APP_INSTALL_DIR%]
(a version you have bundled) -
/usr/share/postscriptbarcode
(Fedora's postscriptbarcode package) -
/usr/share/libpostscriptbarcode
(Debian's libpostscriptbarcode package)
To make the presentation of the list of barcode formats manageable any such list of barcodes should be rendered in the same/similar way as the web-based generator.
Point your users at the online BWIPP symbologies and options references.
The reference is written these in a way that is intended to be fairly environment agnostic but if you have any ideas or want to improve them in some way then please contribute.
- https://github.com/bwipp/postscriptbarcode/wiki/Symbologies-Reference
- https://github.com/bwipp/postscriptbarcode/wiki/Options-Reference
Pass arguments to BWIPP in an injection-proof way that does not allow users to invoke arbitrary PostScript commands by means of un-escaped )
or otherwise.
The best way is to "hexify" the data, options and encoder string data in your output, for example:
0 0 moveto
<3032333435363733> <-- Instead of (02345673)
<696e636c75646574657874> <-- Instead of (includetext)
<65616e38> cvn <-- Instead of /ean8
/uk.co.terryburton.bwipp findresource exec
Example Python:
import binascii, textwrap
def hexify(input):
return textwrap.TextWrapper(subsequent_indent=' ', width=72). \
fill('<' + binascii.hexlify(string) + '>')
Example Perl:
sub hexify {
return '<'.(join "\n ", unpack '(A72)*', unpack 'H*', shift).'>';
}
Frontends often have a requirement to render an cropped image file (vector or bitmap) based on the PostScript description of the symbol.
One approach is to create an EPS file that includes a BoundingBox description. It is usual to derive the BoundingBox using a dedicated pass over the PostScript with GhostScript's "bbox" driver.
Start by populating a basic PostScript template from the user input:
%!PS
[% BWIPP_resources %]
0 0 moveto
[% hexify($DAT) %] [% hexify($OPT) %] [% hexify($ENC) %] cvn /uk.co.terryburton.bwipp findresource exec
showpage
Process the populated template (in.ps
) with the bbox
device to extract the bounding box with something like:
PAGE_OFFSET=3000
gs -dSAFER -dQUIET -dNOPAUSE -dBATCH -sDEVICE=bbox \
-c "<</PageOffset [$PAGE_OFFSET $PAGE_OFFSET]>> setpagedevice" -f in.ps
Note: PAGE_OFFSET
is an arbitrarily large value sufficient to force the entire symbol into the first quadrant due to deficiencies in GhostScript's bbox
driver.
The output will be of the form:
%%BoundingBox: <BB_X1> <BB_Y1> <BB_X2> <BB_Y2>
From this output, you can now derive the symbol dimensions and the translation that is required to draw from the coordinate system's origin:
WID = $BB_X2 - $BB_X1
HEI = $BB_Y2 - $BB_Y1
T_X = $PAGE_OFFSET - $BB_X1
T_Y = $PAGE_OFFSET - $BB_Y1
Finally, create an EPS file by populating a template such as the one below from the user input and the values derived above:
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 [% $WID %] [% $HEI %]
%%Pages: 1
%%LanguageLevel: 2
%%EndComments
%%BeginProlog
[% BWIPP_resources_with_DSC_comments %]
%%EndProlog
%%Page: 1 1
[% $T_X %] [% $T_Y %] moveto
[% hexify($DAT) %] [% hexify($OPT) %] [% hexify($ENC) %] cvn /uk.co.terryburton.bwipp findresource exec
showpage
%%Trailer
%%EOF
This EPS could now be rendered to a vector format such as PDF with something like:
gs -dSAFER -dQUIET -dNOPAUSE -dBATCH -sDEVICE=pdfwrite "-dDEVICEWIDTHPOINTS=$WID" "-dDEVICEHEIGHTPOINTS=$HEI" \
-sOutputFile=out.pdf in.eps
Or to a bitmap format such as PNG with something like:
gs -dSAFER -dQUIET -dNOPAUSE -dBATCH -sDEVICE=png16m "-dDEVICEWIDTHPOINTS=$WID" "-dDEVICEHEIGHTPOINTS=$HEI" -r288 \
-dTextAlphaBits=4 -dGraphicsAlphaBits=1 -sOutputFile=out.png in.eps
Home | Download | Documentation (PDF) | Source | Support | Issues | Online Barcode Generator