2
0
mirror of https://github.com/inventree/inventree-website.git synced 2025-04-27 21:16:45 +00:00
inventree-website/SergeoLacruz/inventree-zebra-plugin.html
2025-04-26 01:14:01 +00:00

553 lines
28 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang=" en-US ">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/assets/splide/css/splide.min.css">
<link rel="stylesheet" href="/assets/index.css">
<link rel="shortcut icon" type="image/png" href="/assets/icon/favicon.ico">
<script src="/assets/splide/js/splide.min.js"></script>
<!-- Fontawesome integration -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
<title>InvenTree - Inventree Zebra Plugin</title>
<meta itemprop="description" name="description"
content="InvenTree is an open-source inventory management system which provides intuitive parts management and stock control. It is at the center of an ecosystem of a..." />
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>Inventree Zebra Plugin | InvenTree</title>
<meta name="generator" content="Jekyll v4.3.3" />
<meta property="og:title" content="Inventree Zebra Plugin" />
<meta name="author" content="SergeoLacruz" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="Zebra Label Printer Plugin for Inventree" />
<meta property="og:description" content="Zebra Label Printer Plugin for Inventree" />
<link rel="canonical" href="/SergeoLacruz/inventree-zebra-plugin" />
<meta property="og:url" content="/SergeoLacruz/inventree-zebra-plugin" />
<meta property="og:site_name" content="InvenTree" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2025-04-26T01:13:50+00:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="Inventree Zebra Plugin" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"SergeoLacruz"},"dateModified":"2025-04-26T01:13:50+00:00","datePublished":"2025-04-26T01:13:50+00:00","description":"Zebra Label Printer Plugin for Inventree","headline":"Inventree Zebra Plugin","mainEntityOfPage":{"@type":"WebPage","@id":"/SergeoLacruz/inventree-zebra-plugin"},"url":"/SergeoLacruz/inventree-zebra-plugin"}</script>
<!-- End Jekyll SEO tag -->
</head>
<body class="flex flex-col antialiased cm-gray-1 min-h-screen">
<div class="flex-none">
<header class="cm-gray-2 body-font sticky top-0 z-50 bg-gradient-to-r from-white to-secondary">
<div class="container mx-auto flex flex-wrap p-5 flex-row items-center">
<a class="flex title-font font-medium items-center cm-gray-1 mb-0 mr-2" href="/">
<img src="/assets/logo.png" alt="logo" height="32" width="32" class="h-8">
<span class="ml-3 text-xl">InvenTree</span>
</a>
<div class="flex-grow xs:flex-none"></div>
<nav class="md:mr-auto md:py-1 xs:ml-4 xs:pl-4 xs:border-l xs:border-gray-400 flex flex-wrap items-center text-base justify-center">
<a class="mr-5 hover:cm-gray-1" href="/deploy.html">Deploy</a>
<a class="mr-5 hover:cm-gray-1" href="https://docs.inventree.org/en/stable/">Docs</a>
<a class="mr-5 hover:cm-gray-1" href="/blog">Blog</a>
</nav>
</div>
</header> <article class="max-w-p90">
<h2>inventree-zebra-plugin
<span class="sm:ml-2 text-sm">
<a href="/SergeoLacruz">
<span class="inline-flex">
<img src="https://github.com/SergeoLacruz.png?size=40" alt="mdo" class="self-center w-5 h-5 rounded-full" style="margin:0px">
<p> SergeoLacruz</p>
</span>
</a></span>
<span class="sm:ml-2 text-sm">last modified: 26 Apr 2025</span>
</h2>
<div class="flex-wrap md:flex md:flex-nowrap">
<div class="w-full md:w-auto md:mr-4">
<p>Zebra Label Printer Plugin for Inventree</p>
<p>This is a label printing plugin for <a href="https://inventree.org">InvenTree</a>, which provides
support for Zebra Label printers. It allows two modes of operation:</p>
<ul>
<li>Use the ZPL library to convert the png data provided by InvenTree to Zebras bitmap
format and send this to the printer.</li>
<li>Write a ZPL template and let the printer do the rendering.</li>
</ul>
<p>It can output the print data either to a local printer connected to the computer via
USB or to a network printer with an IP address. The output can be configured in the
InvenTree plugin user interface.</p>
<p>Error handling is very basic.</p>
<h2 id="installation">Installation</h2>
<p>The plugin is on pypi. Install this plugin using pip with the following command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install inventree-zebra-plugin
</code></pre></div></div>
<h2 id="configuration-options">Configuration Options</h2>
<h3 id="printer-interface">Printer Interface</h3>
<p>Here you can chose between local printer, network printer or the labelary.com API.
The last one is useful for preview of labels, especially when ZPL templates are used.
Default value is a local printer.</p>
<h3 id="ip-address">IP address</h3>
<p>In case you use an IP printer set the IPv4 address here.</p>
<h3 id="port">Port</h3>
<p>In case you use an IP printer set the port number here. The default port number is 9100.</p>
<h3 id="local-device">Local Device</h3>
<p>In case of a local printer set the device here. The plugin actually puts the data directly to the
device /dev/usb/lp0. No printer spooler is involved so far.</p>
<h3 id="threshold">Threshold</h3>
<p>The image from pillow comes in greyscale. The plugin converts it ti pure BW because this gives a much
better print result. The threshold between black and white can be adjusted here.</p>
<h3 id="darkness">Darkness</h3>
<p>This is a value that influences the darkness of the print. Allowed values are 0 (white) to 30 (black).
It is directly converted to a SD command in ZPL. If your black areas tend to blur out reduce the
darkness.</p>
<h3 id="dots-per-mm">Dots per mm</h3>
<p>This sets the resolution of the printer. You can choose between 8, 12 and 24
dpmm depending on your printer model.</p>
<h3 id="printer-init">Printer init</h3>
<p>This string added to the printer output. It can be used to set special commands
e.g. label rotation, mirror or white on black. Please refer to the ZPL manual
for more information.</p>
<p>Zebra printers store settings after printing. So if a rotated label has been
printed all following label will be rotated unless you change it. The default
sets the printer to settings that have been useful for me. You might want to
change it according to your requirements. Please keep in mind that this string
is passed directly to the printer without any checks. So be careful when editing
here.</p>
<h3 id="get-printer-info">Get Printer info</h3>
<p>Turn this switch on to display a collection of all IP printers below on this page.</p>
<h2 id="label-template">Label Template</h2>
<p>The label needs a template described in html and css. The template should
start with a page definition that defines the label size as shown below:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
@page {
{% localize off %}
height: {{ height }}mm;
width: {{ width }}mm;
{% endlocalize %}
padding: 0mm;
margin: 0px 0px 0px 0px;
background-color: white;
}
</code></pre></div></div>
<p>The height and width parameters are defined in the InvenTree admin panel
in the label section. These values have to fit the label size that is in
the printer. See the example templates for details on template definition.</p>
<h2 id="multi-printer-hack">Multi printer hack</h2>
<p>We have the requirement to print labels in different sizes. As we do not
want to change the reel for each print we set up a second printer loaded
with a different label size. InvenTree is not yet able to handle different
printers. So I added a multi printer hack. You can define a key with an IP
address in the label meta data:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"ip_address":"xxx.yyy.zzz.eee"}
{"darkness":xx}
</code></pre></div></div>
<p>If the printer driver finds that key, the IP address from the printer settings
is overwritten with the address from the meta data. So the print will end up
in another printer.</p>
<p>Only the IP address and darkness can be overwritten so far. All other settings remain.</p>
<h2 id="quality-matters">Quality matters</h2>
<p>The InvenTree printer system uses a graphical representation of the label. The label is described
in HTML, converted to a pixel graphic and printed. The advantage is independency from printer
models and systems. Disadvantage is larger data and quality problems with darkness and scaling.
Lets have a look at the following printout:</p>
<p><img src="/assets/plugins/qr.png" alt="QRCodes"></p>
<p>Both codes have been printed with the same printer on the same reel. The left one is
hardly readable using my mobile. The right one reads easily even as it is smaller.</p>
<h3 id="secret-1-scale">Secret 1, Scale</h3>
<p>The printer resolution is 8 dots per mm resulting in a dot size of 0.125mm. The QR code pixel
and the printer pixel size should be integrally divisible. The code in the picture has 21
pixels plus one in the frame, so 23 pixel. The frame is set in the HTML description.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% qrcode qr_data border=1 %}
</code></pre></div></div>
<p>I selected two dots per pixel. So 23 * 2 * 0.125 = 6.125mm. If the size is something different
scaling takes place and the result might be worse. If you like a larger printout select more
dots per pixel. From a certain size upwards the value does not matter any more because the code
gets large enough to be readable in any quality.</p>
<h3 id="secret-2-darkness">Secret 2: Darkness</h3>
<p>Zebra printers allow to set the darkness of the print in values between 0 (white) and 30 (max)
The left code was printed with a value of 30. The black dots tend to blur out a bit resulting
in smaller white areas. The right code was printed with a value of 25 resulting in larger white
pixels. The darkness values are just examples. Your values will differ based on printer model,
media type and printer age. The printer head tends to wear out and the darkness value might
need an adjustment from time to time.</p>
<h2 id="in-printer-rendering">In printer rendering</h2>
<p>You can also bypass the InvenTree print engine and render the label inside the printer.
The printer knows how to render the label for best quality. Inspired by the
inventree-zpl-plugin <a href="https://github.com/yellowcrescent/inventree-zpl-plugin">inventree-zpl-plugin</a> a similar
function was aded to the zebra printer driver. You can write a ZPL template and upload
it to the InvenTree Label templates as usual. Add a command to the templates metadata:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"zpl_template": "True"}
</code></pre></div></div>
<p>In that case the printer driver ignores the picture rendered by WeasyPrint. Instead
it calls the render_to_string function of the template and sends the
result to the printer. The result can look like:</p>
<p><img src="/assets/plugins/example_label.png" alt="Label Example"></p>
<p>The upper label was created using this template:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
{% autoescape off %}
^FT30,25^A0N,18,22^FDIPN^FS
^FT150,30^FB100,1,,C,,^A0N,24,32^FDACME^FS
^FT320,25^A0N,18,22^FD{{ item.pk }}^FS
^FT100,70^FB200,2,,C,,^A0N,18,22^FD{{ part.name }}^FS
^FT100,100^FB200,1,,C,,^A0N,18,22^FD{{ part.manufacturer_parts.first.manufacturer.name }}^FS
^FT30,150^FB340,1,,C,,^A0N,30,40^FD{{ part.IPN }}^FS
^FT20,210^FB360,3,,L,,^A0N,18,22^FD{{ part.description }}^FS
^FT15,110^BQ,2,3^FDQA,{{ part.IPN }}^FS
^FT310,130^BQ,2,3^FDQA,{{ qr_data }}^FS
{% endautoescape %}
</code></pre></div></div>
<p>Autoescape must be off. We do not need &amp;quot and similar escapes here.
Context variables can be used as usual.</p>
<p>!!! warning “Limitation”
ZPL commands starting with backslash like \&amp; cannot be used so far.</p>
<h3 id="preview">Preview</h3>
<p>The printer driver allows an output device called “preview”. If this is selected
the ZPL code is sent to the API of labelary.com. The API sends back pdf data
which is displayed in a new browser window. This is helpful while writing ZPL
templates but works with HTML templates too. Please be careful and do not send
confidential information to the API.</p>
<p>In case you need to pass a proxy for the POST requests set the environment
variables PROXY_CON and PROXY_URL on the server. The plugin does not have
settings for this.</p>
<h2 id="getting-printer-info">Getting printer info</h2>
<p>With the multi printer feature it can happen that you have several printers in
your setup. When the Get Printer Info switch is set ON, the driver calls each printer
once a minute and collects some info about it. It calls the printer configured
in the settings as well as all printers it finds in the label templates.
In case a printer is unreachable, an error message is shown. If a printer is used
in several templates it is listed only once.</p>
<p>The printer info feature works for local USB printers too.</p>
<p><img src="/assets/plugins/printer_info.png" alt="Printer Info"></p>
<h2 id="how-it-works">How it works</h2>
<p>First import all the stuff you need. Here we use the translation mechanism from Django for multi language support.
The import the InvenTree libs and everything you need for plugin. Here we have ZPL for the Zebra bitmaps and socket
for the IP connection to the printer.</p>
<p>The next part is this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ZebraLabelPlugin</span><span class="p">(</span><span class="n">LabelPrintingMixin</span><span class="p">,</span> <span class="n">SettingsMixin</span><span class="p">,</span> <span class="n">IntegrationPluginBase</span><span class="p">):</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="sh">"</span><span class="s">Michael Buchmann</span><span class="sh">"</span>
<span class="n">DESCRIPTION</span> <span class="o">=</span> <span class="sh">"</span><span class="s">Label printing plugin for Zebra printers</span><span class="sh">"</span>
<span class="n">VERSION</span> <span class="o">=</span> <span class="n">ZEBRA_PLUGIN_VERSION</span>
<span class="n">NAME</span> <span class="o">=</span> <span class="sh">"</span><span class="s">Zebra labels</span><span class="sh">"</span>
<span class="n">SLUG</span> <span class="o">=</span> <span class="sh">"</span><span class="s">zebra</span><span class="sh">"</span>
<span class="n">TITLE</span> <span class="o">=</span> <span class="sh">"</span><span class="s">Zebra Label Printer</span><span class="sh">"</span>
</code></pre></div></div>
<p>The name of the class can be freely chosen but should be different from SLUG. Otherwise it does not
show up. You reference to it in the entry_points section of the setup.py file.
The parameters need to be like in the example. Then there is the description block. The keywords are fixed and
need to be like that. The values are found in the UI as shown in the picture below.</p>
<p><img src="/assets/plugins/plugin.png" alt="Config"></p>
<p>Then we add the configuration parameters.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SETTINGS</span> <span class="o">=</span> <span class="p">{</span>
<span class="sh">'</span><span class="s">CONNECTION</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span>
<span class="sh">'</span><span class="s">name</span><span class="sh">'</span><span class="p">:</span> <span class="nf">_</span><span class="p">(</span><span class="sh">'</span><span class="s">Printer Interface</span><span class="sh">'</span><span class="p">),</span>
<span class="sh">'</span><span class="s">description</span><span class="sh">'</span><span class="p">:</span> <span class="nf">_</span><span class="p">(</span><span class="sh">'</span><span class="s">Select local or network printer</span><span class="sh">'</span><span class="p">),</span>
<span class="sh">'</span><span class="s">choices</span><span class="sh">'</span><span class="p">:</span> <span class="p">[(</span><span class="sh">'</span><span class="s">local</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">Local printer e.g. USB</span><span class="sh">'</span><span class="p">),(</span><span class="sh">'</span><span class="s">network</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">Network printer with IP address</span><span class="sh">'</span><span class="p">)],</span>
<span class="sh">'</span><span class="s">default</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">local</span><span class="sh">'</span><span class="p">,</span>
<span class="p">},</span>
<span class="sh">'</span><span class="s">PORT</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span>
<span class="sh">'</span><span class="s">name</span><span class="sh">'</span><span class="p">:</span> <span class="nf">_</span><span class="p">(</span><span class="sh">'</span><span class="s">Port</span><span class="sh">'</span><span class="p">),</span>
<span class="sh">'</span><span class="s">description</span><span class="sh">'</span><span class="p">:</span> <span class="nf">_</span><span class="p">(</span><span class="sh">'</span><span class="s">Network port in case of network printer</span><span class="sh">'</span><span class="p">),</span>
<span class="sh">'</span><span class="s">default</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">9100</span><span class="sh">'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We need to define a dict with the name SETTINGS. Please be aware the keys need to be in all CAPITAL letters like CONNECTION.
Simple parameters are just text strings like the port. We can set a default. The name and description shows up in the UI.
Instead of a simple text we can also use choices. The first string like “local” it the key you use in the code. The second
one is the description in the UI.
After that we need to define a function:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_label</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">){</span>
</code></pre></div></div>
<p>The kwargs is a dict with the following keys:</p>
<ul>
<li>pdf_data</li>
<li>user</li>
<li>filename</li>
<li>label_instance</li>
<li>item_instance</li>
<li>width</li>
<li>height</li>
<li>png_file</li>
</ul>
<p>The item_instance is the part to be printed. This allows direct access to all part data. The arguments width and height
come from the settings of the label in the admin interface. NOT from the html template.
For the Zebra printer we use the png_file. This is a PIL (python Pillow) object with the graphic of the label in PNG format.
The PIL object is a greyscale image. Because the printer can just print pure BW we convert this to a BW picture.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fn</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span> <span class="p">:</span> <span class="mi">255</span> <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="n">Threshold</span> <span class="k">else</span> <span class="mi">0</span>
<span class="n">label_image</span> <span class="o">=</span> <span class="n">label_image</span><span class="p">.</span><span class="nf">convert</span><span class="p">(</span><span class="sh">'</span><span class="s">L</span><span class="sh">'</span><span class="p">).</span><span class="nf">point</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="sh">'</span><span class="s">1</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>
<p>The threshold can by modified by a plugin parameter. 200 is a good starting value. This trick gives much better prints.
We can put the result of this directly into the ZPL library.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">l</span> <span class="o">=</span> <span class="n">zpl</span><span class="p">.</span><span class="nc">Label</span><span class="p">(</span><span class="n">Height</span><span class="p">,</span> <span class="n">Width</span><span class="p">,</span> <span class="n">dpmm</span><span class="p">)</span>
<span class="n">li</span><span class="p">.</span><span class="nf">set_darkness</span><span class="p">(</span><span class="n">darkness</span><span class="p">)</span>
<span class="bp">...</span>
<span class="n">l</span><span class="p">.</span><span class="nf">write_graphic</span><span class="p">(</span><span class="n">label_image</span><span class="p">,</span> <span class="n">Width</span><span class="p">)</span>
<span class="n">l</span><span class="p">.</span><span class="nf">endorigin</span><span class="p">()</span>
</code></pre></div></div>
<p>Width and Height define is the size of the label in millimeters as described above.
The third parameter is the resolution of the printer in dots per mm.
write_graphic converts the pillow data to ZPL.</p>
<p>The plugin was tested with a labels of various sizes defined using css and html. The DPI scaling
can be chosen in the InvenTree settings. 800 is a good value because it gives high quality.</p>
<p>The rest of the code is just output to the printer on different interfaces.</p>
<p>Happy printing.</p>
</div>
<div class="w-full md:w-1/4">
<h2 class="block sm:hidden">Detail section</h2>
<div>
<p class="mb-0">License:
<small>
<i class="fa-brands fa-osi"></i>
MIT<br>
</small>
</p>
<div class="">
Status:<br>
<div>
<small>
<i class="fa-regular fa-circle-check success"></i>Stable
</small>
</div>
<div>
<small>
<i class="fa-regular fa-circle-check success"></i>Maintained
</small>
</div>
</div>
<a href="https://github.com/SergeoLacruz/inventree-zebra-plugin/issues" class="no-underline" aria-label="open the issue tracker">
<div>
<h4 class="plugin_links">Issue Tracker <i class="fa-solid fa-arrow-up-right-from-square" title="External link"></i>
</h4>
</div>
</a>
<a href="https://github.com/SergeoLacruz/inventree-zebra-plugin" class="no-underline" aria-label="open source on GitHub">
<div>
<h4 class="plugin_links">Sourcecode on GitHub <i class="fa-solid fa-arrow-up-right-from-square" title="External link"></i>
</h4>
<p class="font-normal">SergeoLacruz/inventree-zebra-plugin</p>
</div>
</a>
<div>
<h4 class="plugin_links">Categories</h4>
<div>
<a href="/plugins/categories/#printer"><span class="plugin_cat">Printer</span></a>
</div>
</div>
<div>
<h4 class="plugin_links">Tags</h4>
<div>
<a href="/plugins/tags/#label"><span class="plugin_tag">Label</span></a>
<a href="/plugins/tags/#printer"><span class="plugin_tag">Printer</span></a>
<a href="/plugins/tags/#zebra"><span class="plugin_tag">Zebra</span></a>
<a href="/plugins/tags/#zpl"><span class="plugin_tag">ZPL</span></a>
</div>
</div>
</div>
</div>
</div>
</article>
</div>
<div class="flex-grow"></div>
<div class="flex-none">
<footer class="cm-gray-2 body-font">
<div class="container px-5 pt-8 mx-auto flex md:flex-row md:flex-nowrap flex-wrap flex-col">
<div class="w-64 flex-shrink-0 md:mx-0 mx-auto text-center md:text-left">
<div class="flex title-font font-medium items-center md:justify-start justify-center cm-gray-1">
<img src="/assets/logo.png" alt="logo" height="32" width="32" class="h-8">
<span class="ml-3 text-xl">InvenTree</span>
</div>
<p class="mt-2 text-sm cm-gray-3">Intuitive Inventory Management</p>
</div>
<div class="flex-grow flex flex-wrap md:pl-10 mb-1 md:mt-0 mt-10 md:text-left text-center md:justify-left justify-center">
<div class="md:w-1/4 px-4">
<h2 class="footer-categorie title-font">
Quick
</h2>
<nav class="list-none mb-10"><ul>
<li><a href="/demo.html" class="footer-link">Demo</a></li>
<li><a href="/deploy.html" class="footer-link">Deploy</a></li>
<li><a href="https://docs.inventree.org/en/stable/" class="footer-link">Docs</a></li>
<li><a href="/news" class="footer-link">News</a></li>
<li><a href="/plugins" class="footer-link">Plugin List</a></li>
</ul></nav>
</div>
<div class="md:w-1/4 px-4">
<h2 class="footer-categorie title-font">
<a href="/extend/">Ecosystem</a>
</h2>
<nav class="list-none mb-10"><ul>
<li><a href="/extend/api.html" class="footer-link">API</a></li>
<li><a href="/extend/app.html" class="footer-link">App</a></li>
<li><a href="/extend/plugin/" class="footer-link">Plugins</a></li>
<li><a href="/extend/integrate/" class="footer-link">Integrations</a></li>
</ul></nav>
</div>
<div class="md:w-1/4 px-4">
<h2 class="footer-categorie title-font">
Sitemap
</h2>
<nav class="list-none mb-10"><ul>
<li><a href="/about/" class="footer-link">About</a></li>
<li><a href="/alternatives/" class="footer-link">Alternatives</a></li>
<li><a href="/blog" class="footer-link">Blog</a></li>
<li><a href="/contribute.html" class="footer-link">Contribute</a></li>
<li><a href="/support.html" class="footer-link">Support</a></li>
</ul></nav>
</div>
</div>
</div>
<div class="bg-gray-100">
<div class="container mx-auto py-4 px-5 flex flex-wrap flex-col sm:flex-row">
<p class="cm-gray-2 text-sm text-center sm:text-left">© 2021-now InvenTree by<a href="https://github.com/inventree" rel="noopener" class="cm-gray-2 ml-1" target="_blank">@inventree</a>— website made with ♥ by<a href="https://github.com/matmair" rel="noopener" class="cm-gray-2 ml-1" target="_blank">@matmair</a></p>
<span class="inline-flex sm:ml-auto sm:mt-0 mt-2 justify-center sm:justify-start">
<a href="https://github.com/inventree/inventree" alt="github repo" class="ml-3 cm-gray-3">
<img class="h-5 w-5" alt="GitHub logo" src="/assets/github.svg">
</a>
<a href="https://reddit.com/r/inventree" alt="Reddit" class="ml-3 cm-gray-3">
<img class="h-5 w-5" alt="Reddit logo" src="/assets/reddit.svg">
</a>
<a href="https://twitter.com/inventreedb" alt="Twitter" class="ml-3 cm-gray-3">
<img class="h-5 w-5" alt="Twitter logo" src="/assets/twitter.svg">
</a>
<a href="https://chaos.social/@InvenTree" rel="me" alt="Mastodon" class="ml-3 cm-gray-3">
<img class="h-5 w-5" alt="Mastodon logo" src="/assets/mastodon.svg">
</a>
</span>
</div>
</div>
</footer>
</div>
</body>
</html>