Add template selection and multiple copy printing features

- Implemented template selection: type 0=OK (green), type 1=NOK (red)
- Added multiple copy printing (1-100 copies)
- Extended file format to 5 fields: ARTICLE;NR_ART;SERIAL;TYPE;COUNT
- Created OK/NOK SVG templates with visual distinction
- Fixed PDF landscape orientation issues
- Updated SumatraPDF to use noscale for exact dimensions
- Auto-generate conf folder with default templates on first run
- Made pystray optional for system tray functionality
- Updated build scripts for Python 3.13 compatibility (Kivy 2.3+, PyInstaller 6.18)
- Added comprehensive build documentation
- Improved printer configuration guidance
This commit is contained in:
NAME
2026-02-13 23:34:59 +02:00
parent 3b23f89cf0
commit 839828340d
13 changed files with 1131 additions and 117 deletions

8
.gitignore vendored
View File

@@ -1,6 +1,12 @@
label/ label/
build/ build/
dist/
logs/ logs/
pdf_backup/ pdf_backup/
venv/ venv/
__pycache__/
*.pyc
*.pyo
*.spec
*.egg-info/
.eggs/

303
BUILD_EXECUTABLE.md Normal file
View File

@@ -0,0 +1,303 @@
# Build Windows Executable (.exe)
## Quick Start
**Option 1: One Command (Recommended)**
```bash
build_windows.bat
```
**Option 2: Python Script**
```bash
python build_exe.py
```
**Option 3: Direct PyInstaller**
```bash
pyinstaller LabelPrinter.spec
```
---
## Requirements
- **Windows** 10/11
- **Python** 3.10 - 3.13 (Python 3.14+ may have compatibility issues)
- **Dependencies** installed (see below)
---
## Step-by-Step Build Process
### 1. Install Python Dependencies
If not already installed:
```bash
pip install -r requirements_windows.txt
```
Or install manually:
```bash
pip install python-barcode pillow reportlab kivy==2.2.1 pyinstaller==6.1.0 pywin32 wmi watchdog svglib cairosvg
```
### 2. Build the Executable
Run the build script:
```bash
build_windows.bat
```
This will:
- ✅ Install/upgrade dependencies
- ✅ Clean old build artifacts
- ✅ Create `LabelPrinter.exe` in `dist/` folder
- ⏱️ Takes 5-15 minutes
### 3. Test the Executable
```bash
cd dist
LabelPrinter.exe
```
---
## What Gets Included
The executable is **self-contained** and includes:
- ✅ All Python code
- ✅ All dependencies (Kivy, ReportLab, svglib, cairosvg, etc.)
- ✅ Auto-generates `conf/` folder on first run
- ✅ Auto-generates `pdf_backup/` and `logs/` folders
**On first run, the app automatically creates:**
```
LabelPrinter.exe location/
├── conf/
│ ├── app.conf (default settings)
│ ├── label_template.svg (default template)
│ ├── label_template_ok.svg (OK labels - green)
│ └── label_template_nok.svg (NOK labels - red)
├── pdf_backup/ (generated PDFs)
└── logs/ (print logs)
```
---
## Output
After successful build:
```
📁 dist/
└── LabelPrinter.exe (Single file, ~80-120 MB)
```
**File size**: 80-120 MB (includes Python runtime + all libraries)
---
## Distribution
### To Deploy on Other Windows Machines:
**Simply copy `LabelPrinter.exe` to the target machine!**
The executable is **100% self-contained**:
- ✅ No Python installation needed
- ✅ No dependencies needed
- ✅ Auto-creates all required folders on first run
- ✅ Generates default templates automatically
**First run will create:**
```
[wherever you put LabelPrinter.exe]/
├── LabelPrinter.exe ← Copy just this file!
├── conf/ ← Auto-generated on first run
│ ├── app.conf
│ ├── label_template.svg
│ ├── label_template_ok.svg
│ └── label_template_nok.svg
├── pdf_backup/ ← Auto-generated
└── logs/ ← Auto-generated
```
**To customize templates on target machine:**
1. Run `LabelPrinter.exe` once (generates conf folder)
2. Edit SVG files in the `conf/` folder
3. Restart the application
**Requirements on target machine:**
- ✅ Windows 10/11
- ✅ Thermal printer configured (35mm x 25mm paper size)
- ⚠️ No other software required!
---
## Troubleshooting
### Build Fails - Missing Dependencies
```bash
pip install --upgrade pip setuptools wheel
pip install -r requirements_windows.txt
```
### Build Fails - PyInstaller Error
```bash
pip install --upgrade pyinstaller==6.1.0
```
### Executable Won't Start
- Check Windows Defender didn't quarantine it
- Run as Administrator once to register
- Check Event Viewer for errors
### Executable is Huge (>150 MB)
This is normal! It includes:
- Python runtime (~40 MB)
- Kivy framework (~30 MB)
- ReportLab, PIL, etc. (~20 MB)
- Your code + data (~10 MB)
### First Run is Slow
- First launch takes 10-30 seconds (Kivy initialization)
- Subsequent launches are faster (~2-5 seconds)
- This is normal behavior
---
## Advanced Options
### Build with Console (for debugging)
Edit `build_windows.bat` and remove `--windowed`:
```bat
pyinstaller label_printer_gui.py ^
--onefile ^
--name=LabelPrinter ^
...
```
### Add Custom Icon
```bat
pyinstaller label_printer_gui.py ^
--onefile ^
--windowed ^
--icon=icon.ico ^
...
```
### Reduce File Size
Not recommended, but possible:
```bat
pyinstaller label_printer_gui.py ^
--onefile ^
--windowed ^
--strip ^
--exclude-module=tkinter ^
--exclude-module=matplotlib ^
...
```
---
## What's Built
**Source Files Compiled:**
- `label_printer_gui.py` (Main GUI)
- `print_label.py` (Printing logic)
- `print_label_pdf.py` (PDF generation)
**Data Files Included:**
- `conf/app.conf` (Settings)
- `conf/label_template.svg` (Default template)
- `conf/label_template_ok.svg` (OK labels - green)
- `conf/label_template_nok.svg` (NOK labels - red)
- `conf/accepted.png` (Checkmark image)
**Output Folders:**
- `pdf_backup/` - Stores generated PDFs
- `logs/` - Stores print logs
---
## Build Times
- **First build**: 10-15 minutes (PyInstaller analyzes all dependencies)
- **Rebuild**: 5-10 minutes (cached analysis)
- **Clean build**: 10-15 minutes (removed cache)
---
## File Structure After Build
```
📁 Project Root/
├── 📄 label_printer_gui.py
├── 📄 print_label.py
├── 📄 print_label_pdf.py
├── 📄 build_windows.bat ← Run this
├── 📄 build_exe.py
├── 📄 LabelPrinter.spec
├── 📁 dist/
│ ├── 📄 LabelPrinter.exe ← Your executable!
│ ├── 📁 conf/
│ ├── 📁 pdf_backup/
│ └── 📁 logs/
├── 📁 build/ (temp files, can delete)
└── 📁 conf/
├── app.conf
├── label_template.svg
├── label_template_ok.svg
└── label_template_nok.svg
```
---
## Clean Build
To remove all build artifacts:
```bash
rmdir /s /q build
rmdir /s /q dist
del *.spec
```
Then rebuild:
```bash
build_windows.bat
```
---
## Testing
After building, test with:
1. **Run the executable**:
```bash
dist\LabelPrinter.exe
```
2. **Configure monitoring**:
- File: `C:\Users\Public\Documents\check.txt`
3. **Test print**:
- Add to check.txt: `COM-001;ART-123;SN-001;0;1`
- Should print 1 OK label
4. **Test template switching**:
- `COM-002;ART-456;SN-002;1;1` → NOK label (red)
---
## Support
If build fails, check:
1. Python version (3.10-3.13 required)
2. All dependencies installed
3. Windows 10/11 (not Windows 7/8)
4. Disk space (need ~500MB for build)
5. Antivirus not blocking PyInstaller

View File

@@ -46,6 +46,12 @@ args = [
'--hidden-import=reportlab', '--hidden-import=reportlab',
'--hidden-import=print_label', '--hidden-import=print_label',
'--hidden-import=print_label_pdf', '--hidden-import=print_label_pdf',
'--hidden-import=svglib',
'--hidden-import=cairosvg',
'--hidden-import=watchdog',
'--hidden-import=watchdog.observers',
'--hidden-import=watchdog.events',
'--hidden-import=pystray',
] ]
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -39,8 +39,8 @@ echo.
REM Install dependencies REM Install dependencies
echo [3/5] Installing dependencies... echo [3/5] Installing dependencies...
echo Installing: python-barcode, pillow, reportlab, kivy, pyinstaller, pywin32, wmi... echo Installing: python-barcode, pillow, reportlab, kivy, pyinstaller, pywin32, wmi, watchdog, svglib, cairosvg, pystray...
pip install python-barcode pillow reportlab kivy==2.2.1 pyinstaller==6.1.0 pywin32 wmi pip install python-barcode pillow reportlab kivy pyinstaller pywin32 wmi watchdog svglib cairosvg pystray
if errorlevel 1 ( if errorlevel 1 (
echo ERROR: Failed to install dependencies echo ERROR: Failed to install dependencies
pause pause
@@ -72,6 +72,12 @@ pyinstaller label_printer_gui.py ^
--hidden-import=reportlab ^ --hidden-import=reportlab ^
--hidden-import=print_label ^ --hidden-import=print_label ^
--hidden-import=print_label_pdf ^ --hidden-import=print_label_pdf ^
--hidden-import=svglib ^
--hidden-import=cairosvg ^
--hidden-import=watchdog ^
--hidden-import=watchdog.observers ^
--hidden-import=watchdog.events ^
--hidden-import=pystray ^
-y -y
if errorlevel 1 ( if errorlevel 1 (

20
check_pdf_size.py Normal file
View File

@@ -0,0 +1,20 @@
from reportlab.lib.pagesizes import landscape
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
import os
# Check the test PDF file
if os.path.exists('test_label.pdf'):
file_size = os.path.getsize('test_label.pdf')
print(f'test_label.pdf exists ({file_size} bytes)')
print(f'Expected: 35mm x 25mm landscape (99.2 x 70.9 points)')
print(f'')
print(f'Open test_label.pdf in a PDF viewer to verify:')
print(f' - Page size should be wider than tall')
print(f' - Content should be correctly oriented')
print(f'')
print(f'In Adobe Reader: File > Properties > Description')
print(f' Page size should show: 3.5 x 2.5 cm or 1.38 x 0.98 in')
else:
print('test_label.pdf not found')

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="35mm" height="25mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 35 25"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<font id="FontID0" horiz-adv-x="722" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="700">
<font-face
font-family="Arial">
<font-face-src>
<font-face-name name="Arial Bold"/>
</font-face-src>
</font-face>
<missing-glyph><path d="M0 0z"/></missing-glyph>
<glyph unicode="." horiz-adv-x="277" d="M71.0096 0l0 136.998 136.998 0 0 -136.998 -136.998 0z"/>
<glyph unicode=":" horiz-adv-x="332" d="M98.0096 381.997l0 136.998 136.998 0 0 -136.998 -136.998 0zm0 -381.997l0 136.998 136.998 0 0 -136.998 -136.998 0z"/>
<glyph unicode="A" horiz-adv-x="722" d="M718.011 0l-156.185 0 -61.8388 162.999 -287.163 0 -59.482 -162.999 -153.342 0 277.993 715.987 153.162 0 286.856 -715.987zm-265.005 284.013l-99.4954 264.979 -96.6775 -264.979 196.173 0z"/>
<glyph unicode="C" horiz-adv-x="722" d="M531.009 264.006l139.995 -43.0105c-21.4924,-78.8227 -57.3302,-137.331 -107.334,-175.654 -50.0038,-38.1689 -113.328,-57.3302 -190.179,-57.3302 -95.1661,0 -173.323,32.482 -234.649,97.4972 -61.1727,64.9896 -91.836,153.828 -91.836,266.67 0,119.143 30.8169,211.825 92.3227,277.813 61.5058,66.0143 142.506,99.0086 242.847,99.0086 87.6604,0 158.824,-26.001 213.49,-77.8236 32.6613,-30.6888 56.9972,-74.6727 73.3407,-132.182l-143.018 -33.9934c-8.47914,36.9905 -26.1547,66.3217 -52.9754,87.8397 -27,21.4924 -59.687,32.149 -98.0096,32.149 -53.1803,0 -96.3445,-18.982 -129.339,-57.1509 -33.1737,-38.0152 -49.6708,-99.6747 -49.6708,-185.004 0,-90.3246 16.3435,-154.827 48.8511,-193.176 32.6613,-38.5019 74.9801,-57.6632 127.161,-57.6632 38.5019,0 71.65,12.1679 99.316,36.6831 27.6661,24.4896 47.6727,62.8122 59.687,115.326z"/>
<glyph unicode="N" horiz-adv-x="722" d="M73.0077 0l0 715.987 140.328 0 294.669 -479.827 0 479.827 134.001 0 0 -715.987 -144.837 0 -290.161 470.656 0 -470.656 -134.001 0z"/>
<glyph unicode="S" horiz-adv-x="667" d="M34.9924 232.011l141.02 13.9867c8.47914,-47.1604 25.4886,-81.6661 51.3103,-103.825 25.8473,-22.1841 60.686,-33.1737 104.516,-33.1737 46.315,0 81.3331,9.83682 104.824,29.5105 23.5162,19.648 35.3255,42.6518 35.3255,68.9858 0,17.0095 -4.99526,31.3293 -14.8321,43.3435 -9.8112,11.8349 -27.1537,22.1585 -51.8226,30.8169 -16.8302,6.01993 -55.1784,16.3435 -115.173,31.3549 -77.1576,19.315 -131.337,42.9849 -162.487,71.1633 -43.8302,39.501 -65.6813,87.6604 -65.6813,144.504 0,36.4782 10.3492,70.8302 30.8425,102.646 20.6727,31.8416 50.3369,55.9982 89.1718,72.6746 38.835,16.6765 85.483,25.0019 140.482,25.0019 89.5048,0 157.005,-19.8273 202.167,-59.6613 45.3416,-39.834 69.0115,-92.835 71.3426,-159.336l-144.991 -4.99526c-6.17363,36.9905 -19.3406,63.5039 -39.501,79.668 -20.186,16.1642 -50.5162,24.3359 -90.8369,24.3359 -41.6784,0 -74.3397,-8.68407 -97.8303,-26.001 -15.1651,-11.1689 -22.8501,-26.001 -22.8501,-44.6756 0,-17.0095 7.17268,-31.5086 21.518,-43.4972 18.1623,-15.4981 62.3255,-31.5086 132.49,-48.1594 70.1642,-16.5228 122.012,-33.8397 155.494,-51.5152 33.686,-17.8292 60.02,-41.9858 79.002,-72.8283 19.0076,-30.8425 28.5114,-68.8321 28.5114,-113.994 0,-41.0124 -11.3482,-79.5143 -34.1727,-115.352 -22.8245,-35.8122 -54.9991,-62.4792 -96.6775,-79.8217 -41.6528,-17.4962 -93.6547,-26.1547 -155.827,-26.1547 -90.5039,0 -160.002,20.8264 -208.495,62.6585 -48.4925,41.6528 -77.3369,102.493 -86.8407,182.34z"/>
<glyph unicode="a" horiz-adv-x="556" d="M173.989 358.993l-123.985 22.0048c13.9867,50.6699 38.1689,88.1728 72.3416,112.509 34.1471,24.3359 84.9963,36.5038 152.317,36.5038 61.1727,0 106.847,-7.17268 136.845,-21.6717 29.8179,-14.4991 51.0029,-32.8406 63.1708,-55.1784 12.1679,-22.3378 18.316,-63.1708 18.316,-122.832l-1.9981 -160.002c0,-45.4953 2.17742,-79.1557 6.50665,-100.827 4.32923,-21.6717 12.501,-44.8293 24.4896,-69.4982l-135.999 0c-3.48387,8.99147 -7.99242,22.3378 -13.167,39.9877 -2.1518,8.17173 -3.81689,13.5 -4.81594,16.0105 -23.3368,-23.0038 -48.3388,-40.167 -75.0058,-51.6689 -26.667,-11.5019 -54.9991,-17.3169 -85.1756,-17.3169 -53.1547,0 -95.1661,14.4991 -125.829,43.4972 -30.6632,28.8188 -46.0076,65.502 -46.0076,109.819 0,29.1774 7.01898,55.3321 21.0057,78.3359 14.0123,22.8245 33.5067,40.5 58.8416,52.668 25.1556,12.1679 61.5058,22.8245 108.999,31.9953 63.9906,12.0142 108.487,23.3368 133.156,33.6604l0 13.833c0,26.667 -6.48103,45.6746 -19.4943,57.1765 -13.167,11.3482 -37.8359,17.0095 -74.0067,17.0095 -24.4896,0 -43.4972,-4.84156 -57.1509,-14.6784 -13.833,-9.6575 -24.8482,-26.8207 -33.353,-51.3359zm184.005 -110.997c-17.4962,-5.84061 -45.316,-12.834 -83.4849,-21.0057 -38.0152,-8.14612 -63.0171,-16.1642 -74.6727,-23.8236 -17.8292,-12.834 -26.8463,-28.8444 -26.8463,-48.3388 0,-19.315 7.17268,-35.8378 21.518,-49.8245 14.3197,-14.0123 32.482,-21.0057 54.6661,-21.0057 24.6689,0 48.3388,8.17173 70.8302,24.3359 16.4972,12.501 27.4867,27.5124 32.6613,45.4953 3.50949,11.6812 5.32828,33.9934 5.32828,66.834l0 27.333z"/>
<glyph unicode="c" horiz-adv-x="556" d="M523.99 365.013l-135 -24.0029c-4.48293,26.8207 -14.8321,46.9811 -30.9962,60.6604 -16.1642,13.5 -36.9905,20.3397 -62.6585,20.3397 -34.1727,0 -61.5058,-11.8349 -81.8454,-35.5048 -20.3141,-23.6699 -30.4839,-63.1708 -30.4839,-118.682 0,-61.6595 10.3235,-105.157 30.9962,-130.645 20.6727,-25.5143 48.3388,-38.1689 82.9982,-38.1689 26.001,0 47.3397,7.48008 63.8369,22.3122 16.6509,14.8577 28.3321,40.3463 35.1718,76.6709l135 -23.0038c-14.0123,-61.9925 -40.8331,-108.82 -80.5134,-140.482 -39.6547,-31.6623 -92.835,-47.4934 -159.669,-47.4934 -75.6718,0 -136.153,23.9773 -181.161,71.8293 -45.1623,47.9801 -67.6538,114.327 -67.6538,199.17 0,85.816 22.6452,152.496 67.8331,200.323 45.1623,47.8264 106.309,71.6756 183.493,71.6756 62.9915,0 113.175,-13.6793 150.498,-40.8331 37.1699,-27.1793 63.8369,-68.4991 80.1547,-124.164z"/>
<glyph unicode="d" horiz-adv-x="610" d="M547.993 0l-126.982 0 0 76.1585c-21.185,-29.6642 -46.3407,-51.8226 -75.1851,-66.834 -28.8188,-14.8321 -57.9963,-22.3122 -87.3274,-22.3122 -59.8407,0 -110.997,23.9773 -153.675,72.1623 -42.4981,48.1594 -63.8113,115.147 -63.8113,201.322 0,87.9934 20.6471,155.007 62.1462,200.835 41.4991,45.8283 93.8341,68.6784 157.184,68.6784 57.9963,0 108.333,-24.1822 150.652,-72.3416l0 258.319 136.998 0 0 -715.987zm-365.986 269.488c0,-55.4858 7.6594,-95.6528 22.8245,-120.475 22.1585,-36.0171 53.001,-54.0001 92.6813,-54.0001 31.483,0 58.3293,13.5 80.3084,40.5 22.1841,27 33.1737,67.1414 33.1737,120.808 0,59.8407 -10.6566,102.851 -32.149,129.185 -21.518,26.334 -48.8511,39.501 -82.3578,39.501 -32.482,0 -59.6613,-13.0133 -81.6661,-39.0143 -21.8254,-26.001 -32.815,-64.8359 -32.815,-116.505z"/>
<glyph unicode="e" horiz-adv-x="556" d="M372.006 163.998l136.998 -23.0038c-17.4962,-50.1575 -45.3416,-88.3265 -83.1775,-114.66 -37.9896,-26.1547 -85.483,-39.3217 -142.327,-39.3217 -90.1709,0 -157.005,29.4848 -200.169,88.4802 -34.1727,47.3397 -51.3359,107.001 -51.3359,179.163 0,86.021 22.5171,153.521 67.3464,202.167 44.8293,48.8511 101.647,73.187 170.326,73.187 77.0039,0 137.844,-25.5143 182.494,-76.5172 44.4962,-51.0029 65.835,-129.16 63.8369,-234.495l-343.008 0c0.999052,-40.6537 12.0142,-72.3416 33.1737,-94.9868 21.0057,-22.6708 47.3397,-34.019 78.669,-34.019 21.4924,0 39.501,5.84061 54.0001,17.4962 14.6784,11.6812 25.668,30.5095 33.1737,56.5105zm7.99242 138.996c-0.999052,39.834 -11.1689,70.1642 -30.6632,90.8369 -19.4943,20.8264 -43.1642,31.1756 -71.1633,31.1756 -29.8435,0 -54.5124,-11.0152 -74.0067,-32.8406 -19.4943,-21.8254 -28.9981,-51.6689 -28.6651,-89.1718l204.498 0z"/>
<glyph unicode="i" horiz-adv-x="277" d="M72.0086 589.005l0 126.982 136.998 0 0 -126.982 -136.998 0zm0 -589.005l0 518.995 136.998 0 0 -518.995 -136.998 0z"/>
<glyph unicode="l" horiz-adv-x="277" d="M72.0086 0l0 715.987 136.998 0 0 -715.987 -136.998 0z"/>
<glyph unicode="m" horiz-adv-x="889" d="M61.9925 518.995l126.009 0 0 -70.8302c45.1623,54.5124 99.0086,81.8454 161.488,81.8454 33.1737,0 62.0181,-6.83966 86.354,-20.519 24.4896,-13.6537 44.4962,-34.3264 59.9944,-61.9925 22.8245,27.6661 47.4934,48.3388 73.8274,61.9925 26.334,13.6793 54.5124,20.519 84.5096,20.519 37.9896,0 70.1642,-7.68502 96.6519,-23.1831 26.334,-15.4981 46.0076,-38.1689 58.9953,-68.1661 9.5038,-22.0048 14.166,-57.8169 14.166,-107.334l0 -331.327 -136.998 0 0 296.155c0,51.5152 -4.66224,84.6889 -14.166,99.521 -12.6547,19.4943 -32.3283,29.3311 -58.6623,29.3311 -19.1613,0 -37.3236,-5.84061 -54.3331,-17.4962 -16.8302,-11.8349 -29.1518,-28.9981 -36.6575,-51.5152 -7.5057,-22.6708 -11.1689,-58.3293 -11.1689,-107.155l0 -248.841 -136.998 0 0 284.013c0,50.3112 -2.51044,82.9982 -7.32638,97.4972 -4.84156,14.6528 -12.3473,25.668 -22.6708,32.815 -10.1698,7.17268 -24.1822,10.6822 -41.6784,10.6822 -21.1594,0 -40.167,-5.6613 -56.9972,-17.0095 -17.0095,-11.5019 -28.9981,-27.8198 -36.3245,-49.3378 -7.352,-21.4924 -11.0152,-57.1509 -11.0152,-106.822l0 -251.838 -136.998 0 0 518.995z"/>
<glyph unicode="n" horiz-adv-x="610" d="M543.997 0l-136.998 0 0 264.493c0,55.9982 -2.99716,92.169 -8.83777,108.512 -5.99431,16.4972 -15.4981,29.1518 -28.8188,38.3226 -13.3463,9.17079 -29.3311,13.6793 -48.0057,13.6793 -24.0029,0 -45.4953,-6.50665 -64.5029,-19.4943 -19.1613,-13.0133 -32.1746,-30.3558 -39.168,-51.6689 -7.17268,-21.518 -10.6566,-61.1727 -10.6566,-119.169l0 -234.675 -136.998 0 0 518.995 126.982 0 0 -76.1585c45.4953,58.1756 102.672,87.1737 171.837,87.1737 30.3302,0 58.1756,-5.5076 83.3312,-16.3435 25.3349,-10.9896 44.3425,-24.8226 57.1765,-41.6784 12.9877,-16.9839 22.0048,-36.1452 27,-57.6632 5.17458,-21.4924 7.6594,-52.1556 7.6594,-92.169l0 -322.156z"/>
<glyph unicode="o" horiz-adv-x="610" d="M39.9877 265.825c0,45.6746 11.1689,89.8378 33.686,132.515 22.4915,42.8312 54.3331,75.3388 95.4991,97.8303 41.1661,22.4915 86.9944,33.8397 137.818,33.8397 78.5153,0 142.685,-25.5143 192.843,-76.5172 50.1575,-51.1566 75.1595,-115.48 75.1595,-193.483 0,-78.669 -25.3349,-143.838 -75.8255,-195.507 -50.6699,-51.6689 -114.327,-77.4906 -191.178,-77.4906 -47.4934,0 -92.835,10.8103 -135.999,32.3283 -42.9849,21.4924 -75.8255,53.001 -98.317,94.6538 -22.5171,41.4991 -33.686,92.169 -33.686,151.83zm141.02 -7.32638c0,-51.4896 12.1679,-90.9906 36.5038,-118.324 24.4896,-27.5124 54.4868,-41.1661 90.3246,-41.1661 35.6585,0 65.6557,13.6537 89.8378,41.1661 24.1566,27.333 36.3245,67.167 36.3245,119.323 0,50.8236 -12.1679,89.9915 -36.3245,117.325 -24.1822,27.5124 -54.1794,41.1661 -89.8378,41.1661 -35.8378,0 -65.835,-13.6537 -90.3246,-41.1661 -24.3359,-27.333 -36.5038,-66.834 -36.5038,-118.324z"/>
<glyph unicode="r" horiz-adv-x="389" d="M203.013 0l-137.024 0 0 518.995 127.008 0 0 -73.6737c21.8254,34.8387 41.4991,57.6889 58.9953,68.5247 17.4962,10.8103 37.3492,16.1642 59.5076,16.1642 31.3293,0 61.5058,-8.68407 90.5039,-25.8473l-42.4981 -119.656c-23.1831,14.9858 -44.6756,22.4915 -64.5029,22.4915 -19.3406,0 -35.6585,-5.32828 -49.0048,-15.8311 -13.3207,-10.6566 -23.8236,-29.6642 -31.5086,-57.3302 -7.6594,-27.6661 -11.4763,-85.6623 -11.4763,-173.835l0 -160.002z"/>
<glyph unicode="t" horiz-adv-x="332" d="M308.989 518.995l0 -108.999 -93.9878 0 0 -210.16c0,-42.6775 0.819735,-67.5001 2.66414,-74.4934 1.8444,-7.01898 5.84061,-12.834 12.3473,-17.5218 6.32733,-4.48293 13.9867,-6.81405 23.1575,-6.81405 12.834,0 31.1756,4.32923 55.3321,13.167l11.5019 -106.668c-31.8416,-13.6793 -67.8331,-20.4934 -108.179,-20.4934 -24.6689,0 -46.8274,4.14991 -66.6547,12.4753 -19.8273,8.35105 -34.3264,19.1869 -43.4972,32.3539 -9.3501,13.3207 -15.6774,31.1499 -19.3406,53.8207 -2.84346,16.0105 -4.32923,48.3388 -4.32923,97.1642l0 227.169 -62.9915 0 0 108.999 62.9915 0 0 103.005 136.998 81.0001 0 -184.005 93.9878 0z"/>
<glyph unicode="{" horiz-adv-x="389" d="M28.9981 200.989l0 117.017c23.8236,1.33207 41.6784,4.81594 53.8464,10.8359 11.9886,5.815 22.3122,15.6518 31.1499,29.4848 8.83777,13.833 14.8321,31.1756 18.1623,52.0019 2.51044,15.6774 3.84251,42.8312 3.84251,81.6661 0,63.1708 2.99716,107.18 8.83777,132.182 5.84061,25.0019 16.3179,44.983 31.6623,60.1481 15.3444,15.1651 37.6566,27 66.834,35.8378 19.8273,5.84061 51.1566,8.83777 93.8341,8.83777l25.8217 0 0 -116.992c-36.3245,0 -59.482,-1.9981 -69.8312,-6.17363 -10.3235,-3.99621 -17.8292,-10.1698 -22.8245,-18.4953 -4.84156,-8.35105 -7.32638,-22.5171 -7.32638,-42.6775 0,-20.4934 -1.33207,-59.5076 -3.99621,-116.659 -1.66509,-32.3283 -5.84061,-58.3293 -12.6803,-78.5153 -6.83966,-19.981 -15.4981,-36.4782 -26.1547,-49.4915 -10.5029,-12.9877 -26.667,-26.4877 -48.5181,-40.5 19.3406,-10.9896 35.0181,-24.0029 47.3397,-38.835 12.3473,-14.8321 21.6717,-32.8406 28.1784,-54.0001 6.66035,-21.1594 10.8359,-49.6708 12.834,-85.15 1.9981,-54.0001 2.99716,-88.6851 2.99716,-103.671 0,-21.518 2.66414,-36.5038 7.83872,-45.0086 5.14896,-8.32543 12.9877,-14.8321 23.6442,-19.1613 10.5029,-4.50854 33.353,-6.66035 68.4991,-6.66035l0 -117.017 -25.8217 0c-44.0095,0 -77.6699,3.50949 -101.16,10.5029 -23.3368,6.99337 -43.1642,18.6746 -59.1746,34.9924 -16.1642,16.1898 -27,36.3501 -32.5076,60.353 -5.48198,23.8236 -8.32543,61.6595 -8.32543,113.149 0,59.8407 -2.66414,98.8293 -7.83872,116.684 -7.17268,26.1547 -17.9829,44.8293 -32.482,55.9982 -14.4991,11.3226 -36.6831,17.6499 -66.6803,19.315z"/>
<glyph unicode="}" horiz-adv-x="389" d="M355.996 200.989c-23.8236,-1.33207 -41.6528,-4.81594 -53.667,-10.6566 -12.1679,-5.99431 -22.4915,-15.8311 -31.1499,-29.6642 -8.50475,-13.833 -14.6784,-31.1756 -18.3416,-52.0019 -2.51044,-15.6774 -3.84251,-42.6775 -3.84251,-81.1538 0,-63.1708 -2.81784,-107.334 -8.50475,-132.515 -5.6613,-25.0019 -16.1642,-45.1623 -31.483,-60.3274 -15.3444,-15.1651 -37.8359,-27 -67.3464,-35.8378 -19.8273,-5.84061 -51.1566,-8.83777 -93.8341,-8.83777l-25.8217 0 0 117.017c34.9924,0 57.8169,2.1518 68.6528,6.66035 10.6822,4.32923 18.6746,10.6566 23.6699,19.0076 5.17458,8.32543 7.68502,22.3122 7.83872,42.3188 0.179317,19.8273 1.51139,57.8426 3.84251,113.841 1.66509,33.9934 5.99431,60.9934 13.167,81.4868 7.14707,20.3397 16.6509,37.6822 28.4858,51.8482 12.0142,14.166 27.1793,26.4877 45.6746,37.3236 -24.0029,15.6774 -41.6784,30.9962 -52.668,45.8283 -15.3444,21.518 -25.8473,48.8511 -31.3293,82.1784 -3.50949,22.6708 -5.99431,72.8283 -7.32638,150.319 -0.333017,24.3359 -2.51044,40.6794 -6.68596,48.8511 -3.99621,7.99242 -11.3226,14.3197 -22.0048,18.649 -10.6566,4.50854 -34.3264,6.68596 -71.317,6.68596l0 116.992 25.8217 0c44.0095,0 77.6699,-3.50949 101.186,-10.3235 23.3112,-6.83966 42.9849,-18.5209 58.9953,-34.8387 15.9848,-16.4972 26.8207,-36.6831 32.482,-60.6604 5.68691,-23.8492 8.50475,-61.6851 8.50475,-113.175 0,-59.5076 2.51044,-98.4963 7.32638,-116.659 7.17268,-26.1803 18.0086,-44.8549 32.6869,-56.0238 14.6528,-11.3226 36.9905,-17.6499 66.9877,-19.315l0 -117.017z"/>
</font>
<style type="text/css">
<![CDATA[
@font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:bold;src:url("#FontID0") format(svg)}
.fil0 {fill:#373435}
.fil1 {fill:#373435}
.fil2 {fill:#373435;fill-rule:nonzero}
.fnt0 {font-weight:bold;font-size:3.9037px;font-family:'Arial'}
]]>
</style>
</defs>
<g id="Layer_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g transform="matrix(0.576293 0 0 1 -8.27317 4.15816)">
<text x="17.5" y="12.5" class="fil0 fnt0">Nr. Comanda:{Article}</text>
</g>
<g transform="matrix(0.576293 0 0 1 -8.27317 7.72657)">
<text x="17.5" y="12.5" class="fil0 fnt0">Nr. Art.:{NrArt}</text>
</g>
<g transform="matrix(0.576293 0 0 1 -8.33607 11.0472)">
<text x="17.5" y="12.5" class="fil0 fnt0">Serial No.:{Serial}</text>
</g>
<g id="_1503869020736">
<path class="fil1" d="M13.9026 6.1147c-0.0969,0.0226 -0.5344,0.3421 -0.5861,0.348 -0.0365,0.0041 -0.3442,-0.227 -0.3951,-0.2642 -0.0499,-0.0364 -0.0741,-0.0672 -0.136,-0.0838 0,0.0623 0.0141,0.1755 0.0234,0.2513 0.0277,0.2244 0.0714,0.489 0.1349,0.7076 0.086,0.2957 0.1778,0.558 0.3056,0.8116 0.2029,0.4024 0.3688,0.6622 0.6587,1.008 0.7996,0.9535 2.1521,1.6354 3.4012,1.6354 0.8602,0 1.4215,-0.0956 2.1368,-0.4273 0.2518,-0.1167 0.4901,-0.2561 0.7094,-0.4077 0.3749,-0.2591 0.6465,-0.5227 0.9323,-0.8626 0.0294,-0.035 0.0411,-0.0435 0.0679,-0.0786 0.57,-0.7469 0.8516,-1.4248 0.9811,-2.3706 0.0101,-0.0742 0.0259,-0.2049 0.0259,-0.2671 -0.0958,-0.0641 -0.1973,-0.1198 -0.293,-0.1831 -0.3674,-0.243 -0.2819,-0.2176 -0.5911,0.0133 -0.3726,0.278 -0.1599,0.0504 -0.3023,0.6684 -0.032,0.1389 -0.0657,0.2528 -0.1072,0.3873 -0.2015,0.6534 -0.7073,1.2896 -1.2311,1.6992 -0.1584,0.1239 -0.4254,0.286 -0.6008,0.37 -1.2009,0.5747 -2.7309,0.4386 -3.7667,-0.4035 -0.1586,-0.1289 -0.3761,-0.3128 -0.4993,-0.4714 -0.2438,-0.3139 -0.3987,-0.4932 -0.5704,-0.8948 -0.1469,-0.3437 -0.2981,-0.8086 -0.2981,-1.1854z"/>
<path class="fil1" d="M12.7854 5.5653c0.092,0.0616 0.1745,0.1224 0.2711,0.1868 0.3448,0.2298 0.2204,0.2304 0.5604,0.0037 0.1015,-0.0677 0.1895,-0.1261 0.2857,-0.1905 0,-0.6008 0.3296,-1.3796 0.7036,-1.8788 0.1683,-0.2248 0.5135,-0.5784 0.7579,-0.7439 0.6122,-0.4147 1.1468,-0.6373 1.9084,-0.674 0.2685,-0.013 0.0473,-0.0281 0.3856,-0.001 0.2398,0.0192 0.2983,0.0032 0.5892,0.0702 0.4301,0.0991 0.855,0.2746 1.2086,0.513 0.7053,0.4753 1.2198,1.1687 1.4592,1.984 0.0526,0.1792 0.1303,0.5118 0.1303,0.7305 0.0881,-0.0235 0.4644,-0.3425 0.5128,-0.348 0.0656,0.0175 0.2282,0.1315 0.301,0.1752 0.0656,0.0395 0.2453,0.1592 0.3034,0.1728l-0.0573 -0.4922c-0.1187,-0.6931 -0.4034,-1.4203 -0.8244,-1.9778 -0.177,-0.2344 -0.4287,-0.5477 -0.6595,-0.7324 -0.1682,-0.1347 -0.2514,-0.2282 -0.466,-0.3765 -0.6841,-0.4728 -1.6125,-0.835 -2.5349,-0.835 -0.8789,0 -1.3878,0.1016 -2.106,0.4215 -1.3809,0.6151 -2.4156,1.9744 -2.669,3.4847 -0.0144,0.0857 -0.0219,0.1508 -0.0342,0.2406 -0.0101,0.0742 -0.0259,0.2049 -0.0259,0.2671z"/>
<polygon class="fil2" points="14.8273,3.5997 16.8077,3.5997 17.5,4.8137 18.3014,3.5997 20.1392,3.5997 18.658,5.6752 20.2482,7.9549 18.3014,7.9549 17.5,6.5521 16.5476,7.9549 14.7434,7.9549 16.328,5.6752 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="35mm" height="25mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 35 25"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<font id="FontID0" horiz-adv-x="722" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="700">
<font-face
font-family="Arial">
<font-face-src>
<font-face-name name="Arial Bold"/>
</font-face-src>
</font-face>
<missing-glyph><path d="M0 0z"/></missing-glyph>
<glyph unicode="." horiz-adv-x="277" d="M71.0096 0l0 136.998 136.998 0 0 -136.998 -136.998 0z"/>
<glyph unicode=":" horiz-adv-x="332" d="M98.0096 381.997l0 136.998 136.998 0 0 -136.998 -136.998 0zm0 -381.997l0 136.998 136.998 0 0 -136.998 -136.998 0z"/>
<glyph unicode="A" horiz-adv-x="722" d="M718.011 0l-156.185 0 -61.8388 162.999 -287.163 0 -59.482 -162.999 -153.342 0 277.993 715.987 153.162 0 286.856 -715.987zm-265.005 284.013l-99.4954 264.979 -96.6775 -264.979 196.173 0z"/>
<glyph unicode="C" horiz-adv-x="722" d="M531.009 264.006l139.995 -43.0105c-21.4924,-78.8227 -57.3302,-137.331 -107.334,-175.654 -50.0038,-38.1689 -113.328,-57.3302 -190.179,-57.3302 -95.1661,0 -173.323,32.482 -234.649,97.4972 -61.1727,64.9896 -91.836,153.828 -91.836,266.67 0,119.143 30.8169,211.825 92.3227,277.813 61.5058,66.0143 142.506,99.0086 242.847,99.0086 87.6604,0 158.824,-26.001 213.49,-77.8236 32.6613,-30.6888 56.9972,-74.6727 73.3407,-132.182l-143.018 -33.9934c-8.47914,36.9905 -26.1547,66.3217 -52.9754,87.8397 -27,21.4924 -59.687,32.149 -98.0096,32.149 -53.1803,0 -96.3445,-18.982 -129.339,-57.1509 -33.1737,-38.0152 -49.6708,-99.6747 -49.6708,-185.004 0,-90.3246 16.3435,-154.827 48.8511,-193.176 32.6613,-38.5019 74.9801,-57.6632 127.161,-57.6632 38.5019,0 71.65,12.1679 99.316,36.6831 27.6661,24.4896 47.6727,62.8122 59.687,115.326z"/>
<glyph unicode="N" horiz-adv-x="722" d="M73.0077 0l0 715.987 140.328 0 294.669 -479.827 0 479.827 134.001 0 0 -715.987 -144.837 0 -290.161 470.656 0 -470.656 -134.001 0z"/>
<glyph unicode="S" horiz-adv-x="667" d="M34.9924 232.011l141.02 13.9867c8.47914,-47.1604 25.4886,-81.6661 51.3103,-103.825 25.8473,-22.1841 60.686,-33.1737 104.516,-33.1737 46.315,0 81.3331,9.83682 104.824,29.5105 23.5162,19.648 35.3255,42.6518 35.3255,68.9858 0,17.0095 -4.99526,31.3293 -14.8321,43.3435 -9.8112,11.8349 -27.1537,22.1585 -51.8226,30.8169 -16.8302,6.01993 -55.1784,16.3435 -115.173,31.3549 -77.1576,19.315 -131.337,42.9849 -162.487,71.1633 -43.8302,39.501 -65.6813,87.6604 -65.6813,144.504 0,36.4782 10.3492,70.8302 30.8425,102.646 20.6727,31.8416 50.3369,55.9982 89.1718,72.6746 38.835,16.6765 85.483,25.0019 140.482,25.0019 89.5048,0 157.005,-19.8273 202.167,-59.6613 45.3416,-39.834 69.0115,-92.835 71.3426,-159.336l-144.991 -4.99526c-6.17363,36.9905 -19.3406,63.5039 -39.501,79.668 -20.186,16.1642 -50.5162,24.3359 -90.8369,24.3359 -41.6784,0 -74.3397,-8.68407 -97.8303,-26.001 -15.1651,-11.1689 -22.8501,-26.001 -22.8501,-44.6756 0,-17.0095 7.17268,-31.5086 21.518,-43.4972 18.1623,-15.4981 62.3255,-31.5086 132.49,-48.1594 70.1642,-16.5228 122.012,-33.8397 155.494,-51.5152 33.686,-17.8292 60.02,-41.9858 79.002,-72.8283 19.0076,-30.8425 28.5114,-68.8321 28.5114,-113.994 0,-41.0124 -11.3482,-79.5143 -34.1727,-115.352 -22.8245,-35.8122 -54.9991,-62.4792 -96.6775,-79.8217 -41.6528,-17.4962 -93.6547,-26.1547 -155.827,-26.1547 -90.5039,0 -160.002,20.8264 -208.495,62.6585 -48.4925,41.6528 -77.3369,102.493 -86.8407,182.34z"/>
<glyph unicode="a" horiz-adv-x="556" d="M173.989 358.993l-123.985 22.0048c13.9867,50.6699 38.1689,88.1728 72.3416,112.509 34.1471,24.3359 84.9963,36.5038 152.317,36.5038 61.1727,0 106.847,-7.17268 136.845,-21.6717 29.8179,-14.4991 51.0029,-32.8406 63.1708,-55.1784 12.1679,-22.3378 18.316,-63.1708 18.316,-122.832l-1.9981 -160.002c0,-45.4953 2.17742,-79.1557 6.50665,-100.827 4.32923,-21.6717 12.501,-44.8293 24.4896,-69.4982l-135.999 0c-3.48387,8.99147 -7.99242,22.3378 -13.167,39.9877 -2.1518,8.17173 -3.81689,13.5 -4.81594,16.0105 -23.3368,-23.0038 -48.3388,-40.167 -75.0058,-51.6689 -26.667,-11.5019 -54.9991,-17.3169 -85.1756,-17.3169 -53.1547,0 -95.1661,14.4991 -125.829,43.4972 -30.6632,28.8188 -46.0076,65.502 -46.0076,109.819 0,29.1774 7.01898,55.3321 21.0057,78.3359 14.0123,22.8245 33.5067,40.5 58.8416,52.668 25.1556,12.1679 61.5058,22.8245 108.999,31.9953 63.9906,12.0142 108.487,23.3368 133.156,33.6604l0 13.833c0,26.667 -6.48103,45.6746 -19.4943,57.1765 -13.167,11.3482 -37.8359,17.0095 -74.0067,17.0095 -24.4896,0 -43.4972,-4.84156 -57.1509,-14.6784 -13.833,-9.6575 -24.8482,-26.8207 -33.353,-51.3359zm184.005 -110.997c-17.4962,-5.84061 -45.316,-12.834 -83.4849,-21.0057 -38.0152,-8.14612 -63.0171,-16.1642 -74.6727,-23.8236 -17.8292,-12.834 -26.8463,-28.8444 -26.8463,-48.3388 0,-19.315 7.17268,-35.8378 21.518,-49.8245 14.3197,-14.0123 32.482,-21.0057 54.6661,-21.0057 24.6689,0 48.3388,8.17173 70.8302,24.3359 16.4972,12.501 27.4867,27.5124 32.6613,45.4953 3.50949,11.6812 5.32828,33.9934 5.32828,66.834l0 27.333z"/>
<glyph unicode="c" horiz-adv-x="556" d="M523.99 365.013l-135 -24.0029c-4.48293,26.8207 -14.8321,46.9811 -30.9962,60.6604 -16.1642,13.5 -36.9905,20.3397 -62.6585,20.3397 -34.1727,0 -61.5058,-11.8349 -81.8454,-35.5048 -20.3141,-23.6699 -30.4839,-63.1708 -30.4839,-118.682 0,-61.6595 10.3235,-105.157 30.9962,-130.645 20.6727,-25.5143 48.3388,-38.1689 82.9982,-38.1689 26.001,0 47.3397,7.48008 63.8369,22.3122 16.6509,14.8577 28.3321,40.3463 35.1718,76.6709l135 -23.0038c-14.0123,-61.9925 -40.8331,-108.82 -80.5134,-140.482 -39.6547,-31.6623 -92.835,-47.4934 -159.669,-47.4934 -75.6718,0 -136.153,23.9773 -181.161,71.8293 -45.1623,47.9801 -67.6538,114.327 -67.6538,199.17 0,85.816 22.6452,152.496 67.8331,200.323 45.1623,47.8264 106.309,71.6756 183.493,71.6756 62.9915,0 113.175,-13.6793 150.498,-40.8331 37.1699,-27.1793 63.8369,-68.4991 80.1547,-124.164z"/>
<glyph unicode="d" horiz-adv-x="610" d="M547.993 0l-126.982 0 0 76.1585c-21.185,-29.6642 -46.3407,-51.8226 -75.1851,-66.834 -28.8188,-14.8321 -57.9963,-22.3122 -87.3274,-22.3122 -59.8407,0 -110.997,23.9773 -153.675,72.1623 -42.4981,48.1594 -63.8113,115.147 -63.8113,201.322 0,87.9934 20.6471,155.007 62.1462,200.835 41.4991,45.8283 93.8341,68.6784 157.184,68.6784 57.9963,0 108.333,-24.1822 150.652,-72.3416l0 258.319 136.998 0 0 -715.987zm-365.986 269.488c0,-55.4858 7.6594,-95.6528 22.8245,-120.475 22.1585,-36.0171 53.001,-54.0001 92.6813,-54.0001 31.483,0 58.3293,13.5 80.3084,40.5 22.1841,27 33.1737,67.1414 33.1737,120.808 0,59.8407 -10.6566,102.851 -32.149,129.185 -21.518,26.334 -48.8511,39.501 -82.3578,39.501 -32.482,0 -59.6613,-13.0133 -81.6661,-39.0143 -21.8254,-26.001 -32.815,-64.8359 -32.815,-116.505z"/>
<glyph unicode="e" horiz-adv-x="556" d="M372.006 163.998l136.998 -23.0038c-17.4962,-50.1575 -45.3416,-88.3265 -83.1775,-114.66 -37.9896,-26.1547 -85.483,-39.3217 -142.327,-39.3217 -90.1709,0 -157.005,29.4848 -200.169,88.4802 -34.1727,47.3397 -51.3359,107.001 -51.3359,179.163 0,86.021 22.5171,153.521 67.3464,202.167 44.8293,48.8511 101.647,73.187 170.326,73.187 77.0039,0 137.844,-25.5143 182.494,-76.5172 44.4962,-51.0029 65.835,-129.16 63.8369,-234.495l-343.008 0c0.999052,-40.6537 12.0142,-72.3416 33.1737,-94.9868 21.0057,-22.6708 47.3397,-34.019 78.669,-34.019 21.4924,0 39.501,5.84061 54.0001,17.4962 14.6784,11.6812 25.668,30.5095 33.1737,56.5105zm7.99242 138.996c-0.999052,39.834 -11.1689,70.1642 -30.6632,90.8369 -19.4943,20.8264 -43.1642,31.1756 -71.1633,31.1756 -29.8435,0 -54.5124,-11.0152 -74.0067,-32.8406 -19.4943,-21.8254 -28.9981,-51.6689 -28.6651,-89.1718l204.498 0z"/>
<glyph unicode="i" horiz-adv-x="277" d="M72.0086 589.005l0 126.982 136.998 0 0 -126.982 -136.998 0zm0 -589.005l0 518.995 136.998 0 0 -518.995 -136.998 0z"/>
<glyph unicode="l" horiz-adv-x="277" d="M72.0086 0l0 715.987 136.998 0 0 -715.987 -136.998 0z"/>
<glyph unicode="m" horiz-adv-x="889" d="M61.9925 518.995l126.009 0 0 -70.8302c45.1623,54.5124 99.0086,81.8454 161.488,81.8454 33.1737,0 62.0181,-6.83966 86.354,-20.519 24.4896,-13.6537 44.4962,-34.3264 59.9944,-61.9925 22.8245,27.6661 47.4934,48.3388 73.8274,61.9925 26.334,13.6793 54.5124,20.519 84.5096,20.519 37.9896,0 70.1642,-7.68502 96.6519,-23.1831 26.334,-15.4981 46.0076,-38.1689 58.9953,-68.1661 9.5038,-22.0048 14.166,-57.8169 14.166,-107.334l0 -331.327 -136.998 0 0 296.155c0,51.5152 -4.66224,84.6889 -14.166,99.521 -12.6547,19.4943 -32.3283,29.3311 -58.6623,29.3311 -19.1613,0 -37.3236,-5.84061 -54.3331,-17.4962 -16.8302,-11.8349 -29.1518,-28.9981 -36.6575,-51.5152 -7.5057,-22.6708 -11.1689,-58.3293 -11.1689,-107.155l0 -248.841 -136.998 0 0 284.013c0,50.3112 -2.51044,82.9982 -7.32638,97.4972 -4.84156,14.6528 -12.3473,25.668 -22.6708,32.815 -10.1698,7.17268 -24.1822,10.6822 -41.6784,10.6822 -21.1594,0 -40.167,-5.6613 -56.9972,-17.0095 -17.0095,-11.5019 -28.9981,-27.8198 -36.3245,-49.3378 -7.352,-21.4924 -11.0152,-57.1509 -11.0152,-106.822l0 -251.838 -136.998 0 0 518.995z"/>
<glyph unicode="n" horiz-adv-x="610" d="M543.997 0l-136.998 0 0 264.493c0,55.9982 -2.99716,92.169 -8.83777,108.512 -5.99431,16.4972 -15.4981,29.1518 -28.8188,38.3226 -13.3463,9.17079 -29.3311,13.6793 -48.0057,13.6793 -24.0029,0 -45.4953,-6.50665 -64.5029,-19.4943 -19.1613,-13.0133 -32.1746,-30.3558 -39.168,-51.6689 -7.17268,-21.518 -10.6566,-61.1727 -10.6566,-119.169l0 -234.675 -136.998 0 0 518.995 126.982 0 0 -76.1585c45.4953,58.1756 102.672,87.1737 171.837,87.1737 30.3302,0 58.1756,-5.5076 83.3312,-16.3435 25.3349,-10.9896 44.3425,-24.8226 57.1765,-41.6784 12.9877,-16.9839 22.0048,-36.1452 27,-57.6632 5.17458,-21.4924 7.6594,-52.1556 7.6594,-92.169l0 -322.156z"/>
<glyph unicode="o" horiz-adv-x="610" d="M39.9877 265.825c0,45.6746 11.1689,89.8378 33.686,132.515 22.4915,42.8312 54.3331,75.3388 95.4991,97.8303 41.1661,22.4915 86.9944,33.8397 137.818,33.8397 78.5153,0 142.685,-25.5143 192.843,-76.5172 50.1575,-51.1566 75.1595,-115.48 75.1595,-193.483 0,-78.669 -25.3349,-143.838 -75.8255,-195.507 -50.6699,-51.6689 -114.327,-77.4906 -191.178,-77.4906 -47.4934,0 -92.835,10.8103 -135.999,32.3283 -42.9849,21.4924 -75.8255,53.001 -98.317,94.6538 -22.5171,41.4991 -33.686,92.169 -33.686,151.83zm141.02 -7.32638c0,-51.4896 12.1679,-90.9906 36.5038,-118.324 24.4896,-27.5124 54.4868,-41.1661 90.3246,-41.1661 35.6585,0 65.6557,13.6537 89.8378,41.1661 24.1566,27.333 36.3245,67.167 36.3245,119.323 0,50.8236 -12.1679,89.9915 -36.3245,117.325 -24.1822,27.5124 -54.1794,41.1661 -89.8378,41.1661 -35.8378,0 -65.835,-13.6537 -90.3246,-41.1661 -24.3359,-27.333 -36.5038,-66.834 -36.5038,-118.324z"/>
<glyph unicode="r" horiz-adv-x="389" d="M203.013 0l-137.024 0 0 518.995 127.008 0 0 -73.6737c21.8254,34.8387 41.4991,57.6889 58.9953,68.5247 17.4962,10.8103 37.3492,16.1642 59.5076,16.1642 31.3293,0 61.5058,-8.68407 90.5039,-25.8473l-42.4981 -119.656c-23.1831,14.9858 -44.6756,22.4915 -64.5029,22.4915 -19.3406,0 -35.6585,-5.32828 -49.0048,-15.8311 -13.3207,-10.6566 -23.8236,-29.6642 -31.5086,-57.3302 -7.6594,-27.6661 -11.4763,-85.6623 -11.4763,-173.835l0 -160.002z"/>
<glyph unicode="t" horiz-adv-x="332" d="M308.989 518.995l0 -108.999 -93.9878 0 0 -210.16c0,-42.6775 0.819735,-67.5001 2.66414,-74.4934 1.8444,-7.01898 5.84061,-12.834 12.3473,-17.5218 6.32733,-4.48293 13.9867,-6.81405 23.1575,-6.81405 12.834,0 31.1756,4.32923 55.3321,13.167l11.5019 -106.668c-31.8416,-13.6793 -67.8331,-20.4934 -108.179,-20.4934 -24.6689,0 -46.8274,4.14991 -66.6547,12.4753 -19.8273,8.35105 -34.3264,19.1869 -43.4972,32.3539 -9.3501,13.3207 -15.6774,31.1499 -19.3406,53.8207 -2.84346,16.0105 -4.32923,48.3388 -4.32923,97.1642l0 227.169 -62.9915 0 0 108.999 62.9915 0 0 103.005 136.998 81.0001 0 -184.005 93.9878 0z"/>
<glyph unicode="{" horiz-adv-x="389" d="M28.9981 200.989l0 117.017c23.8236,1.33207 41.6784,4.81594 53.8464,10.8359 11.9886,5.815 22.3122,15.6518 31.1499,29.4848 8.83777,13.833 14.8321,31.1756 18.1623,52.0019 2.51044,15.6774 3.84251,42.8312 3.84251,81.6661 0,63.1708 2.99716,107.18 8.83777,132.182 5.84061,25.0019 16.3179,44.983 31.6623,60.1481 15.3444,15.1651 37.6566,27 66.834,35.8378 19.8273,5.84061 51.1566,8.83777 93.8341,8.83777l25.8217 0 0 -116.992c-36.3245,0 -59.482,-1.9981 -69.8312,-6.17363 -10.3235,-3.99621 -17.8292,-10.1698 -22.8245,-18.4953 -4.84156,-8.35105 -7.32638,-22.5171 -7.32638,-42.6775 0,-20.4934 -1.33207,-59.5076 -3.99621,-116.659 -1.66509,-32.3283 -5.84061,-58.3293 -12.6803,-78.5153 -6.83966,-19.981 -15.4981,-36.4782 -26.1547,-49.4915 -10.5029,-12.9877 -26.667,-26.4877 -48.5181,-40.5 19.3406,-10.9896 35.0181,-24.0029 47.3397,-38.835 12.3473,-14.8321 21.6717,-32.8406 28.1784,-54.0001 6.66035,-21.1594 10.8359,-49.6708 12.834,-85.15 1.9981,-54.0001 2.99716,-88.6851 2.99716,-103.671 0,-21.518 2.66414,-36.5038 7.83872,-45.0086 5.14896,-8.32543 12.9877,-14.8321 23.6442,-19.1613 10.5029,-4.50854 33.353,-6.66035 68.4991,-6.66035l0 -117.017 -25.8217 0c-44.0095,0 -77.6699,3.50949 -101.16,10.5029 -23.3368,6.99337 -43.1642,18.6746 -59.1746,34.9924 -16.1642,16.1898 -27,36.3501 -32.5076,60.353 -5.48198,23.8236 -8.32543,61.6595 -8.32543,113.149 0,59.8407 -2.66414,98.8293 -7.83872,116.684 -7.17268,26.1547 -17.9829,44.8293 -32.482,55.9982 -14.4991,11.3226 -36.6831,17.6499 -66.6803,19.315z"/>
<glyph unicode="}" horiz-adv-x="389" d="M355.996 200.989c-23.8236,-1.33207 -41.6528,-4.81594 -53.667,-10.6566 -12.1679,-5.99431 -22.4915,-15.8311 -31.1499,-29.6642 -8.50475,-13.833 -14.6784,-31.1756 -18.3416,-52.0019 -2.51044,-15.6774 -3.84251,-42.6775 -3.84251,-81.1538 0,-63.1708 -2.81784,-107.334 -8.50475,-132.515 -5.6613,-25.0019 -16.1642,-45.1623 -31.483,-60.3274 -15.3444,-15.1651 -37.8359,-27 -67.3464,-35.8378 -19.8273,-5.84061 -51.1566,-8.83777 -93.8341,-8.83777l-25.8217 0 0 117.017c34.9924,0 57.8169,2.1518 68.6528,6.66035 10.6822,4.32923 18.6746,10.6566 23.6699,19.0076 5.17458,8.32543 7.68502,22.3122 7.83872,42.3188 0.179317,19.8273 1.51139,57.8426 3.84251,113.841 1.66509,33.9934 5.99431,60.9934 13.167,81.4868 7.14707,20.3397 16.6509,37.6822 28.4858,51.8482 12.0142,14.166 27.1793,26.4877 45.6746,37.3236 -24.0029,15.6774 -41.6784,30.9962 -52.668,45.8283 -15.3444,21.518 -25.8473,48.8511 -31.3293,82.1784 -3.50949,22.6708 -5.99431,72.8283 -7.32638,150.319 -0.333017,24.3359 -2.51044,40.6794 -6.68596,48.8511 -3.99621,7.99242 -11.3226,14.3197 -22.0048,18.649 -10.6566,4.50854 -34.3264,6.68596 -71.317,6.68596l0 116.992 25.8217 0c44.0095,0 77.6699,-3.50949 101.186,-10.3235 23.3112,-6.83966 42.9849,-18.5209 58.9953,-34.8387 15.9848,-16.4972 26.8207,-36.6831 32.482,-60.6604 5.68691,-23.8492 8.50475,-61.6851 8.50475,-113.175 0,-59.5076 2.51044,-98.4963 7.32638,-116.659 7.17268,-26.1803 18.0086,-44.8549 32.6869,-56.0238 14.6528,-11.3226 36.9905,-17.6499 66.9877,-19.315l0 -117.017z"/>
</font>
<style type="text/css">
<![CDATA[
@font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:bold;src:url("#FontID0") format(svg)}
.fil0 {fill:#373435}
.fil1 {fill:black}
.fnt0 {font-weight:bold;font-size:3.9037px;font-family:'Arial'}
]]>
</style>
</defs>
<g id="Layer_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g transform="matrix(0.576293 0 0 1 -8.27317 4.15816)">
<text x="17.5" y="12.5" class="fil0 fnt0">Nr. Comanda:{Article}</text>
</g>
<g transform="matrix(0.576293 0 0 1 -8.27317 7.72657)">
<text x="17.5" y="12.5" class="fil0 fnt0">Nr. Art.:{NrArt}</text>
</g>
<g transform="matrix(0.576293 0 0 1 -8.33607 11.0472)">
<text x="17.5" y="12.5" class="fil0 fnt0">Serial No.:{Serial}</text>
</g>
<g id="_1503865902032">
<path class="fil1" d="M12.82 7.2323c-0.1161,0.0271 -0.6403,0.41 -0.7023,0.417 -0.0438,0.005 -0.4125,-0.272 -0.4735,-0.3166 -0.0598,-0.0436 -0.0888,-0.0806 -0.163,-0.1004 0,0.0747 0.0169,0.2103 0.0281,0.3011 0.0331,0.269 0.0855,0.5861 0.1616,0.848 0.103,0.3544 0.2131,0.6687 0.3663,0.9726 0.2431,0.4823 0.4418,0.7936 0.7893,1.208 0.9583,1.1426 2.579,1.9598 4.0759,1.9598 1.0308,0 1.7035,-0.1145 2.5607,-0.512 0.3018,-0.1399 0.5873,-0.307 0.8502,-0.4886 0.4492,-0.3106 0.7746,-0.6264 1.1172,-1.0338 0.0352,-0.0419 0.0492,-0.052 0.0813,-0.0942 0.6831,-0.895 1.0205,-1.7074 1.1757,-2.8408 0.0122,-0.0889 0.0311,-0.2456 0.0311,-0.3201 -0.1148,-0.0768 -0.2364,-0.1435 -0.3512,-0.2195 -0.4402,-0.2912 -0.3377,-0.2606 -0.7083,0.016 -0.4465,0.3331 -0.1917,0.0604 -0.3623,0.801 -0.0383,0.1665 -0.0787,0.3029 -0.1284,0.4642 -0.2415,0.783 -0.8476,1.5453 -1.4754,2.0363 -0.1897,0.1484 -0.5097,0.3427 -0.7199,0.4433 -1.4392,0.6888 -3.2727,0.5256 -4.5139,-0.4835 -0.1901,-0.1546 -0.4507,-0.3749 -0.5984,-0.5649 -0.2922,-0.3762 -0.4778,-0.591 -0.6835,-1.0723 -0.1761,-0.412 -0.3573,-0.9691 -0.3573,-1.4206z"/>
<path class="fil1" d="M11.4812 6.5739c0.1103,0.0738 0.2091,0.1467 0.3248,0.2238 0.4133,0.2755 0.2641,0.2761 0.6717,0.0045 0.1215,-0.0811 0.227,-0.1511 0.3423,-0.2283 0,-0.7199 0.395,-1.6532 0.8432,-2.2515 0.2017,-0.2693 0.6154,-0.6932 0.9082,-0.8915 0.7337,-0.4969 1.3743,-0.7636 2.287,-0.8077 0.3218,-0.0155 0.0567,-0.0336 0.4622,-0.0011 0.2873,0.023 0.3574,0.0038 0.706,0.0841 0.5154,0.1187 1.0246,0.3291 1.4484,0.6147 0.8452,0.5696 1.4617,1.4005 1.7486,2.3776 0.063,0.2147 0.1562,0.6133 0.1562,0.8754 0.1055,-0.0282 0.5565,-0.4104 0.6145,-0.417 0.0786,0.021 0.2734,0.1575 0.3607,0.2099 0.0787,0.0473 0.2939,0.1908 0.3636,0.2071l-0.0687 -0.5898c-0.1423,-0.8307 -0.4834,-1.7021 -0.9879,-2.3701 -0.2122,-0.2809 -0.5138,-0.6564 -0.7903,-0.8778 -0.2016,-0.1613 -0.3013,-0.2735 -0.5584,-0.4512 -0.8199,-0.5666 -1.9324,-1.0006 -3.0378,-1.0006 -1.0533,0 -1.6632,0.1218 -2.5238,0.5051 -1.6549,0.7371 -2.8948,2.3661 -3.1985,4.176 -0.0172,0.1027 -0.0262,0.1807 -0.0409,0.2883 -0.0122,0.0889 -0.0311,0.2456 -0.0311,0.3201z"/>
<path class="fil1" d="M16.4195 7.4518l-1.4213 -1.41 -1.0534 1.0643 2.4473 2.4582 3.8738 -3.8629 -1.0537 -1.0423 -0.1758 0.164c-0.0315,0.0363 -0.0538,0.0557 -0.0893,0.0863l-0.6063 0.6008c-0.003,0.0034 -0.0071,0.0084 -0.0101,0.0119l-0.1755 0.1757c-0.0032,0.0032 -0.0077,0.0078 -0.0109,0.011l-0.2499 0.2549c-0.0507,0.0484 -0.0461,0.0309 -0.092,0.0836 -0.1863,0.2139 -1.3182,1.3079 -1.3829,1.4045z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,83 @@
# Configure Thermal Printer for 35mm x 25mm Labels
# Run as Administrator if needed
Write-Host "====================================" -ForegroundColor Cyan
Write-Host "Label Printer Paper Size Setup" -ForegroundColor Cyan
Write-Host "Target: 35mm x 25mm (Landscape)" -ForegroundColor Cyan
Write-Host "====================================" -ForegroundColor Cyan
Write-Host ""
# Get list of printers
$printers = Get-Printer | Select-Object Name, DriverName, PortName
Write-Host "Available Printers:" -ForegroundColor Yellow
$printers | Format-Table -AutoSize
Write-Host ""
Write-Host "MANUAL CONFIGURATION STEPS:" -ForegroundColor Green
Write-Host "===========================" -ForegroundColor Green
Write-Host ""
Write-Host "1. Open Control Panel > Devices and Printers" -ForegroundColor White
Write-Host "2. Right-click your thermal printer (e.g., Citizen CTS-310 or Zebra ZD420)" -ForegroundColor White
Write-Host "3. Select 'Printing Preferences'" -ForegroundColor White
Write-Host "4. Look for 'Paper Size' or 'Media' settings:" -ForegroundColor White
Write-Host " - Size: Custom or 35mm x 25mm" -ForegroundColor Cyan
Write-Host " - Width: 35mm (1.38 inches)" -ForegroundColor Cyan
Write-Host " - Height: 25mm (0.98 inches)" -ForegroundColor Cyan
Write-Host " - Orientation: Landscape" -ForegroundColor Cyan
Write-Host " - Media Type: LABELS (not Continuous)" -ForegroundColor Cyan
Write-Host "5. Click 'Apply' and 'OK'" -ForegroundColor White
Write-Host ""
Write-Host "IMPORTANT FOR ZEBRA PRINTERS:" -ForegroundColor Red
Write-Host "- Must set Media Type = 'Labels' (enables gap sensor)" -ForegroundColor Yellow
Write-Host "- If set to 'Continuous', printer will print on entire roll" -ForegroundColor Yellow
Write-Host ""
Write-Host "CRITICAL PRINTER SETTINGS:" -ForegroundColor Magenta
Write-Host "- Darkness: 10-15 (thermal printers)" -ForegroundColor White
Write-Host "- Speed: Medium" -ForegroundColor White
Write-Host "- Print Mode: Thermal Transfer or Direct Thermal" -ForegroundColor White
Write-Host ""
# Show printer properties access
Write-Host "To open printer properties directly, run:" -ForegroundColor Green
Write-Host 'control printers' -ForegroundColor Cyan
Write-Host ""
# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($isAdmin) {
Write-Host "✓ Running as Administrator" -ForegroundColor Green
} else {
Write-Host "⚠ Not running as Administrator (some settings may require elevation)" -ForegroundColor Yellow
}
Write-Host ""
Write-Host "For Citizen CTS-310 printers:" -ForegroundColor Cyan
Write-Host " 1. Open Devices and Printers" -ForegroundColor White
Write-Host " 2. Right-click 'Citizen CTS-310II' > Printer Properties" -ForegroundColor White
Write-Host " 3. Device Settings tab > Form to Tray Assignment" -ForegroundColor White
Write-Host " 4. Click 'Create Form' button" -ForegroundColor White
Write-Host " 5. Form name: Label35x25" -ForegroundColor White
Write-Host " 6. Width: 35mm, Height: 25mm" -ForegroundColor White
Write-Host " 7. Save and select this form in Printing Preferences" -ForegroundColor White
Write-Host ""
Write-Host "For Zebra ZD420/ZD421 printers:" -ForegroundColor Cyan
Write-Host " 1. Open Zebra Setup Utility (ZSU)" -ForegroundColor White
Write-Host " 2. Select your printer" -ForegroundColor White
Write-Host " 3. Click 'Printer Configuration'" -ForegroundColor White
Write-Host " 4. Media section:" -ForegroundColor White
Write-Host " - Media Type: Label Stock" -ForegroundColor Cyan
Write-Host " - Label Width: 35mm" -ForegroundColor Cyan
Write-Host " - Label Height: 25mm" -ForegroundColor Cyan
Write-Host " 5. Print section:" -ForegroundColor White
Write-Host " - Darkness: 10-15" -ForegroundColor Cyan
Write-Host " - Print Speed: 4 ips (medium)" -ForegroundColor Cyan
Write-Host " 6. Click 'Send to Printer'" -ForegroundColor White
Write-Host ""
Write-Host "After configuration, test print with:" -ForegroundColor Green
Write-Host " python label_printer_gui.py" -ForegroundColor Cyan
Write-Host ""
Write-Host "Press any key to open Printer settings..." -ForegroundColor Yellow
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
control printers

View File

@@ -28,10 +28,17 @@ from print_label import print_label_standalone, get_available_printers
from kivy.clock import Clock from kivy.clock import Clock
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
# Optional system tray support
try:
import pystray
from pystray import MenuItem as item
PYSTRAY_AVAILABLE = True
except ImportError:
PYSTRAY_AVAILABLE = False
print("Warning: pystray not available. System tray functionality disabled.")
# Set window size - portrait/phone dimensions (375x667 like iPhone) # Set window size - portrait/phone dimensions (375x667 like iPhone)
# Adjusted to be slightly wider for touch-friendly UI # Adjusted to be slightly wider for touch-friendly UI
Window.size = (420, 700) Window.size = (420, 700)
@@ -63,6 +70,8 @@ class LabelPrinterApp(App):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
# Initialize conf folder with default templates
self.initialize_conf_folder()
# Build printer display names and mapping to full names # Build printer display names and mapping to full names
full_printers = get_available_printers() full_printers = get_available_printers()
self.printer_display_map = {} # display_name -> full_name self.printer_display_map = {} # display_name -> full_name
@@ -109,6 +118,107 @@ class LabelPrinterApp(App):
"""Resolve display name back to full printer name for printing.""" """Resolve display name back to full printer name for printing."""
return self.printer_display_map.get(display_name, display_name) return self.printer_display_map.get(display_name, display_name)
def initialize_conf_folder(self):
"""
Initialize conf folder with default templates and configuration.
Creates folder and files if they don't exist.
"""
conf_dir = 'conf'
# Create conf folder if it doesn't exist
if not os.path.exists(conf_dir):
os.makedirs(conf_dir, exist_ok=True)
print(f"Created conf folder: {conf_dir}")
# Define default SVG template (minimal 35mm x 25mm with placeholders)
default_svg_template = '''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="35mm" height="25mm" viewBox="0 0 35 25">
<defs>
<style type="text/css">
.text { font-family: Arial; font-weight: bold; font-size: 3.5px; fill: #000000; }
.checkmark { fill: #008000; }
</style>
</defs>
<g id="content">
<text x="2" y="7" class="text">Nr. Comanda: {Article}</text>
<text x="2" y="12" class="text">Nr. Art.: {NrArt}</text>
<text x="2" y="17" class="text">Serial No.: {Serial}</text>
<circle cx="31" cy="7" r="2.5" class="checkmark"/>
<path d="M30,7 L30.8,7.8 L32,6" stroke="white" stroke-width="0.5" fill="none"/>
</g>
</svg>'''
# OK template (green checkmark)
ok_svg_template = '''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="35mm" height="25mm" viewBox="0 0 35 25">
<defs>
<style type="text/css">
.text { font-family: Arial; font-weight: bold; font-size: 3.5px; fill: #000000; }
.checkmark { fill: #008000; }
.ok-text { font-family: Arial; font-weight: bold; font-size: 4px; fill: #008000; }
</style>
</defs>
<g id="content">
<text x="2" y="7" class="text">Nr. Comanda: {Article}</text>
<text x="2" y="12" class="text">Nr. Art.: {NrArt}</text>
<text x="2" y="17" class="text">Serial No.: {Serial}</text>
<text x="29" y="5" class="ok-text">OK</text>
<circle cx="31" cy="12" r="2.5" class="checkmark"/>
<path d="M30,12 L30.8,12.8 L32,11" stroke="white" stroke-width="0.5" fill="none"/>
</g>
</svg>'''
# NOK template (red text and checkmark)
nok_svg_template = '''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="35mm" height="25mm" viewBox="0 0 35 25">
<defs>
<style type="text/css">
.text { font-family: Arial; font-weight: bold; font-size: 3.5px; fill: #CC0000; }
.checkmark { fill: #CC0000; }
.nok-text { font-family: Arial; font-weight: bold; font-size: 4px; fill: #CC0000; }
</style>
</defs>
<g id="content">
<text x="2" y="7" class="text">Nr. Comanda: {Article}</text>
<text x="2" y="12" class="text">Nr. Art.: {NrArt}</text>
<text x="2" y="17" class="text">Serial No.: {Serial}</text>
<text x="27" y="5" class="nok-text">NOK</text>
<circle cx="31" cy="12" r="2.5" class="checkmark"/>
<path d="M30,12 L30.8,12.8 L32,11" stroke="white" stroke-width="0.5" fill="none"/>
</g>
</svg>'''
# Create SVG template files if they don't exist
templates = {
'label_template.svg': default_svg_template,
'label_template_ok.svg': ok_svg_template,
'label_template_nok.svg': nok_svg_template,
}
for filename, content in templates.items():
file_path = os.path.join(conf_dir, filename)
if not os.path.exists(file_path):
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
print(f"Created template: {file_path}")
except Exception as e:
print(f"Failed to create {file_path}: {e}")
# Create default app.conf if it doesn't exist
config_path = os.path.join(conf_dir, 'app.conf')
if not os.path.exists(config_path):
default_config = '''[Settings]
file_path = C:\\Users\\Public\\Documents\\check.txt
printer = PDF
'''
try:
with open(config_path, 'w', encoding='utf-8') as f:
f.write(default_config)
print(f"Created config: {config_path}")
except Exception as e:
print(f"Failed to create {config_path}: {e}")
def cleanup_old_pdfs(self, days=5): def cleanup_old_pdfs(self, days=5):
""" """
Delete PDF files older than specified days from pdf_backup folder. Delete PDF files older than specified days from pdf_backup folder.
@@ -469,6 +579,10 @@ class LabelPrinterApp(App):
""" """
Minimize the application to system tray. Minimize the application to system tray.
""" """
if not PYSTRAY_AVAILABLE:
print("System tray not available - window will minimize normally")
return
if self.is_minimized: if self.is_minimized:
return return
@@ -652,9 +766,13 @@ class LabelPrinterApp(App):
print(f"Error clearing file: {e}") print(f"Error clearing file: {e}")
def read_file_variables(self): def read_file_variables(self):
"""Read variables from monitored file""" """Read variables from monitored file
Expected format: article;nr_art;serial;template_type;count
template_type: 0=OK, 1=NOK
count: number of labels to print (default=1)
"""
if not self.monitored_file or not os.path.exists(self.monitored_file): if not self.monitored_file or not os.path.exists(self.monitored_file):
return None, None, None return None, None, None, 0, 1
try: try:
with open(self.monitored_file, 'r', encoding='utf-8') as f: with open(self.monitored_file, 'r', encoding='utf-8') as f:
@@ -662,14 +780,16 @@ class LabelPrinterApp(App):
# Skip if file is empty or only contains "-" (cleared marker) # Skip if file is empty or only contains "-" (cleared marker)
if not content or content == '-': if not content or content == '-':
return None, None, None return None, None, None, 0, 1
# Parse file content - expecting format: article;nr_art;serial # Parse file content - expecting format: article;nr_art;serial;template_type;count
# or key=value pairs on separate lines # or key=value pairs on separate lines
lines = content.split('\n') lines = content.split('\n')
article = "" article = ""
nr_art = "" nr_art = ""
serial = "" serial = ""
template_type = 0 # Default to OK template
count = 1 # Default to 1 label
# Try semicolon-separated format first # Try semicolon-separated format first
if ';' in content: if ';' in content:
@@ -680,6 +800,20 @@ class LabelPrinterApp(App):
nr_art = parts[1].strip() nr_art = parts[1].strip()
if len(parts) >= 3: if len(parts) >= 3:
serial = parts[2].strip() serial = parts[2].strip()
if len(parts) >= 4:
try:
template_type = int(parts[3].strip())
except ValueError:
template_type = 0
if len(parts) >= 5:
try:
count = int(parts[4].strip())
if count < 1:
count = 1
if count > 100: # Safety limit
count = 100
except ValueError:
count = 1
else: else:
# Try key=value format # Try key=value format
for line in lines: for line in lines:
@@ -694,16 +828,30 @@ class LabelPrinterApp(App):
nr_art = value nr_art = value
elif key in ['serial', 'serial_no', 'serial-no', 'serialno']: elif key in ['serial', 'serial_no', 'serial-no', 'serialno']:
serial = value serial = value
elif key in ['template', 'template_type', 'type']:
try:
template_type = int(value)
except ValueError:
template_type = 0
elif key in ['count', 'quantity', 'copies']:
try:
count = int(value)
if count < 1:
count = 1
if count > 100:
count = 100
except ValueError:
count = 1
return article, nr_art, serial return article, nr_art, serial, template_type, count
except Exception as e: except Exception as e:
print(f"Error reading file: {e}") print(f"Error reading file: {e}")
return None, None, None return None, None, None
def print_label(self, instance): def print_label(self, instance):
"""Handle print button press - read from file and print""" """Handle print button press - read from file and print"""
# Read variables from file # Read variables from file including template type and count
article, nr_art, serial = self.read_file_variables() article, nr_art, serial, template_type, count = self.read_file_variables()
# Resolve display name to full printer name # Resolve display name to full printer name
printer = self._get_full_printer_name(self.printer_spinner.text) printer = self._get_full_printer_name(self.printer_spinner.text)
@@ -713,10 +861,23 @@ class LabelPrinterApp(App):
self.show_popup("Error", "No data in file or file not set", auto_dismiss_after=3) self.show_popup("Error", "No data in file or file not set", auto_dismiss_after=3)
return return
# Select template based on template_type
if template_type == 1:
template_path = os.path.join('conf', 'label_template_nok.svg')
template_name = "NOK"
else:
template_path = os.path.join('conf', 'label_template_ok.svg')
template_name = "OK"
# Verify template exists, fallback to default if not
if not os.path.exists(template_path):
template_path = os.path.join('conf', 'label_template.svg')
template_name = "DEFAULT"
# Create combined label text using semicolon separator # Create combined label text using semicolon separator
label_text = f"{article};{nr_art};{serial}" label_text = f"{article};{nr_art};{serial}"
# Show loading popup # Show loading popup with count info
popup = Popup( popup = Popup(
title='Printing', title='Printing',
content=BoxLayout( content=BoxLayout(
@@ -727,15 +888,32 @@ class LabelPrinterApp(App):
size_hint=(0.8, 0.3) size_hint=(0.8, 0.3)
) )
popup.content.add_widget(Label(text='Processing label...\nPlease wait')) popup.content.add_widget(Label(text=f'Processing {count} label(s)...\nTemplate: {template_name}\nPlease wait'))
popup.open() popup.open()
# Print in background thread (using PDF by default) # Print in background thread (using PDF by default)
def print_thread(): def print_thread():
pdf_filename = None pdf_filename = None
success = False success = False
all_success = True
try: try:
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True) # Print multiple copies if count > 1
for i in range(count):
if count > 1:
Clock.schedule_once(lambda dt, idx=i: popup.content.children[0].text.replace(
f'Processing {count} label(s)...',
f'Printing {idx+1} of {count}...'
), 0)
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True, svg_template=template_path)
if not success:
all_success = False
break
# Small delay between prints for multiple copies
if i < count - 1:
time.sleep(0.5)
# Get the PDF filename that was created # Get the PDF filename that was created
# Files are saved to pdf_backup/ with timestamp # Files are saved to pdf_backup/ with timestamp
@@ -744,7 +922,7 @@ class LabelPrinterApp(App):
# Get the most recently created PDF file # Get the most recently created PDF file
pdf_filename = max(pdf_files, key=os.path.getctime) pdf_filename = max(pdf_files, key=os.path.getctime)
if success: if all_success:
# Log the successful print action # Log the successful print action
self.log_print_action(article, nr_art, serial, printer, pdf_filename or "unknown", True) self.log_print_action(article, nr_art, serial, printer, pdf_filename or "unknown", True)
@@ -753,7 +931,7 @@ class LabelPrinterApp(App):
# Use Clock.schedule_once to update UI from main thread # Use Clock.schedule_once to update UI from main thread
Clock.schedule_once(lambda dt: popup.dismiss(), 0) Clock.schedule_once(lambda dt: popup.dismiss(), 0)
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!", auto_dismiss_after=3), 0.1) Clock.schedule_once(lambda dt: self.show_popup("Success", f"{count} label(s) printed successfully!", auto_dismiss_after=3), 0.1)
else: else:
# Log the failed print action # Log the failed print action
self.log_print_action(article, nr_art, serial, printer, pdf_filename or "unknown", False) self.log_print_action(article, nr_art, serial, printer, pdf_filename or "unknown", False)

View File

@@ -3,6 +3,7 @@ import barcode
from barcode.writer import ImageWriter from barcode.writer import ImageWriter
import time import time
import os import os
import sys
import datetime import datetime
import platform import platform
import subprocess import subprocess
@@ -173,7 +174,14 @@ def create_label_image(text):
# Resize barcode to fit in row width # Resize barcode to fit in row width
barcode_width = label_width - left_margin - 10 barcode_width = label_width - left_margin - 10
barcode_height = row_height - 25 barcode_height = row_height - 25
barcode_resized = barcode_img.resize((barcode_width, barcode_height), Image.LANCZOS) # Use high-quality resampling for crisp barcodes
try:
# Try newer Pillow API first
from PIL.Image import Resampling
barcode_resized = barcode_img.resize((barcode_width, barcode_height), Resampling.LANCZOS)
except (ImportError, AttributeError):
# Fallback for older Pillow versions
barcode_resized = barcode_img.resize((barcode_width, barcode_height), Image.LANCZOS)
label_img.paste(barcode_resized, (left_margin, row_y + 20)) label_img.paste(barcode_resized, (left_margin, row_y + 20))
else: else:
# Fallback: show value as text # Fallback: show value as text
@@ -194,13 +202,14 @@ def create_label_image(text):
return label_img return label_img
def create_label_pdf(text): def create_label_pdf(text, svg_template=None):
""" """
Create a high-quality PDF label with 3 rows: label + barcode for each field. Create a high-quality PDF label with 3 rows: label + barcode for each field.
PDFs are saved to the pdf_backup folder. PDFs are saved to the pdf_backup folder.
Args: Args:
text (str): Combined text in format "article;nr_art;serial" or single value text (str): Combined text in format "article;nr_art;serial" or single value
svg_template (str): Path to specific SVG template to use (optional)
Returns: Returns:
str: Path to the generated PDF file str: Path to the generated PDF file
@@ -221,11 +230,17 @@ def create_label_pdf(text):
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
pdf_filename = os.path.join(pdf_backup_dir, f"final_label_{timestamp}.pdf") pdf_filename = os.path.join(pdf_backup_dir, f"final_label_{timestamp}.pdf")
# Check for default SVG template # Use SVG template for customizable layout
svg_template = None if svg_template is None or not os.path.exists(svg_template):
default_svg = os.path.join('conf', 'label_template.svg') # Try default templates
if os.path.exists(default_svg): default_svg = os.path.join('conf', 'label_template.svg')
svg_template = default_svg if os.path.exists(default_svg):
svg_template = default_svg
print(f"Using SVG template: {default_svg}")
else:
print("SVG template not found, using fallback PDF generation")
else:
print(f"Using SVG template: {svg_template}")
# Check for default image path # Check for default image path
image_path = os.path.join('conf', 'accepted.png') image_path = os.path.join('conf', 'accepted.png')
@@ -233,9 +248,94 @@ def create_label_pdf(text):
return generator.create_label_pdf(article, nr_art, serial, pdf_filename, image_path, svg_template) return generator.create_label_pdf(article, nr_art, serial, pdf_filename, image_path, svg_template)
def configure_printer_quality(printer_name, width_mm=35, height_mm=25):
"""
Configure printer for high quality label printing (Windows only).
Sets paper size, orientation, and QUALITY settings.
Args:
printer_name (str): Name of the printer
width_mm (int): Label width in millimeters (default 35)
height_mm (int): Label height in millimeters (default 25)
Returns:
bool: True if successful
"""
if SYSTEM != "Windows" or not WIN32_AVAILABLE:
return False
try:
import win32print
import pywintypes
hprinter = win32print.OpenPrinter(printer_name)
try:
# Get current printer properties
props = win32print.GetPrinter(hprinter, 2)
devmode = props.get("pDevMode")
if devmode is None:
print("Could not get printer DEVMODE")
return False
# CRITICAL: Set print quality to HIGHEST
# This prevents dotted/pixelated text
try:
devmode.PrintQuality = 600 # 600 DPI (high quality)
except:
try:
devmode.PrintQuality = 4 # DMRES_HIGH
except:
pass
# Set custom paper size
try:
devmode.PaperSize = 256 # DMPAPER_USER (custom size)
devmode.PaperLength = height_mm * 10 # Height in 0.1mm units
devmode.PaperWidth = width_mm * 10 # Width in 0.1mm units
except:
pass
# Set orientation to landscape
try:
devmode.Orientation = 2 # Landscape
except:
pass
# Set additional quality settings
try:
devmode.Color = 1 # Monochrome for labels
except:
pass
try:
devmode.TTOption = 2 # DMTT_BITMAP - print TrueType as graphics (sharper)
except:
pass
# Apply settings
try:
props["pDevMode"] = devmode
win32print.SetPrinter(hprinter, 2, props, 0)
print(f"Printer configured: {width_mm}x{height_mm}mm @ HIGH QUALITY")
return True
except Exception as set_err:
print(f"Could not apply printer settings: {set_err}")
return False
finally:
win32print.ClosePrinter(hprinter)
except Exception as e:
print(f"Could not configure printer quality: {e}")
return False
def print_to_printer(printer_name, file_path): def print_to_printer(printer_name, file_path):
""" """
Print file to printer (cross-platform). Print file to printer (cross-platform).
Uses SumatraPDF for silent printing on Windows.
Args: Args:
printer_name (str): Name of printer or "PDF" for PDF output printer_name (str): Name of printer or "PDF" for PDF output
@@ -258,66 +358,145 @@ def print_to_printer(printer_name, file_path):
return True return True
elif SYSTEM == "Windows": elif SYSTEM == "Windows":
# Windows: Print PDF using various methods # Windows: Print PDF silently without any viewer opening
try: try:
if WIN32_AVAILABLE:
import win32print
import win32api
if file_path.endswith('.pdf'): if file_path.endswith('.pdf'):
# Method 1: Try SumatraPDF for best silent printing # Try silent printing methods (no viewer opens)
sumatra_paths = [ import os
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf', 'SumatraPDF.exe'), import winreg
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf', 'SumatraPDF-portable.exe'),
printed = False
# Method 1: SumatraPDF (bundled inside exe or external)
sumatra_paths = []
# Get the directory where this script/exe is running
if getattr(sys, 'frozen', False):
# Running as compiled executable
# PyInstaller extracts bundled files to sys._MEIPASS temp folder
if hasattr(sys, '_MEIPASS'):
# Check bundled version first (inside the exe)
bundled_sumatra = os.path.join(sys._MEIPASS, 'SumatraPDF.exe')
sumatra_paths.append(bundled_sumatra)
# Also check app directory for external version
app_dir = os.path.dirname(sys.executable)
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
else:
# Running as script - check local folders
app_dir = os.path.dirname(os.path.abspath(__file__))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
sumatra_paths.append(os.path.join(app_dir, "conf", "SumatraPDF.exe"))
# Then check system installations
sumatra_paths.extend([
r"C:\Program Files\SumatraPDF\SumatraPDF.exe", r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe", r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
os.path.expandvars(r"%LOCALAPPDATA%\SumatraPDF\SumatraPDF.exe"), ])
]
sumatra_found = False
for sumatra_path in sumatra_paths: for sumatra_path in sumatra_paths:
if os.path.exists(sumatra_path): if os.path.exists(sumatra_path):
sumatra_found = True
try: try:
# Use SumatraPDF silent printing with high quality settings # Use noscale with paper size specification for thermal printers
# noscale = print at actual size without scaling # Format: "noscale,paper=<size>" where paper size matches PDF (35mm x 25mm)
# landscape = force landscape orientation for 35x25mm labels # SumatraPDF will use the PDF's page size when noscale is used
# Note: SumatraPDF uses printer driver's quality settings
subprocess.run([ subprocess.run([
sumatra_path, sumatra_path,
'-print-to', printer_name, "-print-to",
'-silent', printer_name,
'-print-settings', 'noscale,landscape', file_path,
file_path "-print-settings",
], check=True, creationflags=subprocess.CREATE_NO_WINDOW) "noscale", # Preserve exact PDF page dimensions
"-silent",
"-exit-when-done"
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via SumatraPDF: {printer_name}") print(f"Label sent to printer via SumatraPDF: {printer_name}")
return True print(f"Note: Printer '{printer_name}' should be configured for 35mm x 25mm labels")
except Exception as sumatra_err: printed = True
print(f"SumatraPDF print failed: {sumatra_err}")
break break
except Exception as e:
print(f"SumatraPDF error: {e}")
# Method 2: Use ShellExecute with printto (requires default PDF viewer) # Method 2: Adobe Reader silent printing
if not sumatra_found or WIN32_AVAILABLE: if not printed:
try: adobe_path = None
import win32api for key_path in [
win32api.ShellExecute( r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe",
0, "printto", file_path, r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Acrobat.exe"
f'"{printer_name}"', ".", 0 ]:
)
print(f"Label sent to printer via ShellExecute: {printer_name}")
return True
except Exception as shell_err:
print(f"ShellExecute print failed: {shell_err}")
# Method 3: Open PDF with default viewer (last resort)
try: try:
os.startfile(file_path, "print") key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path)
print(f"Opened PDF for printing: {file_path}") adobe_path, _ = winreg.QueryValueEx(key, "")
print("Please close the PDF viewer after printing.") winreg.CloseKey(key)
return True break
except Exception as startfile_err: except:
print(f"Could not open PDF: {startfile_err}") pass
print("PDF saved as backup only")
return True if adobe_path and os.path.exists(adobe_path):
try:
subprocess.run([
adobe_path,
"/t", # Print and close
file_path,
printer_name
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via Adobe Reader: {printer_name}")
printed = True
except:
pass
# Method 3: GhostScript (if installed)
if not printed:
gs_paths = [
r"C:\Program Files\gs\gs10.02.1\bin\gswin64c.exe",
r"C:\Program Files (x86)\gs\gs10.02.1\bin\gswin32c.exe",
]
# Try to find gswin in PATH
try:
gs_result = subprocess.run(['where', 'gswin64c'],
capture_output=True, text=True, check=False)
if gs_result.returncode == 0:
gs_paths.insert(0, gs_result.stdout.strip().split('\n')[0])
except:
pass
for gs_path in gs_paths:
if os.path.exists(gs_path):
try:
subprocess.run([
gs_path,
"-dNOPAUSE", "-dBATCH", "-dQUIET",
f"-sDEVICE=mswinpr2",
f"-sOutputFile=%printer%{printer_name}",
file_path
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via GhostScript: {printer_name}")
printed = True
break
except:
pass
if not printed:
# Fallback: Let user know and save PDF
print("=" * 60)
print("NOTICE: Silent PDF printing requires SumatraPDF")
print("SumatraPDF not found (should be bundled inside the app)")
print("If you built the app yourself, ensure SumatraPDF.exe is downloaded first.")
print("=" * 60)
print(f"PDF saved to: {file_path}")
print("The PDF can be printed manually.")
return True
else: else:
# Non-PDF files: print silently with notepad # Non-PDF files
subprocess.run(['notepad', '/p', file_path], subprocess.run(['notepad', '/p', file_path],
check=False, check=False,
creationflags=subprocess.CREATE_NO_WINDOW) creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer: {printer_name}") print(f"Label sent to printer: {printer_name}")
return True return True
@@ -343,7 +522,7 @@ def print_to_printer(printer_name, file_path):
return True return True
def print_label_standalone(value, printer, preview=0, use_pdf=True): def print_label_standalone(value, printer, preview=0, use_pdf=True, svg_template=None):
""" """
Print a label with the specified text on the specified printer. Print a label with the specified text on the specified printer.
Always generates a PDF backup in pdf_backup and prints that PDF. Always generates a PDF backup in pdf_backup and prints that PDF.
@@ -353,6 +532,7 @@ def print_label_standalone(value, printer, preview=0, use_pdf=True):
printer (str): The name of the printer to use printer (str): The name of the printer to use
preview (int): 0 = no preview, 1-3 = 3s preview, >3 = 5s preview preview (int): 0 = no preview, 1-3 = 3s preview, >3 = 5s preview
use_pdf (bool): False to also generate a PNG if PDF generation fails use_pdf (bool): False to also generate a PNG if PDF generation fails
svg_template (str): Path to specific SVG template to use (optional)
Returns: Returns:
bool: True if printing was successful, False otherwise bool: True if printing was successful, False otherwise
@@ -370,7 +550,7 @@ def print_label_standalone(value, printer, preview=0, use_pdf=True):
# Always generate a PDF backup and print that PDF for verification # Always generate a PDF backup and print that PDF for verification
try: try:
pdf_file = create_label_pdf(value) pdf_file = create_label_pdf(value, svg_template)
if pdf_file and os.path.exists(pdf_file): if pdf_file and os.path.exists(pdf_file):
print(f"PDF label created: {pdf_file}") print(f"PDF label created: {pdf_file}")
print(f"PDF backup saved to: {pdf_file}") print(f"PDF backup saved to: {pdf_file}")

View File

@@ -38,17 +38,19 @@ except (ImportError, OSError) as e:
class PDFLabelGenerator: class PDFLabelGenerator:
"""Generate high-quality PDF labels with image and text""" """Generate high-quality PDF labels with image and text"""
def __init__(self, label_width=3.5, label_height=2.5, dpi=600): def __init__(self, label_width=3.5, label_height=2.5, dpi=1200):
""" """
Initialize PDF label generator. Initialize PDF label generator.
Args: Args:
label_width (float): Width in cm (default 3.5 cm = 35mm) label_width (float): Width in cm (default 3.5 cm = 35mm)
label_height (float): Height in cm (default 2.5 cm = 25mm) label_height (float): Height in cm (default 2.5 cm = 25mm)
dpi (int): DPI for image rendering (default 600 for high quality print) dpi (int): DPI for image rendering (default 1200 for high quality thermal printer)
""" """
self.label_width = label_width * cm self.label_width = label_width * cm
self.label_height = label_height * cm self.label_height = label_height * cm
# Force landscape: ensure width > height
self.page_size = landscape((self.label_height, self.label_width)) if self.label_width > self.label_height else (self.label_width, self.label_height)
self.dpi = dpi self.dpi = dpi
self.margin = 1 * mm # Minimal margin self.margin = 1 * mm # Minimal margin
@@ -68,9 +70,11 @@ class PDFLabelGenerator:
try: try:
img = Image.open(image_path) img = Image.open(image_path)
# Convert to RGB if needed # Convert to RGB for best quality (don't use grayscale)
if img.mode not in ['RGB', 'L']: if img.mode != 'RGB':
img = img.convert('RGB') img = img.convert('RGB')
# Set DPI information for high-quality output
img.info['dpi'] = (self.dpi, self.dpi)
return img return img
except Exception as e: except Exception as e:
print(f"Image loading error: {e}") print(f"Image loading error: {e}")
@@ -130,42 +134,25 @@ class PDFLabelGenerator:
else: else:
pdf_output = tempfile.NamedTemporaryFile(suffix='.pdf', delete=False).name pdf_output = tempfile.NamedTemporaryFile(suffix='.pdf', delete=False).name
# Try svglib first (more portable, no external dependencies) # Use cairosvg FIRST as it handles fonts and complex SVGs better
if SVG_AVAILABLE:
try:
drawing = svg2rlg(temp_svg_path)
if drawing:
# Render at original size - quality depends on PDF rendering
# The PDF will contain vector graphics for sharp output
renderPDF.drawToFile(drawing, pdf_output)
# Clean up temp SVG
try:
os.remove(temp_svg_path)
except:
pass
if filename:
return pdf_output
else:
with open(pdf_output, 'rb') as f:
pdf_bytes = f.read()
os.remove(pdf_output)
return pdf_bytes
except Exception as svg_err:
print(f"svglib conversion failed: {svg_err}, trying cairosvg...")
# Fallback: Try cairosvg (requires system Cairo library)
if CAIROSVG_AVAILABLE: if CAIROSVG_AVAILABLE:
try: try:
# Render at high DPI for sharp output print("Converting SVG to PDF using CairoSVG (high quality)...")
cairosvg.svg2pdf(url=temp_svg_path, write_to=pdf_output, dpi=self.dpi) # CRITICAL: Let CairoSVG read dimensions from SVG file (width="35mm" height="25mm")
# DO NOT specify output_width/output_height as they control raster size, not PDF page size
# The SVG already has the correct dimensions, just render at high DPI
cairosvg.svg2pdf(
url=temp_svg_path,
write_to=pdf_output,
dpi=300 # High DPI for sharp output, page size comes from SVG
)
# Clean up temp SVG # Clean up temp SVG
try: try:
os.remove(temp_svg_path) os.remove(temp_svg_path)
except: except:
pass pass
print(f"✅ PDF created from SVG template: {pdf_output}")
if filename: if filename:
return pdf_output return pdf_output
else: else:
@@ -174,9 +161,54 @@ class PDFLabelGenerator:
os.remove(pdf_output) os.remove(pdf_output)
return pdf_bytes return pdf_bytes
except Exception as cairo_err: except Exception as cairo_err:
print(f"CairoSVG conversion failed: {cairo_err}") print(f"CairoSVG conversion failed: {cairo_err}, trying svglib...")
print("SVG conversion failed. svglib and cairosvg both unavailable or failed.") # Fallback: Try svglib (generates many warnings but works)
if SVG_AVAILABLE:
try:
print("Converting SVG to PDF using svglib (fallback)...")
drawing = svg2rlg(temp_svg_path)
if drawing:
# CRITICAL: Force exact label dimensions (35mm x 25mm landscape)
# Convert to points: 1mm = 2.834645669 points
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas as pdf_canvas
target_width = 35 * mm
target_height = 25 * mm
# Scale drawing to exact size
if drawing.width > 0 and drawing.height > 0:
scale_x = target_width / drawing.width
scale_y = target_height / drawing.height
drawing.width = target_width
drawing.height = target_height
drawing.scale(scale_x, scale_y)
# Create PDF with explicit landscape page size
c = pdf_canvas.Canvas(pdf_output, pagesize=(target_width, target_height))
c.setPageCompression(0) # No compression for quality
renderPDF.draw(drawing, c, 0, 0)
c.save()
# Clean up temp SVG
try:
os.remove(temp_svg_path)
except:
pass
print(f"✅ PDF created from SVG template: {pdf_output}")
if filename:
return pdf_output
else:
with open(pdf_output, 'rb') as f:
pdf_bytes = f.read()
os.remove(pdf_output)
return pdf_bytes
except Exception as svg_err:
print(f"svglib conversion failed: {svg_err}")
print("❌ SVG conversion failed. Both cairosvg and svglib unavailable or failed.")
return None return None
except Exception as e: except Exception as e:
@@ -226,11 +258,15 @@ class PDFLabelGenerator:
else: else:
pdf_buffer = io.BytesIO() pdf_buffer = io.BytesIO()
# Create canvas with label dimensions # Create canvas with label dimensions - explicitly landscape
c = canvas.Canvas(pdf_buffer, pagesize=(self.label_width, self.label_height)) c = canvas.Canvas(pdf_buffer, pagesize=self.page_size)
# Set higher resolution for better quality # CRITICAL: Disable compression for maximum print quality
c.setPageCompression(1) # Enable compression c.setPageCompression(0) # Disable compression for best quality
# Set high resolution for crisp output on thermal printers
# Page size already set to landscape orientation
c._pagesize = self.page_size
# Calculate dimensions # Calculate dimensions
usable_width = self.label_width - 2 * self.margin usable_width = self.label_width - 2 * self.margin
@@ -257,11 +293,11 @@ class PDFLabelGenerator:
temp_img_path = temp_img_file.name temp_img_path = temp_img_file.name
temp_img_file.close() temp_img_file.close()
# Convert to grayscale for black and white # Keep as RGB for better quality (thermal printers handle conversion)
img_bw = img.convert('L') # Save at high DPI for sharp output
img_bw.save(temp_img_path, 'PNG') img.save(temp_img_path, 'PNG', dpi=(self.dpi, self.dpi), optimize=False)
# Draw image maintaining aspect ratio # Draw image maintaining aspect ratio with high quality
c.drawImage( c.drawImage(
temp_img_path, temp_img_path,
image_x, image_x,
@@ -269,7 +305,8 @@ class PDFLabelGenerator:
width=image_width, width=image_width,
height=image_height, height=image_height,
preserveAspectRatio=True, preserveAspectRatio=True,
anchor='c' anchor='c',
mask='auto' # Better quality rendering
) )
# Clean up # Clean up
@@ -291,10 +328,15 @@ class PDFLabelGenerator:
else: else:
text = f"{label_name} -" text = f"{label_name} -"
# Use appropriate font size to fit (6pt = ~2.1mm height) # IMPROVED: Larger font size for better readability (8pt = ~2.8mm height)
font_size = 6 # This is critical for thermal printers - text must be crisp and readable
font_size = 8
c.setFont("Helvetica-Bold", font_size) c.setFont("Helvetica-Bold", font_size)
# Enable text rendering mode for crisp output
c.setStrokeColorRGB(0, 0, 0)
c.setFillColorRGB(0, 0, 0)
try: try:
c.drawString(text_area_x, y_position, text) c.drawString(text_area_x, y_position, text)
except Exception as e: except Exception as e:

View File

@@ -1,6 +1,6 @@
python-barcode python-barcode
pillow pillow
kivy>=2.1.0 kivy>=2.3.0
reportlab reportlab
pyinstaller>=6.0.0 pyinstaller>=6.0.0
pywin32 pywin32

63
test_conf_generation.py Normal file
View File

@@ -0,0 +1,63 @@
"""
Test script to verify conf folder auto-generation
Run this to test that conf/ folder is created with default templates
"""
import os
import shutil
# Temporarily rename existing conf folder to test auto-generation
if os.path.exists('conf'):
print("Backing up existing conf folder to conf_backup...")
if os.path.exists('conf_backup'):
shutil.rmtree('conf_backup')
shutil.move('conf', 'conf_backup')
print("✓ Existing conf folder backed up")
print("\nTesting conf folder auto-generation...")
print("Starting label_printer_gui initialization...")
# Import the app (this will trigger initialization)
from label_printer_gui import LabelPrinterApp
# Create app instance (will auto-create conf folder)
app = LabelPrinterApp()
# Check if conf folder was created
if os.path.exists('conf'):
print("\n✓ SUCCESS: conf folder created")
# Check for required files
required_files = [
'conf/app.conf',
'conf/label_template.svg',
'conf/label_template_ok.svg',
'conf/label_template_nok.svg'
]
missing_files = []
for file in required_files:
if os.path.exists(file):
print(f"{file}")
else:
print(f"{file} MISSING")
missing_files.append(file)
if not missing_files:
print("\n✓ All required files created successfully!")
else:
print(f"\n✗ Missing files: {missing_files}")
else:
print("\n✗ FAILED: conf folder not created")
# Restore original conf folder
print("\nRestoring original conf folder...")
if os.path.exists('conf_backup'):
if os.path.exists('conf'):
shutil.rmtree('conf')
shutil.move('conf_backup', 'conf')
print("✓ Original conf folder restored")
print("\n" + "="*50)
print("Test complete!")
print("="*50)