manetta
4 years ago
13 changed files with 534 additions and 99 deletions
@ -0,0 +1,2 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
|
@ -0,0 +1,6 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
|
||||
|
import utils |
||||
|
import patterns |
||||
|
import marks |
||||
|
import text |
@ -0,0 +1,49 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from utils import make_lines, merge, print_lines, rotate, visit, visit_horizontal |
||||
|
from patterns import diagonal, horizontal, vertical, sinus_horizontal, sinus_vertical, image |
||||
|
from marks import random_mark, sequence_mark, space |
||||
|
from random import choice, random |
||||
|
import math |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
# marks = ['┼', '│', '░', '▓', 'X', '■', '≡', '·', '¦', ' '] |
||||
|
# blank = ' ' |
||||
|
width = 75 |
||||
|
height = 50 |
||||
|
mark = sequence_mark('O.P.E.N D.E.S.I.G.N C.O.U.R.S.E ') |
||||
|
|
||||
|
|
||||
|
layers = [] |
||||
|
|
||||
|
# layers.append(visit(make_lines(width, height), image('blobs-small.png'), mark, space(' '))) |
||||
|
|
||||
|
for offset in range(-50, 50, 15): |
||||
|
lines = [[] for l in range(height)] |
||||
|
sinus = sinus_vertical(period=50, amplitude=25, offset=offset, offset_t=random()) |
||||
|
layers.append(visit(make_lines(width, height), sinus, sequence_mark(' K A S K G E N T '), space())) |
||||
|
|
||||
|
for offset in range(-43, 57, 15): |
||||
|
lines = [[] for l in range(height)] |
||||
|
sinus = sinus_vertical(period=40, amplitude=10, offset=offset, offset_t=.5+random()) |
||||
|
layers.append(visit(make_lines(width, height), sinus, mark, space())) |
||||
|
|
||||
|
print_lines(merge(width, height, space()(), layers)) |
||||
|
|
||||
|
# for line in overlay(50, 50, ' ', [rotate(merged), merged]): |
||||
|
# stdout.write('{}\n'.format(''.join(line))) |
||||
|
|
||||
|
# sinus = sinus_horizontal(period=30, amplitude=8) |
||||
|
# for x in range(width): |
||||
|
# for y in range(height): |
||||
|
# lines[y].append(sinus(x, y, width, height, mark, space())) |
||||
|
|
||||
|
# for line in lines: |
||||
|
# stdout.write('{}\n'.format(''.join(line))) |
||||
|
|
||||
|
# lines = [[draw(x, y, marks) for x in range(width)] for y in range(height)] |
||||
|
|
||||
|
# sys.sdout.write('\n'.join([''.join(line) for line in lines])) |
||||
|
|
||||
|
# sys.sdout.write('\n'.join([''.join([draw(x, y, marks) for x in range(width)]) for y in range(height)])) |
@ -0,0 +1,24 @@ |
|||||
|
from random import choice |
||||
|
|
||||
|
def random (marks=['']): |
||||
|
def func (): |
||||
|
return choice(marks) |
||||
|
|
||||
|
return func |
||||
|
|
||||
|
def sentence (text): |
||||
|
chars = list(text) |
||||
|
def f(): |
||||
|
char = chars.pop(0) |
||||
|
chars.append(char) |
||||
|
return char |
||||
|
return f |
||||
|
|
||||
|
def single (char): |
||||
|
def f(): |
||||
|
return char |
||||
|
|
||||
|
return f |
||||
|
|
||||
|
def space (space=' '): |
||||
|
return single(space) |
@ -0,0 +1,67 @@ |
|||||
|
import math |
||||
|
|
||||
|
# # Linear |
||||
|
def diagonal(): |
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
if x == math.floor((y / float(height)) * width): |
||||
|
return mark() |
||||
|
else: |
||||
|
return blank() |
||||
|
|
||||
|
return f |
||||
|
|
||||
|
# Cross |
||||
|
def cross (): |
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
pos = math.floor((y / float(height)) * width) |
||||
|
|
||||
|
if x == pos or (width - 1) - pos == x: |
||||
|
return mark() |
||||
|
else: |
||||
|
return blank() |
||||
|
return f |
||||
|
|
||||
|
def horizontal (position): |
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
return mark() if position == y else blank() |
||||
|
return f |
||||
|
|
||||
|
def vertical (position): |
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
return mark() if position == x else blank() |
||||
|
return f |
||||
|
|
||||
|
# Sinus |
||||
|
def sinus_vertical (period=0.2, amplitude=0.5, offset_t=0, offset=0): |
||||
|
period = (period / (math.pi * 2)) |
||||
|
|
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
middle = (width - 1) * .5 |
||||
|
to_mark = math.floor(middle + math.sin(offset_t + y / period) * amplitude) |
||||
|
return mark() if (x + offset) == to_mark else blank() |
||||
|
return f |
||||
|
|
||||
|
def sinus_horizontal (period=0.2, amplitude=0.5, offset_t=0, offset=0): |
||||
|
period = (period / (math.pi * 2)) |
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
middle = (height - 1) * .5 |
||||
|
to_mark = math.floor(middle + math.sin(offset_t + x / period) * amplitude) |
||||
|
return mark() if y + offset == to_mark else blank() |
||||
|
|
||||
|
return f |
||||
|
|
||||
|
def image (path, threshold=128): |
||||
|
from PIL import Image |
||||
|
|
||||
|
im = Image.open(path).convert('L') |
||||
|
image_width, image_height = im.size |
||||
|
pixels = im.load() |
||||
|
|
||||
|
def f (x, y, width, height, mark, blank): |
||||
|
if x < image_width and y < image_height: |
||||
|
if pixels[x, y] < threshold: |
||||
|
return mark() |
||||
|
|
||||
|
return blank() |
||||
|
|
||||
|
return f |
@ -0,0 +1,55 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from .wrap_single_line import wrap_single_line |
||||
|
from .utils import translate, merge |
||||
|
|
||||
|
def make_column(text, line_width=50, height=200, use_hyphenator=None, line_offset=0): |
||||
|
|
||||
|
lines = [] |
||||
|
remaining = text |
||||
|
|
||||
|
while remaining and len(lines) < height: |
||||
|
|
||||
|
if callable(line_width): |
||||
|
width = line_width(len(lines), height) |
||||
|
else: |
||||
|
width = line_width |
||||
|
|
||||
|
if callable(line_offset): |
||||
|
offset = line_offset(len(lines), height) |
||||
|
else: |
||||
|
offset = line_offset |
||||
|
|
||||
|
line, remaining = wrap_single_line(remaining, width, use_hyphenator=use_hyphenator, replace_whitespace=False, drop_whitespace=True) |
||||
|
|
||||
|
line = list(line) |
||||
|
|
||||
|
if offset != 0: |
||||
|
line = [None for _ in range(offset)] + line |
||||
|
|
||||
|
lines.append(line) |
||||
|
|
||||
|
return lines, remaining |
||||
|
|
||||
|
def make_multi_column(text, height=200, column_width=40, column_count=2, column_gap=5, use_hyphenator=None, space_char=None): |
||||
|
# todo: vertical offset? |
||||
|
|
||||
|
remaining = text |
||||
|
i = 0 |
||||
|
|
||||
|
columns = [] |
||||
|
|
||||
|
while remaining and i < column_count: |
||||
|
column, remaining = make_column(remaining, line_width=column_width, height=height, use_hyphenator=use_hyphenator) |
||||
|
if i > 0: |
||||
|
offset = (column_width + column_gap) * i |
||||
|
column = translate(column, x=offset, y=0) |
||||
|
columns.append(column) |
||||
|
i += 1 |
||||
|
|
||||
|
width = (column_width + column_gap) * column_count |
||||
|
lines = merge(width, height, space_char, columns) |
||||
|
return lines, remaining |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
print(make_column('Hello world!', line_width=25, height=10)) |
@ -0,0 +1,61 @@ |
|||||
|
from sys import stdout |
||||
|
|
||||
|
def rotate(layer): |
||||
|
new_width = len(layer) |
||||
|
new_height = len(layer[0]) |
||||
|
rotated = [['' for x in range(new_width)] for l in range(new_height)] |
||||
|
|
||||
|
for y in range(len(layer)): |
||||
|
for x in range(len(layer[y])): |
||||
|
rotated[x][y] = layer[y][x] |
||||
|
|
||||
|
return rotated |
||||
|
|
||||
|
def merge(width, height, space_char, layers): |
||||
|
output = [[space_char for x in range(width)] for y in range(height)] |
||||
|
|
||||
|
for layer in layers: |
||||
|
for y in range(min(len(layer), height)): |
||||
|
for x in range(min(len(layer[y]), width)): |
||||
|
if layer[y][x] and layer[y][x] != space_char: |
||||
|
output[y][x] = layer[y][x] |
||||
|
|
||||
|
return output |
||||
|
|
||||
|
# Make a multidimensional array |
||||
|
# with the given dimensions |
||||
|
def make_lines (width, height, fill_char = ''): |
||||
|
return [[ fill_char for _ in range(width) ] for __ in range(height)] |
||||
|
|
||||
|
def visit (lines, callback, mark, blank): |
||||
|
height = len(lines) |
||||
|
width = len(lines[0]) |
||||
|
|
||||
|
for y in range(height): |
||||
|
for x in range(width): |
||||
|
lines[y][x] = callback(x, y, width, height, mark, blank) |
||||
|
|
||||
|
return lines |
||||
|
|
||||
|
def visit_horizontal (lines, callback, mark, blank): |
||||
|
height = len(lines) |
||||
|
width = len(lines[0]) |
||||
|
|
||||
|
for x in range(width): |
||||
|
for y in range(height): |
||||
|
lines[y][x] = callback(x, y, width, height, mark, blank) |
||||
|
|
||||
|
return lines |
||||
|
|
||||
|
def print_lines (lines): |
||||
|
for line in lines: |
||||
|
stdout.write('{}\n'.format(''.join(line))) |
||||
|
|
||||
|
def translate(shape, x=0, y=0): |
||||
|
## TODO implement a negative translation? |
||||
|
translated = [[] for _ in range(y)] |
||||
|
|
||||
|
for line in shape: |
||||
|
translated.append([None for _ in range(x)] + line) |
||||
|
|
||||
|
return translated |
@ -0,0 +1,137 @@ |
|||||
|
""" |
||||
|
Based on the textwrap2 module included in the PyHyphen library: |
||||
|
https://pypi.org/project/PyHyphen |
||||
|
""" |
||||
|
|
||||
|
import textwrap |
||||
|
|
||||
|
class TextWrapper(textwrap.TextWrapper): |
||||
|
""" |
||||
|
This class extends the Python 3 standard library's TextWrapper and adds an optional |
||||
|
use_hyphenator to its constructor arguments. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, *args, **kwargs): |
||||
|
self.use_hyphenator = kwargs.pop("use_hyphenator", None) |
||||
|
super().__init__(*args, **kwargs) |
||||
|
|
||||
|
def _wrap_chunks(self, chunks): |
||||
|
|
||||
|
lines = [] |
||||
|
if (chunks): |
||||
|
"""Override the mother class method. |
||||
|
|
||||
|
Most of that method is directly copied from the original class, except |
||||
|
for the part with use_hyphenator. |
||||
|
""" |
||||
|
if self.width <= 0: |
||||
|
raise ValueError("invalid width %r (must be > 0)" % self.width) |
||||
|
if self.max_lines is not None: |
||||
|
if self.max_lines > 1: |
||||
|
indent = self.subsequent_indent |
||||
|
else: |
||||
|
indent = self.initial_indent |
||||
|
if len(indent) + len(self.placeholder.lstrip()) > self.width: |
||||
|
raise ValueError("placeholder too large for max width") |
||||
|
|
||||
|
# Arrange in reverse order so items can be efficiently popped |
||||
|
# from a stack of chucks. |
||||
|
chunks.reverse() |
||||
|
|
||||
|
# Start the list of chunks that will make up the current line. |
||||
|
# cur_len is just the length of all the chunks in cur_line. |
||||
|
cur_line = [] |
||||
|
cur_len = 0 |
||||
|
|
||||
|
# Figure out which static string will prefix this line. |
||||
|
if lines: |
||||
|
indent = self.subsequent_indent |
||||
|
else: |
||||
|
indent = self.initial_indent |
||||
|
|
||||
|
# Maximum width for this line. |
||||
|
width = self.width - len(indent) |
||||
|
|
||||
|
# First chunk on line is whitespace -- drop it, unless this |
||||
|
# is the very beginning of the text (ie. no lines started yet). |
||||
|
if self.drop_whitespace and chunks[-1].strip() == '' and chunks[-1][0] != '\n': # and lines: |
||||
|
del chunks[-1] |
||||
|
|
||||
|
while chunks: |
||||
|
l = len(chunks[-1]) |
||||
|
|
||||
|
# Check for a newline character |
||||
|
if chunks[-1] == '\n': |
||||
|
return indent + ''.join(cur_line), ''.join(reversed(chunks[:-1])) |
||||
|
elif chunks[-1][0] == '\n': |
||||
|
return indent + ''.join(cur_line), ''.join(reversed(chunks[:-1] + [chunks[-1][1:]])) |
||||
|
|
||||
|
# Can at least squeeze this chunk onto the current line. |
||||
|
if cur_len + l <= width: |
||||
|
cur_line.append(chunks.pop()) |
||||
|
cur_len += l |
||||
|
|
||||
|
# Nope, this line is full. |
||||
|
# But try hyphenation. |
||||
|
else: |
||||
|
if self.use_hyphenator and (width - cur_len >= 2): |
||||
|
hyphenated_chunk = self.use_hyphenator.wrap(chunks[-1], width - cur_len) |
||||
|
if hyphenated_chunk: |
||||
|
cur_line.append(hyphenated_chunk[0]) |
||||
|
chunks[-1] = hyphenated_chunk[1] |
||||
|
|
||||
|
break |
||||
|
|
||||
|
# The current line is full, and the next chunk is too big to |
||||
|
# fit on *any* line (not just this one). |
||||
|
if chunks and len(chunks[-1]) > width: |
||||
|
self._handle_long_word(chunks, cur_line, cur_len, width) |
||||
|
cur_len = sum(map(len, cur_line)) |
||||
|
|
||||
|
# If the last chunk on this line is all whitespace, drop it. |
||||
|
if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': |
||||
|
cur_len -= len(cur_line[-1]) |
||||
|
del cur_line[-1] |
||||
|
|
||||
|
if cur_line: |
||||
|
if (self.max_lines is None or |
||||
|
len(lines) + 1 < self.max_lines or |
||||
|
(not chunks or |
||||
|
self.drop_whitespace and |
||||
|
len(chunks) == 1 and |
||||
|
not chunks[0].strip()) and cur_len <= width): |
||||
|
# Convert current line back to a string and store it in |
||||
|
# list of all lines (return value). |
||||
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
||||
|
else: |
||||
|
while cur_line: |
||||
|
if (cur_line[-1].strip() and |
||||
|
cur_len + len(self.placeholder) <= width): |
||||
|
cur_line.append(self.placeholder) |
||||
|
|
||||
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
||||
|
|
||||
|
cur_len -= len(cur_line[-1]) |
||||
|
del cur_line[-1] |
||||
|
else: |
||||
|
if lines: |
||||
|
prev_line = lines[-1].rstrip() |
||||
|
if (len(prev_line) + len(self.placeholder) <= |
||||
|
self.width): |
||||
|
lines[-1] = prev_line + self.placeholder |
||||
|
|
||||
|
return indent + self.placeholder.lstrip(), ''.join(reversed(chunks)) |
||||
|
else: |
||||
|
return '', '' |
||||
|
|
||||
|
def wrap_single_line (text, width=70, **kwargs): |
||||
|
w = TextWrapper(width=width, **kwargs) |
||||
|
return w.wrap(text) |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
from hyphen import Hyphenator |
||||
|
h_en = Hyphenator('en_US') |
||||
|
|
||||
|
line, remaining = wrap_single_line('https://stackoverflow.com/questions/3940128/how-can-i-reverse-a-list-in-python', width=46, use_hyphenator=h_en) |
||||
|
|
||||
|
print(line, remaining) |
@ -0,0 +1,95 @@ |
|||||
|
import sys, os, re, json |
||||
|
from datetime import datetime, date |
||||
|
import html2text |
||||
|
from escpos import printer |
||||
|
|
||||
|
from tools.asciiWriter.text import make_column |
||||
|
from tools.asciiWriter.utils import merge, print_lines, make_lines, translate |
||||
|
|
||||
|
def connect(): |
||||
|
# Get the printer's USB initializing code with $ sudo lsbusb |
||||
|
try: |
||||
|
lp = printer.Usb(0x4b8,0xe15) |
||||
|
lp_printer = True |
||||
|
except: |
||||
|
lp = None |
||||
|
lp_printer = False |
||||
|
|
||||
|
return lp, lp_printer |
||||
|
|
||||
|
def database(db): |
||||
|
if not os.path.exists(db): |
||||
|
with open(db, 'w') as f: |
||||
|
f.write(json.dumps({ "printed" : [] }, indent=4)) |
||||
|
|
||||
|
return db |
||||
|
|
||||
|
def html2plain(html): |
||||
|
# remove HTML tags |
||||
|
txt = re.sub(r'<.*?>', '', html) |
||||
|
|
||||
|
# make line breaks |
||||
|
# https://git.vvvvvvaria.org/mb/ascii-art-but-with-unicode |
||||
|
width = 48 |
||||
|
layers = [] |
||||
|
lines, remaining = make_column(txt, line_width=width) |
||||
|
layers.append(lines) |
||||
|
merged = merge(width, len(lines), ' ', layers) |
||||
|
txt = '' |
||||
|
for line in merged: |
||||
|
txt += '{}\n'.format(''.join(line)) |
||||
|
|
||||
|
return txt |
||||
|
|
||||
|
def update_db(txt, source, db, post=None): |
||||
|
try: |
||||
|
items = json.loads(open(db).read()) |
||||
|
except: |
||||
|
items = { "printed" : [] } # Hmm ... |
||||
|
|
||||
|
# save the publishing date of this post |
||||
|
if post: |
||||
|
year = post['published_parsed'][0] |
||||
|
month = post['published_parsed'][1] |
||||
|
day = post['published_parsed'][2] |
||||
|
post_date = date(year, month, day) |
||||
|
date_str = post_date.strftime('%Y-%m-%d') |
||||
|
post = None |
||||
|
else: |
||||
|
post_date = datetime.now() |
||||
|
date_str = post_date.strftime('%Y-%m-%d_%H-%M-%S') |
||||
|
|
||||
|
# clean txt from HTML tags |
||||
|
txt = html2plain(txt) |
||||
|
|
||||
|
# add txt to database |
||||
|
# and check if this item is new |
||||
|
item = { |
||||
|
'source' : source, |
||||
|
'date' : date_str, |
||||
|
'printed' : txt |
||||
|
} |
||||
|
if item not in items['printed']: |
||||
|
new_item = True |
||||
|
with open(db, 'w') as out: |
||||
|
items['printed'].append(item) |
||||
|
out.write(json.dumps(items, indent=4)) |
||||
|
out.close() |
||||
|
else: |
||||
|
new_item = False |
||||
|
|
||||
|
return new_item, txt |
||||
|
|
||||
|
def print_now(txt, source, db, post=None, lp=None, lp_printer=None): |
||||
|
# check if this is a new item |
||||
|
new_item, txt = update_db(txt, source, db, post=post) |
||||
|
|
||||
|
# if this item is new |
||||
|
# then print! |
||||
|
if new_item == True: |
||||
|
if lp_printer == True: |
||||
|
lp.text(txt) |
||||
|
lp.cut() |
||||
|
else: |
||||
|
# or print in the terminal! |
||||
|
sys.stderr.write('Printing output to the terminal:\n\n'+txt+'\n\n') |
Loading…
Reference in new issue