aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcommands.py197
-rw-r--r--pixelterm/__init__.py1
-rwxr-xr-xpixelterm/colorcube.py25
-rwxr-xr-xpixelterm/gifterm.py89
-rwxr-xr-xpixelterm/pixelterm.py44
-rwxr-xr-xpixelterm/pngmeta.py16
-rwxr-xr-xpixelterm/resolvecolor.py17
-rw-r--r--pixelterm/unpixelterm.py34
-rwxr-xr-xsetup.py14
9 files changed, 209 insertions, 228 deletions
diff --git a/commands.py b/commands.py
new file mode 100755
index 0000000..fc35c54
--- /dev/null
+++ b/commands.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+import os, sys, argparse, os.path, json, time, signal, atexit
+from collections import defaultdict
+from pixelterm.pixelterm import termify_pixels, reset_sequence
+from pixelterm.unpixelterm import unpixelterm
+from PIL import Image, PngImagePlugin, GifImagePlugin, ImageSequence
+import re
+
+
+def pixelterm():
+ import os, sys, argparse, os.path, json
+ from multiprocessing.dummy import Pool
+ from PIL import Image, PngImagePlugin
+
+ parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
+ parser.add_argument('image', type=str, nargs='*')
+ parser.add_argument('-d', '--output-dir', type=str, help='Output directory (if not given, output to stdout)')
+ args = parser.parse_args()
+
+ def convert(f):
+ img = Image.open(f).convert("RGBA")
+ if args.output_dir:
+ print(f)
+ foo, _, _ = f.rpartition('.png')
+ output = os.path.join(args.output_dir, os.path.basename(foo)+'.pony')
+ metadata = json.loads(img.info.get('pixelterm-metadata'))
+ comment = metadata.get('_comment')
+ if comment is not None:
+ del metadata['_comment']
+ comment = '\n'+comment
+ else:
+ comment = ''
+ metadataarea = '$$$\n' +\
+ '\n'.join([ '\n'.join([ k.upper() + ': ' + v for v in metadata[k] ]) for k in sorted(metadata.keys()) ]) +\
+ comment + '\n$$$\n'
+ with open(output, 'w') as of:
+ of.write(metadataarea)
+ of.write(termify_pixels(img))
+ else:
+ print(termify_pixels(img))
+
+ p = Pool()
+ p.map(convert, args.image)
+
+
+def unpixelterm():
+ import argparse, json
+
+ parser = argparse.ArgumentParser(description='Convert images rendered by pixelterm-like utilities back to PNG')
+ parser.add_argument('-v', '--verbose', action='store_true')
+ output_group = parser.add_mutually_exclusive_group()
+ output_group.add_argument('-o', '--output', type=str, help='Output file name, defaults to ${input%.pony}.png')
+ output_group.add_argument('-d', '--output-dir', type=str, help='Place output files here')
+ parser.add_argument('input', type=argparse.FileType('r'), nargs='+')
+ args = parser.parse_args()
+ if len(args.input) > 1 and args.output:
+ parser.print_help()
+ print('You probably do not want to overwrite the given output file {} times.'.format(len(args.input)))
+ sys.exit(1)
+
+ for f in args.input:
+ if len(args.input) > 1:
+ print(f.name)
+ img, metadata = unpixelterm(f.read())
+ pnginfo = PngImagePlugin.PngInfo()
+ pnginfo.add_text('pixelterm-metadata', json.dumps(metadata))
+ foo, _, _ = f.name.rpartition('.pony')
+ output = args.output or foo+'.png'
+ if args.output_dir:
+ output = os.path.join(args.output_dir, os.path.basename(output))
+ img.save(output, 'PNG', pnginfo=pnginfo)
+
+
+clear_screen = '\033[H\033[2J'
+home_cursor = '\033[H'
+cursor_invisible = '\033[?25l'
+cursor_visible = '\033[?25h'
+
+def gifterm():
+ parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
+ parser.add_argument('image', type=str)
+ parser.add_argument('-s', '--size', type=str, help='Terminal size, [W]x[H]')
+ parser.add_argument('--serve', type=int, help='Serve via TCP on given port')
+ args = parser.parse_args()
+
+ tw, th = None, None
+ if args.size:
+ tw, th = map(int, args.size.split('x'))
+ else:
+ try:
+ tw, th = os.get_terminal_size()
+ except: # If this is not a regular terminal
+ pass
+ th = th*2
+
+ img = Image.open(args.image)
+ palette = img.getpalette()
+ last_frame = Image.new("RGBA", img.size)
+ frames = []
+
+ for frame in ImageSequence.Iterator(img):
+ #This works around a known bug in Pillow
+ #See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
+ frame.putpalette(palette)
+ c = frame.convert("RGBA")
+
+ if img.info['background'] != img.info.get('transparency'):
+ last_frame.paste(c, c)
+ else:
+ last_frame = c
+
+ im = last_frame.copy()
+ if (tw, th) != (None, None):
+ im.thumbnail((tw, th), Image.NEAREST)
+ frames.append(termify_pixels(im, True))
+
+ if args.serve:
+ from socketserver import ThreadingMixIn, TCPServer, BaseRequestHandler
+
+ # Quote-Of-The-Day protocol implementation
+ # See RFC865 ( https://tools.ietf.org/html/rfc865 ) for details.
+
+ class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
+
+ class QOTDHandler(BaseRequestHandler):
+ def handle(self):
+ try:
+ self.request.sendall(bytes(cursor_invisible, "UTF-8"))
+ while True:
+ for frame in frames:
+ self.request.sendall(bytes(home_cursor + reset_sequence, "UTF-8"))
+ self.request.sendall(bytes(frame, "UTF-8"))
+ time.sleep(min(1/10, img.info['duration']/1000.0))
+ except:
+ pass
+
+ server = ThreadingTCPServer(('', args.serve), QOTDHandler)
+ server.serve_forever()
+ else:
+ print(cursor_invisible)
+ atexit.register(lambda:print(cursor_visible))
+ signal.signal(signal.SIGTERM, lambda signum, stack_frame: exit(1))
+
+ try:
+ while True:
+ for frame in frames:
+ print(home_cursor)
+ print(reset_sequence)
+ print(frame)
+ time.sleep(min(1/10, img.info['duration']/1000.0))
+ except KeyboardInterrupt:
+ pass
+
+
+# Display an xterm-256color color palette on the terminal, including color ids
+
+reset_sequence = '\033[39;49m'
+
+def _esc(i):
+ return '\033[48;5;'+str(i)+'m'
+
+def colorcube():
+ print(''.join([str(i).ljust(4) for i in range(16)]))
+ print(' '.join([_esc(i) for i in range(16)])+' ' + reset_sequence)
+
+ for j in range(6):
+ for k in range(6):
+ c = 16+j*6+k*6*6
+ print(''.join([str(c+i).ljust(4) for i in range(6)]))
+ print(' '.join([_esc(c+i) for i in range(6)])+' ' + reset_sequence)
+
+ print(''.join([str(i).ljust(4) for i in range(16+6*6*6, 16+6*6*6+24)]))
+ print(' '.join([_esc(i) for i in range(16+6*6*6, 16+6*6*6+24)])+' ' + reset_sequence)
+
+
+def pngmeta():
+ parser = argparse.ArgumentParser(description='Print PNG metadata')
+ parser.add_argument('image', type=str)
+ args = parser.parse_args()
+ img = Image.open(args.image)
+ for k, v in img.info.items():
+ print('{:15}: {}'.format(k, v))
+
+
+def resolvecolor():
+ import os, sys, argparse, os.path, json, re
+ from pixelterm.xtermcolors import closest_color
+
+ # Resolve HTML-style hex RGB color codes to xterm-256color color numbers
+
+ if len(sys.argv) != 2:
+ print('Usage: resolvecolor.py #RRGGBB')
+ exit()
+
+ print(closest_color(*[int(s, 16) for s in re.match('#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})', sys.argv[1]).groups()]))
+
diff --git a/pixelterm/__init__.py b/pixelterm/__init__.py
index 8692824..e69de29 100644
--- a/pixelterm/__init__.py
+++ b/pixelterm/__init__.py
@@ -1 +0,0 @@
-__all__ = ['xtermcolors', 'pixelterm']
diff --git a/pixelterm/colorcube.py b/pixelterm/colorcube.py
deleted file mode 100755
index b7fe65c..0000000
--- a/pixelterm/colorcube.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# Display an xterm-256color color palette on the terminal, including color ids
-
-reset_sequence = '\033[39;49m'
-
-def esc(i):
- return '\033[48;5;'+str(i)+'m'
-
-def main():
- print(''.join([str(i).ljust(4) for i in range(16)]))
- print(' '.join([esc(i) for i in range(16)])+' ' + reset_sequence)
-
- for j in range(6):
- for k in range(6):
- c = 16+j*6+k*6*6
- print(''.join([str(c+i).ljust(4) for i in range(6)]))
- print(' '.join([esc(c+i) for i in range(6)])+' ' + reset_sequence)
-
- print(''.join([str(i).ljust(4) for i in range(16+6*6*6, 16+6*6*6+24)]))
- print(' '.join([esc(i) for i in range(16+6*6*6, 16+6*6*6+24)])+' ' + reset_sequence)
-
-if __name__ == '__main__':
- main()
-
diff --git a/pixelterm/gifterm.py b/pixelterm/gifterm.py
deleted file mode 100755
index 23380a0..0000000
--- a/pixelterm/gifterm.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-
-import os, sys, argparse, os.path, json, time, signal, atexit
-from pixelterm import pixelterm
-from PIL import Image, GifImagePlugin, ImageSequence
-
-clear_screen = '\033[H\033[2J'
-home_cursor = '\033[H'
-cursor_invisible = '\033[?25l'
-cursor_visible = '\033[?25h'
-
-def main():
- parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
- parser.add_argument('image', type=str)
- parser.add_argument('-s', '--size', type=str, help='Terminal size, [W]x[H]')
- parser.add_argument('--serve', type=int, help='Serve via TCP on given port')
- args = parser.parse_args()
-
- tw, th = None, None
- if args.size:
- tw, th = map(int, args.size.split('x'))
- else:
- try:
- tw, th = os.get_terminal_size()
- except: # If this is not a regular terminal
- pass
- th = th*2
-
- img = Image.open(args.image)
- palette = img.getpalette()
- last_frame = Image.new("RGBA", img.size)
- frames = []
-
- for frame in ImageSequence.Iterator(img):
- #This works around a known bug in Pillow
- #See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
- frame.putpalette(palette)
- c = frame.convert("RGBA")
-
- if img.info['background'] != img.info.get('transparency'):
- last_frame.paste(c, c)
- else:
- last_frame = c
-
- im = last_frame.copy()
- if (tw, th) != (None, None):
- im.thumbnail((tw, th), Image.NEAREST)
- frames.append(pixelterm.termify_pixels(im, True))
-
- if args.serve:
- from socketserver import ThreadingMixIn, TCPServer, BaseRequestHandler
-
- # Quote-Of-The-Day protocol implementation
- # See RFC865 ( https://tools.ietf.org/html/rfc865 ) for details.
-
- class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
-
- class QOTDHandler(BaseRequestHandler):
- def handle(self):
- try:
- self.request.sendall(bytes(cursor_invisible, "UTF-8"))
- while True:
- for frame in frames:
- self.request.sendall(bytes(home_cursor + pixelterm.reset_sequence, "UTF-8"))
- self.request.sendall(bytes(frame, "UTF-8"))
- time.sleep(min(1/10, img.info['duration']/1000.0))
- except:
- pass
-
- server = ThreadingTCPServer(('', args.serve), QOTDHandler)
- server.serve_forever()
- else:
- print(cursor_invisible)
- atexit.register(lambda:print(cursor_visible))
- signal.signal(signal.SIGTERM, lambda signum, stack_frame: exit(1))
-
- try:
- while True:
- for frame in frames:
- print(home_cursor)
- print(pixelterm.reset_sequence)
- print(frame)
- time.sleep(min(1/10, img.info['duration']/1000.0))
- except KeyboardInterrupt:
- pass
-
-if __name__ == '__main__':
- main()
-
diff --git a/pixelterm/pixelterm.py b/pixelterm/pixelterm.py
index 334e500..8a9d0be 100755
--- a/pixelterm/pixelterm.py
+++ b/pixelterm/pixelterm.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-from pixelterm import xtermcolors
+from pixelterm.xtermcolors import closest_color
reset_sequence = '\033[39;49m'
@@ -20,7 +20,7 @@ def termify_pixels(img, fill=False):
if color in bgd:
return bgd[color]
r,g,b,_ = color
- bgd[color] = '\033[48;5;'+str(xtermcolors.closest_color(r,g,b))+'m'
+ bgd[color] = '\033[48;5;'+str(closest_color(r,g,b))+'m'
return bgd[color]
def fgescape(color):
@@ -29,7 +29,7 @@ def termify_pixels(img, fill=False):
return ''
fg=color
r,g,b,_ = color
- fgd[color] = '\033[38;5;'+str(xtermcolors.closest_color(r,g,b))+'m'
+ fgd[color] = '\033[38;5;'+str(closest_color(r,g,b))+'m'
return fgd[color]
def balloon(x,y):
@@ -69,41 +69,3 @@ def termify_pixels(img, fill=False):
out = (out.rstrip() if bg == (0,0,0,0) and not fill else out) + '\n'
return out[:-1] + reset_sequence + '\n'
-def main():
- import os, sys, argparse, os.path, json
- from multiprocessing.dummy import Pool
- from PIL import Image, PngImagePlugin
-
- parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
- parser.add_argument('image', type=str, nargs='*')
- parser.add_argument('-d', '--output-dir', type=str, help='Output directory (if not given, output to stdout)')
- args = parser.parse_args()
-
- def convert(f):
- img = Image.open(f).convert("RGBA")
- if args.output_dir:
- print(f)
- foo, _, _ = f.rpartition('.png')
- output = os.path.join(args.output_dir, os.path.basename(foo)+'.pony')
- metadata = json.loads(img.info.get('pixelterm-metadata'))
- comment = metadata.get('_comment')
- if comment is not None:
- del metadata['_comment']
- comment = '\n'+comment
- else:
- comment = ''
- metadataarea = '$$$\n' +\
- '\n'.join([ '\n'.join([ k.upper() + ': ' + v for v in metadata[k] ]) for k in sorted(metadata.keys()) ]) +\
- comment + '\n$$$\n'
- with open(output, 'w') as of:
- of.write(metadataarea)
- of.write(termify_pixels(img))
- else:
- print(termify_pixels(img))
-
- p = Pool()
- p.map(convert, args.image)
-
-if __name__ == '__main__':
- main()
-
diff --git a/pixelterm/pngmeta.py b/pixelterm/pngmeta.py
deleted file mode 100755
index 5ddde98..0000000
--- a/pixelterm/pngmeta.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-import os, sys, argparse
-from PIL import Image, PngImagePlugin
-
-def main():
- parser = argparse.ArgumentParser(description='Print PNG metadata')
- parser.add_argument('image', type=str)
- args = parser.parse_args()
- img = Image.open(args.image)
- for k, v in img.info.items():
- print('{:15}: {}'.format(k, v))
-
-if __name__ == '__main__':
- main()
-
diff --git a/pixelterm/resolvecolor.py b/pixelterm/resolvecolor.py
deleted file mode 100755
index 7fab111..0000000
--- a/pixelterm/resolvecolor.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env python
-
-def main():
- import os, sys, argparse, os.path, json, re
- from pixelterm import xtermcolors
-
- # Resolve HTML-style hex RGB color codes to xterm-256color color numbers
-
- if len(sys.argv) != 2:
- print('Usage: resolvecolor.py #RRGGBB')
- exit()
-
- print(xtermcolors.closest_color(*[int(s, 16) for s in re.match('#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})', sys.argv[1]).groups()]))
-
-if __name__ == '__main__':
- main()
-
diff --git a/pixelterm/unpixelterm.py b/pixelterm/unpixelterm.py
index 46c0c83..1159197 100644
--- a/pixelterm/unpixelterm.py
+++ b/pixelterm/unpixelterm.py
@@ -2,7 +2,7 @@
import os, sys, os.path
from collections import defaultdict
-from pixelterm import xtermcolors
+from pixelterm.xtermcolors import xterm_colors
from PIL import Image, PngImagePlugin
try:
import re2 as re
@@ -16,7 +16,7 @@ def parse_escape_sequence(seq):
while i<len(codes):
if codes[i] in [38, 48]:
if codes[i+1] == 5:
- c = xtermcolors.xterm_colors[codes[i+2]]
+ c = xterm_colors[codes[i+2]]
fg, bg = (c, bg) if codes[i] == 38 else (fg, c)
i += 2
elif codes[i] == 39:
@@ -90,33 +90,3 @@ def unpixelterm(text):
x, y = 0, y+2
return img, metadata
-def main():
- import argparse, json
-
- parser = argparse.ArgumentParser(description='Convert images rendered by pixelterm-like utilities back to PNG')
- parser.add_argument('-v', '--verbose', action='store_true')
- output_group = parser.add_mutually_exclusive_group()
- output_group.add_argument('-o', '--output', type=str, help='Output file name, defaults to ${input%.pony}.png')
- output_group.add_argument('-d', '--output-dir', type=str, help='Place output files here')
- parser.add_argument('input', type=argparse.FileType('r'), nargs='+')
- args = parser.parse_args()
- if len(args.input) > 1 and args.output:
- parser.print_help()
- print('You probably do not want to overwrite the given output file {} times.'.format(len(args.input)))
- sys.exit(1)
-
- for f in args.input:
- if len(args.input) > 1:
- print(f.name)
- img, metadata = unpixelterm(f.read())
- pnginfo = PngImagePlugin.PngInfo()
- pnginfo.add_text('pixelterm-metadata', json.dumps(metadata))
- foo, _, _ = f.name.rpartition('.pony')
- output = args.output or foo+'.png'
- if args.output_dir:
- output = os.path.join(args.output_dir, os.path.basename(output))
- img.save(output, 'PNG', pnginfo=pnginfo)
-
-if __name__ == '__main__':
- main()
-
diff --git a/setup.py b/setup.py
index 2a7a1ac..1d42649 100755
--- a/setup.py
+++ b/setup.py
@@ -24,14 +24,14 @@ setup(name = 'pixelterm',
url = 'https://github.com/jaseg/pixelterm',
packages = ['pixelterm'],
install_requires=['pillow'],
+ py_modules = [ 'commands' ],
entry_points = {'console_scripts': [
- 'pixelterm=pixelterm.pixelterm:main',
- 'unpixelterm=pixelterm.unpixelterm:main',
- 'gifterm=pixelterm.gifterm:main',
- 'colorcube=pixelterm.colorcube:main',
- 'resolvecolor=pixelterm.resolvecolor:main',
- 'pngmeta=pixelterm.pngmeta:main']},
- zip_safe = True,
+ 'pixelterm=commands:pixelterm',
+ 'unpixelterm=commands:unpixelterm',
+ 'gifterm=commands:gifterm',
+ 'colorcube=commands:colorcube',
+ 'resolvecolor=commands:resolvecolor',
+ 'pngmeta=commands:pngmeta']},
classifiers = [
'Development Status :: 5 - Production/Stable',
'Environment :: Console',