Note: Note to anyone reading this that knows XSLT/XML. How do I get the below XSL to loop twice so it creates two label pages for each node of this for-each loop <xsl:for-each select="DETAIL/SHIPMENT/STORE/TARE"> WITHOUT duplicating the entire tree of XSLT? Comment below if you can share the secret. (I just don't know how to implement a recursive template)
MessageXchange provide EDI services for a number of Australian Supermarkets
If you are doing a budget implementation you can use their FormXchange product which will help you implement with a super market by giving you off the shelf PO, POA, ASN & INV electronic documents.
The MessageXchange interface is a little like an email web client you have an inbox and an outbox and the ability to print SSCC Pallet Logistics labels
Along with the ability to send and receive EDI documents they also provide for each document type the ability to export the document in XML format.
For example if you don't like the format of their SSCC Label print you can use the XML ASN document to create your own
Using Apache FOP and Barcode4J to Format Your Own Labels
Here is an example I have been working on
This is a 105 x 148mm (A6) label
The stripped down XML of the ASN which the SSCC Logistics label is printed from I have removed everything I'm not referencing in the label
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | < MXCASN > < HEADER > < FROMBUSS >The Toggen Partnership</ FROMBUSS > </ HEADER > < DETAIL > < SHIPMENT > < STORE > < STORECODE >TGN123</ STORECODE > < TARE > < PACK > < SSCC >00999999999000000245</ SSCC > < LINGRP > < LIN > < EAN >19999999999762</ EAN > </ LIN > < IMD disable-output-escaping = "yes" >TOGGEN DUNE HARVESTER OIL</ IMD > < QTY > < TOTALSHIPQUANTITY >48</ TOTALSHIPQUANTITY > </ QTY > < ASNSTATUS /> < DTM > < BESTBEFOREDATE >20220802</ BESTBEFOREDATE > </ DTM > < RFF > < BATCHNO >212301</ BATCHNO > </ RFF > </ LINGRP > </ PACK > </ TARE > < TARE > < PACK > < SSCC >00999999999000000252</ SSCC > < LINGRP > < LIN > < EAN >19999999993678</ EAN > </ LIN > < IMD disable-output-escaping = "yes" >TOGGEN GOLD STANDARD 750ML XXX</ IMD > < QTY > < TOTALSHIPQUANTITY >128</ TOTALSHIPQUANTITY > </ QTY > < ASNSTATUS /> < DTM > < BESTBEFOREDATE >20220802</ BESTBEFOREDATE > </ DTM > < RFF > < BATCHNO >212301</ BATCHNO > </ RFF > </ LINGRP > </ PACK > </ TARE > < TARE > < PACK > < SSCC >00999999999000000269</ SSCC > < LINGRP > < LIN > < EAN >19999999992763</ EAN > </ LIN > < IMD disable-output-escaping = "yes" >TOGGEN GOLDEN PEEL OIL 750ML</ IMD > < QTY > < TOTALSHIPQUANTITY >145</ TOTALSHIPQUANTITY > </ QTY > < ASNSTATUS /> < DTM > < BESTBEFOREDATE >20220802</ BESTBEFOREDATE > </ DTM > < RFF > < BATCHNO >212301</ BATCHNO > </ RFF > </ LINGRP > </ PACK > </ TARE > </ STORE > </ SHIPMENT > </ DETAIL > < SUMMARY > </ SUMMARY > </ MXCASN > |
Here is the XSLT used to transform the ASN XML into a xml-fo for use by Apache FOP and Barcode4j to create a PDF label
Some features of this XSL
- Barcode4J has the ability to recaculate a check digit so the sample below strips the check-digit and re-adds it.
- Also an example of embedding a FNC1 in a variable length barcode field
- Using two variables FONT_SIZE and FONT_SIZE_LABELS to set the human-readable barcode data and the label sizes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | <? xml version = "1.0" encoding = "ISO-8859-1" ?> < xsl:stylesheet version = "1.1" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" xmlns:fo = "http://www.w3.org/1999/XSL/Format" exclude-result-prefixes = "fo" > < xsl:output method = "xml" version = "1.0" omit-xml-declaration = "no" indent = "yes" /> < xsl:param name = "versionParam" select = "'1.0'" /> <!-- ========================= --> <!-- root element: projectteam --> <!-- ========================= --> < xsl:template match = "MXCASN" > < xsl:variable name = "STORECODE" select = "DETAIL/SHIPMENT/STORE/STORECODE" /> < xsl:variable name = "COMPANY" select = "HEADER/FROMBUSS" /> < fo:layout-master-set > < fo:simple-page-master master-name = "first" page-width = "105mm" page-height = "148mm" margin-top = "0mm" margin-bottom = "0mm" margin-left = "0mm" margin-right = "0mm" > < fo:region-body writing-mode = "lr-tb" /> <!-- <fo:region-after extent="0mm" /> --> </ fo:simple-page-master > </ fo:layout-master-set > < fo:page-sequence master-reference = "first" > < fo:flow flow-name = "xsl-region-body" font-size = "12pt" > < xsl:for-each select = "DETAIL/SHIPMENT/STORE/TARE" > < xsl:variable name = "FONT_SIZE" select = "'26pt'" /> < xsl:variable name = "FONT_SIZE_LABELS" select = "'11pt'" /> < xsl:variable name = "SSCC" select = "PACK/SSCC" /> < xsl:variable name = "EAN" select = "PACK/LINGRP/LIN/EAN" /> < xsl:variable name = "BATCH" select = "PACK/LINGRP/RFF/BATCHNO" /> <!-- 20220802 --> < xsl:variable name = "FULL_DATE" select = "PACK/LINGRP/DTM/BESTBEFOREDATE" /> < xsl:variable name = "QTY" select = "PACK/LINGRP/QTY/TOTALSHIPQUANTITY" /> < xsl:variable name = "YEAR" select = "substring($FULL_DATE, 3,2 )" /> < xsl:variable name = "MONTH" select = "substring($FULL_DATE, 5,2)" /> < xsl:variable name = "DAY" select = "substring($FULL_DATE, 7,2 )" /> < fo:block-container > < fo:block text-align = "center" font-family = "Helvetica" font-size = "18pt" margin-top = "2pt" > < xsl:value-of select = "$COMPANY" /> </ fo:block > < fo:block text-align = "left" margin-left = "3mm" font-family = "Helvetica" font-size = "10pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > SSCC </ fo:block > < fo:block text-align = "left" margin-left = "3mm" font-family = "Helvetica" font-size = "24pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE" /> </ xsl:attribute > < xsl:value-of select = "substring($SSCC, 3)" /> </ fo:block > < fo:block text-align = "left" margin-left = "3mm" font-family = "Helvetica" font-size = "14pt" space-after = "8pt" > < xsl:value-of select = "PACK/LINGRP/IMD" /> </ fo:block > < fo:table width = "10cm" table-layout = "fixed" margin-left = "1.5mm" > < fo:table-column column-width = "6.7cm" /> < fo:table-column column-width = "3.2cm" /> < fo:table-body font-family = "sans-serif" font-weight = "normal" font-size = "10pt" > < fo:table-row > < fo:table-cell > < fo:block text-align = "start" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > ITEM NO </ fo:block > </ fo:table-cell > < fo:table-cell > < fo:block text-align = "end" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > QUANTITY </ fo:block > </ fo:table-cell > </ fo:table-row > < fo:table-row > < fo:table-cell > < fo:block text-align = "start" font-size = "26pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE" /> </ xsl:attribute > < xsl:value-of select = "$EAN" /> </ fo:block > </ fo:table-cell > < fo:table-cell > < fo:block text-align = "end" font-size = "10pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE" /> </ xsl:attribute > < xsl:value-of select = "$QTY" /> </ fo:block > </ fo:table-cell > </ fo:table-row > </ fo:table-body > </ fo:table > < fo:table width = "10cm" table-layout = "fixed" margin-left = "1.5mm" > < fo:table-column column-width = "5cm" /> < fo:table-column column-width = "3.0cm" /> < fo:table-column column-width = "2cm" /> < fo:table-body font-family = "sans-serif" font-weight = "normal" font-size = "10pt" > < fo:table-row > < fo:table-cell > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > < fo:block text-align = "start" >BEST BEFORE (ddmmyy)</ fo:block > </ fo:table-cell > < fo:table-cell > < fo:block text-align = "center" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > BATCH </ fo:block > </ fo:table-cell > < fo:table-cell > < fo:block text-align = "end" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > DC </ fo:block > </ fo:table-cell > </ fo:table-row > < fo:table-row > < fo:table-cell > < fo:block text-align = "start" font-size = "24pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE" /> </ xsl:attribute > < xsl:value-of select = "concat($DAY,'.' , $MONTH,'.' , $YEAR)" /> </ fo:block > </ fo:table-cell > < fo:table-cell > < fo:block text-align = "center" font-size = "22pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE" /> </ xsl:attribute > < xsl:value-of select = "$BATCH" /> </ fo:block > </ fo:table-cell > < fo:table-cell vertical-align = "top" > < fo:block text-align = "end" font-size = "16pt" > < xsl:attribute name = "font-size" > < xsl:value-of select = "$FONT_SIZE_LABELS" /> </ xsl:attribute > < xsl:value-of select = "$STORECODE" /> </ fo:block > </ fo:table-cell > </ fo:table-row > </ fo:table-body > </ fo:table > < fo:block line-height = "1pt" text-align = "center" left = "0mm" space-before = "0pt" space-after = "10pt" padding = "0pt" > < fo:leader line-height = "1pt" leader-pattern = "rule" rule-thickness = "1pt" leader-length = "100%" /> </ fo:block > < fo:block text-align = "center" > < fo:instream-foreign-object > < xsl:variable name = "PAD_QTY" select = "format-number($QTY, '00')" /> < xsl:variable name = "BARCODE" select = "concat('02' , substring($EAN,1,13) ,'ð37' , $PAD_QTY, 'ñ15' , $YEAR , $MONTH , $DAY, '10', $BATCH )" /> < xsl:message > < xsl:value-of select = "$BARCODE" /> </ xsl:message > < xsl:attribute name = "message" > < xsl:value-of select = "$BARCODE" /> </ xsl:attribute > < barcode:ean-128 > < barcode:check-digit-marker >ð</ barcode:check-digit-marker > < barcode:module-width >0.32mm</ barcode:module-width > < barcode:template >(02)n13+cd(37)n1-8(15)n6(10)an1-20</ barcode:template > < barcode:height >36mm</ barcode:height > < barcode:human-readable > < barcode:placement >bottom</ barcode:placement > < barcode:font-size >10pt</ barcode:font-size > </ barcode:human-readable > </ barcode:ean-128 > </ barcode:barcode > </ fo:instream-foreign-object > </ fo:block > < fo:block text-align = "center" space-after = "0pt" > < fo:instream-foreign-object > < barcode:barcode xmlns:barcode = "http://barcode4j.krysalis.org/ns" message = "REPLACEDBYATTRIBUTE" orientation = "0" > < xsl:attribute name = "message" > <!-- strip the checkdigit and calc for ourselves 'ð' is the char that barcode4j replaces with its own checkdigit --> < xsl:value-of select = "concat(substring($SSCC,1,19), 'ð')" /> </ xsl:attribute > < barcode:ean-128 > < barcode:module-width >0.49mm</ barcode:module-width > < barcode:template >(00)n17+cd</ barcode:template > < barcode:height >36mm</ barcode:height > < barcode:human-readable > < barcode:font-size >10pt</ barcode:font-size > </ barcode:human-readable > </ barcode:ean-128 > </ barcode:barcode > </ fo:instream-foreign-object > </ fo:block > </ fo:block-container > </ xsl:for-each > </ fo:flow > </ fo:page-sequence > </ fo:root > </ xsl:template > </ xsl:stylesheet > |
Save the above two files as asn-sample.xml
and bc2fo.xsl
and then run them using Apache fop with Barcode4j jars added to it's lib/ directory (see below for instructions on integrating barcode4j with FOP)
Run the following command from the fop/ directory e.g. the folder that has the fop, fop.bat, fop.cmd, fop.js
files in it. In my install look under fop-2.6-bin\fop-2.6\fop
1 2 3 4 5 6 | # one step fop -xml asn-sample.xml -xsl bc2fo.xsl output.pdf # two step generate xml-fo and then create pdf using fop. xsltproc bc2fo.xsl asn-sample.xml > bc .fo fop bc .fo -pdf out1.pdf |
And here is a screenshot of the output. In the XML above it creates one label for each TARE node.

To get Apache FOP and Barcode4j working together
Install OpenJDK
You need java. These are the versions that I'm using on both Windows and Linux
On Windows
java -version
openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment Microsoft-25199 (build 11.0.12+7)
OpenJDK 64-Bit Server VM Microsoft-25199 (build 11.0.12+7, mixed mode)
On Ubuntu Linux WSL
java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)
On Ubuntu Linux
java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (build 1.8.0_292-8u292-b10-0ubuntu1~20.04-b10)
OpenJDK 64-Bit Server VM (build 25.292-b10, mixed mode)
Download Barcode4J and Apache FOP
barcode4j-2.1.0-bin from https://sourceforge.net/projects/barcode4j/files/barcode4j/
fop-2.6-bin from http://xmlgraphics.apache.org/fop/download.html
unpack fop-2.6-bin and barcode4j-2.1.0-bin
Integrate barcode4j with Apache FOP
Copy the following two jars from barcode4j into the Apache FOP lib/ directory
- avalon-framework-4.2.0.jar
- barcode4j-fop-ext-complete.jar
FInd the above two files in the \barcode4j-2.1.0\barcode4j-2.1.0\lib
directory copy them to the fop-2.6-bin\fop-2.6\fop\lib
directory
Depending on your zip program you may have some more parent folders to navigate down but this gives you enough to know where and what to copy to and from.
Print FOP Output using Acrobat Reader on Windows
1 2 3 4 5 | # create the PDF cmd /c fop -xsl toggen\bc2fo.xsl -xml toggen\asn-sample.xml toggen\barcode%1.pdf # send it to the printer using Acrobat Reader start "" "C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" /t toggen\barcode%1.pdf "CAB A6+/300" "CAB A6+/300" "192.168.0.27" |
Arguments for AcroRd32.exe are
/t <pathto/pdf_file.pdf> <printer-name> <printer-driver> <portname>
To get the last three arguments use Powershell
1 2 3 4 5 6 7 8 9 10 11 12 | PS C:\Users\rudolph> Get-Printer | select Name, DriverName,PortName Name DriverName PortName ---- ---------- -------- OneNote for Windows 10 Microsoft Software Printer Driver Microsoft.Office.OneNote_16001.14326.20018.0_x64__8w… OneNote (Desktop) Send to Microsoft OneNote 16 Driver nul: Microsoft XPS Document Writer Microsoft XPS Document Writer v4 PORTPROMPT: Microsoft Print to PDF Microsoft Print To PDF PORTPROMPT: HP Officejet Pro 8620 HP Officejet Pro 8620 10.19.80.102 Fax Microsoft Shared Fax Driver SHRFAX: CutePDF Writer CutePDF Writer v4.0 CPW4: CAB A6+/300 CAB A6+/300 192.168.0.27 |
0 Comments