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
<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.
<?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:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<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 )" />
<barcode:barcode xmlns:barcode="http://barcode4j.krysalis.org/ns" orientation="0">
<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
# 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
# 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
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