Compare commits
No commits in common. "970ab2ff726f04ab2529f43b6551dfcd1ea42836" and "89ca008b87eab4472d66e9e1ce4696070c35f384" have entirely different histories.
970ab2ff72
...
89ca008b87
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -160,4 +160,3 @@ cython_debug/
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
labels/
|
|
54
README.md
54
README.md
|
@ -1,57 +1,3 @@
|
||||||
# thermatext
|
# thermatext
|
||||||
|
|
||||||
fancy name, QL-570 text formater
|
fancy name, QL-570 text formater
|
||||||
|
|
||||||
|
|
||||||
### TODO
|
|
||||||
- use as printer
|
|
||||||
- works, but cant detect the length of text, will output a set (page?) length
|
|
||||||
- fix missing font defentions
|
|
||||||
- fail to take into account letter width
|
|
||||||
- telegram bot (!)
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
`sudo brother_ql -b pyusb --model QL-570 -p usb://0x04f9:0x2028/000M12345678 print -l 62 logo.png`
|
|
||||||
## python
|
|
||||||
python tries to detect letter width and maximise it to fit the label printer constraints (62mm width)
|
|
||||||
`python thermatext.py "label to print"`
|
|
||||||
|
|
||||||
## inkscape
|
|
||||||
[gptgist](https://gist.github.com/5shekel/98cc4898a1a8c7a5a1b305acc01d4326)
|
|
||||||
|
|
||||||
inskcape user extension path
|
|
||||||
`/home/${USER}/.config/inkscape/extensions`
|
|
||||||
|
|
||||||
symbolic link the dir fro here to the inkscape extension
|
|
||||||
`ln -s /home/${USER}/yair/thermatext/inkscape /home/${USER}/.config/inkscape/extensions/thermatext`
|
|
||||||
then check
|
|
||||||
```bash
|
|
||||||
find ~/.config/inkscape/ -type l
|
|
||||||
```
|
|
||||||
|
|
||||||
takes from the [inkscape-silhouette](https://github.com/fablabnbg/inkscape-silhouette) extension, as in, how to install.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git clone https://github.com/fablabnbg/inkscape-silhouette
|
|
||||||
Cloning into 'inkscape-silhouette'...
|
|
||||||
remote: Enumerating objects: 2764, done.
|
|
||||||
remote: Counting objects: 100% (795/795), done.
|
|
||||||
remote: Compressing objects: 100% (364/364), done.
|
|
||||||
remote: Total 2764 (delta 483), reused 657 (delta 416), pack-reused 1969
|
|
||||||
Receiving objects: 100% (2764/2764), 8.53 MiB | 3.64 MiB/s, done.
|
|
||||||
Resolving deltas: 100% (1786/1786), done.
|
|
||||||
|
|
||||||
$ cd inkscape-silhouette/
|
|
||||||
|
|
||||||
$ make install-local
|
|
||||||
|
|
||||||
mkdir -p locale
|
|
||||||
mkdir -p /home/devdesk/.config/inkscape/extensions
|
|
||||||
cp -r silhouette /home/devdesk/.config/inkscape/extensions
|
|
||||||
install -m 755 *silhouette*.py /home/devdesk/.config/inkscape/extensions
|
|
||||||
install -m 644 *.inx /home/devdesk/.config/inkscape/extensions
|
|
||||||
cp -r locale /home/devdesk/.config/inkscape/extensions
|
|
||||||
mkdir -p /home/devdesk/.config/inkscape/templates
|
|
||||||
install -m 644 templates/*.svg /home/devdesk/.config/inkscape/templates
|
|
||||||
|
|
||||||
```
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
sudo brother_ql -b pyusb --model QL-570 -p usb://0x04f9:0x2028/000M6Z401370 print -l 62 ~/text285.png
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
|
||||||
<_name>therma</_name>
|
|
||||||
<id>org.inkscape.effect.exportandrunscript</id>
|
|
||||||
<dependency type="executable" location="extensions">thermatext/thermo.py</dependency>
|
|
||||||
<effect>
|
|
||||||
<object-type>all</object-type>
|
|
||||||
</effect>
|
|
||||||
<script>
|
|
||||||
<command reldir="extensions" interpreter="python">thermatext/thermo.py</command>
|
|
||||||
</script>
|
|
||||||
<category>Fx</category>
|
|
||||||
</inkscape-extension>
|
|
|
@ -1,69 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import inkex
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
class ExportAndRunScript(inkex.EffectExtension):
|
|
||||||
|
|
||||||
def effect(self):
|
|
||||||
# Define the export file path
|
|
||||||
png_file_path = "/home/devdesk/yair/thermatext/labels/exported.png"
|
|
||||||
|
|
||||||
# Get the current selection
|
|
||||||
selection = self.svg.selected
|
|
||||||
|
|
||||||
# If there's no selection, show an error and exit
|
|
||||||
if not selection:
|
|
||||||
inkex.errormsg("No selection to export.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the IDs of the selected elements
|
|
||||||
selected_ids = ",".join(element.get('id') for element in selection.values())
|
|
||||||
|
|
||||||
# Convert objects to paths
|
|
||||||
self.convert_to_path(selected_ids)
|
|
||||||
|
|
||||||
# Run the Inkscape CLI command to export the selection to a PNG
|
|
||||||
self.run_export_command(selected_ids, png_file_path)
|
|
||||||
|
|
||||||
# Run the bash script on the exported PNG
|
|
||||||
self.run_script_on_png(png_file_path)
|
|
||||||
|
|
||||||
def convert_to_path(self, selected_ids):
|
|
||||||
# Create a command that calls Inkscape's command line interface to convert objects to paths
|
|
||||||
command = [
|
|
||||||
"inkscape",
|
|
||||||
"--select={}".format(selected_ids),
|
|
||||||
"--verb=ObjectToPath",
|
|
||||||
self.options.input_file
|
|
||||||
]
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
subprocess.check_call(command)
|
|
||||||
|
|
||||||
def run_export_command(self, selected_ids, png_file_path):
|
|
||||||
# Create a command that calls Inkscape's command line interface to export the selection to a PNG
|
|
||||||
command = [
|
|
||||||
"inkscape",
|
|
||||||
"--export-type=png",
|
|
||||||
"--export-id={}".format(selected_ids),
|
|
||||||
"--export-filename={}".format(png_file_path),
|
|
||||||
self.options.input_file
|
|
||||||
]
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
subprocess.check_call(command)
|
|
||||||
|
|
||||||
def run_script_on_png(self, png_file_path):
|
|
||||||
# Define the path to your bash script
|
|
||||||
# bash_script_path = "print.sh"
|
|
||||||
|
|
||||||
# Create a command that calls the bash script with the PNG as an argument
|
|
||||||
command = ["sudo", "brother_ql", "-b", "pyusb", "--model", "QL-570", "-p", "usb://0x04f9:0x2028/000M6Z401370", "print", "-l", "62", png_file_path]
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
subprocess.check_call(command)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
ExportAndRunScript().run()
|
|
|
@ -1,33 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "Usage: $0 <text_string>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
text_string="$1"
|
|
||||||
filename="$(echo "$text_string" | tr ' ' '_').png"
|
|
||||||
|
|
||||||
# Set the desired image width and calculate the height
|
|
||||||
image_width=200
|
|
||||||
image_height=$((image_width / 4))
|
|
||||||
|
|
||||||
# Calculate the point size based on the length of the text string
|
|
||||||
point_size=$((image_width / ${#text_string} * 13 / 10))
|
|
||||||
|
|
||||||
echo "font_size ${point_size}"
|
|
||||||
# Set the desired font
|
|
||||||
font_file="/home/devdesk/.local/share/fonts/5x5_pixel.ttf"
|
|
||||||
|
|
||||||
convert -background white -fill black -font "$font_file" -pointsize "$point_size" \
|
|
||||||
-size "${image_width}x${image_height}" -gravity Center label:"$text_string" "$filename"
|
|
||||||
|
|
||||||
echo "PNG file created: $filename"
|
|
||||||
|
|
||||||
if [[ "$2" == "debug" ]]; then
|
|
||||||
echo "Debug mode detected, skipping print."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
sudo brother_ql -b pyusb --model QL-570 -p usb://0x04f9:0x2028/000M6Z401370 --debug print -l 62 $filename
|
|
113
thermatext.py
113
thermatext.py
|
@ -1,113 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
MAX_FONT_SIZE = 15 # Maximum font size
|
|
||||||
WORD_SPACING = 10 # Word spacing in pixels
|
|
||||||
SIDE_SPACING = 3 # Space from image sides in pixels
|
|
||||||
TOP_BOTTOM_SPACING = 10 # Space from top and bottom of image in pixels
|
|
||||||
|
|
||||||
def calculate_font_size(text, image_size, font_path):
|
|
||||||
# Create a dummy image to get the text size
|
|
||||||
dummy_img = Image.new('RGB', (1, 1))
|
|
||||||
draw = ImageDraw.Draw(dummy_img)
|
|
||||||
# Start from a large font size
|
|
||||||
font_size = 200
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
# Decrease the font size until the text fits the image width
|
|
||||||
while draw.textbbox((0, 0), text, font)[2] > image_size[0] - 2 * SIDE_SPACING:
|
|
||||||
font_size -= 1
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
return font_size
|
|
||||||
|
|
||||||
def split_text(text, max_font_size, image_size, font_path):
|
|
||||||
# Wrap the text based on word spacing and /n
|
|
||||||
wrapper = WordSpacingTextWrapper(width=image_size[0], word_spacing=WORD_SPACING)
|
|
||||||
wrapped_text = []
|
|
||||||
for line in text.split('\n'):
|
|
||||||
lines = wrapper.wrap(line)
|
|
||||||
wrapped_text.extend(lines)
|
|
||||||
return wrapped_text
|
|
||||||
|
|
||||||
class WordSpacingTextWrapper(textwrap.TextWrapper):
|
|
||||||
def __init__(self, word_spacing=0, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.word_spacing = word_spacing
|
|
||||||
|
|
||||||
def _wrap_chunks(self, chunks):
|
|
||||||
# Add word spacing between chunks
|
|
||||||
chunks = super()._wrap_chunks(chunks)
|
|
||||||
lines = []
|
|
||||||
for i, line in enumerate(chunks):
|
|
||||||
if i < len(chunks) - 1:
|
|
||||||
line += ' ' * self.word_spacing
|
|
||||||
lines.append(line)
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def main(text, debug=False):
|
|
||||||
image_width = 200
|
|
||||||
image_height = image_width // 4
|
|
||||||
image_size = (image_width, image_height)
|
|
||||||
font_path = "/home/devdesk/.local/share/fonts/5x5_pixel.ttf"
|
|
||||||
|
|
||||||
text_for_filename = text.replace(' ', '_').replace('/', '_').replace('\n', '')
|
|
||||||
filename = f"labels/{text_for_filename}.png"
|
|
||||||
|
|
||||||
|
|
||||||
font_size = calculate_font_size(text, image_size, font_path)
|
|
||||||
|
|
||||||
# Split the text into multiple lines if the font size is less than 20
|
|
||||||
if font_size < 20:
|
|
||||||
lines = split_text(text, 20, image_size, font_path)
|
|
||||||
# Recalculate the font size and image height with the split text
|
|
||||||
font_size = calculate_font_size('\n'.join(lines), image_size, font_path)
|
|
||||||
num_lines = len(lines)
|
|
||||||
line_height = font_size
|
|
||||||
line_spacing = line_height // 2 # Adjust line spacing as needed
|
|
||||||
image_height = line_height * num_lines + 2 * TOP_BOTTOM_SPACING + (num_lines - 1) * line_spacing
|
|
||||||
image_size = (image_width, image_height)
|
|
||||||
|
|
||||||
else:
|
|
||||||
lines = [text]
|
|
||||||
num_lines = 1
|
|
||||||
line_height = font_size
|
|
||||||
line_spacing = line_height // 2 # Adjust line spacing as needed
|
|
||||||
image_height = line_height + 2 * TOP_BOTTOM_SPACING
|
|
||||||
|
|
||||||
# Create the image
|
|
||||||
img = Image.new('RGB', image_size, color='white')
|
|
||||||
d = ImageDraw.Draw(img)
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
|
|
||||||
# Draw the text for each line
|
|
||||||
y = TOP_BOTTOM_SPACING
|
|
||||||
for line in lines:
|
|
||||||
words = line.split(' ')
|
|
||||||
word_widths = [d.textbbox((0, 0), word, font)[2] - d.textbbox((0, 0), word, font)[0] for word in words]
|
|
||||||
total_width = sum(word_widths) + (WORD_SPACING * (len(words) - 1))
|
|
||||||
x = (image_width - total_width) // 2 + SIDE_SPACING
|
|
||||||
for i, word in enumerate(words):
|
|
||||||
d.text((x, y), word, fill='black', font=font)
|
|
||||||
x += word_widths[i] + WORD_SPACING
|
|
||||||
y += line_height + line_spacing
|
|
||||||
|
|
||||||
img.save(filename)
|
|
||||||
|
|
||||||
print(f"PNG file created: {filename}")
|
|
||||||
|
|
||||||
if not debug:
|
|
||||||
os.system(
|
|
||||||
f'sudo brother_ql -b pyusb --model QL-570 -p usb://0x04f9:0x2028/000M6Z401370 --debug print -l 62 {filename}'
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage: ./text_to_image.py <text_string> [debug]")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
text = sys.argv[1].replace('\\n', '\n') # Add this line
|
|
||||||
debug = sys.argv[2] == 'debug' if len(sys.argv) > 2 else False
|
|
||||||
|
|
||||||
main(text, debug)
|
|
Loading…
Reference in New Issue
Block a user