From 2a0017371e96d5083b630381b462ded277d89755 Mon Sep 17 00:00:00 2001 From: Voussoir Date: Sun, 17 May 2015 23:48:27 -0700 Subject: [PATCH] else textpixel --- TextPixel/README.md | 24 ++++++ TextPixel/bears.png | Bin 0 -> 267 bytes TextPixel/bears.txt | 5 ++ TextPixel/example.png | Bin 0 -> 95 bytes TextPixel/textpixel.py | 169 +++++++++++++++++++++++++++++++++++++++++ TotalDL/totaldl.py | 9 ++- 6 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 TextPixel/README.md create mode 100644 TextPixel/bears.png create mode 100644 TextPixel/bears.txt create mode 100644 TextPixel/example.png create mode 100644 TextPixel/textpixel.py diff --git a/TextPixel/README.md b/TextPixel/README.md new file mode 100644 index 0000000..70202bd --- /dev/null +++ b/TextPixel/README.md @@ -0,0 +1,24 @@ +TextPixel +============== + +Encode / Decode between strings and PNG images. Can be imported or used on the commandline. Since it uses one channel to store each character, this program is only compatible with characters between 0 and 255 in unicode. + +One channel is used to store the character, and the other two are randomized, so the output looks different every time. + +python example: + + encoded_string = textpixel.encode_string('Wow, look!') + textpixel.write_pixels(encoded_string, 'wowlook.png') + + +commandline example: + + > textpixel encode bears.txt bears + Done. + + > textpixel decode bears + Once upon a time there + was a book. + + It was about bears. + So many bears. \ No newline at end of file diff --git a/TextPixel/bears.png b/TextPixel/bears.png new file mode 100644 index 0000000000000000000000000000000000000000..3ce4e96bd830c3d4dd1d5465e60940df052db5d1 GIT binary patch literal 267 zcmV+m0rdWfP)G_PFb=u^L2QGlVY1;IIiSeQk-6Xn1e3`%%obdLE3sOv28$RT zgUw>~-NF0hI1fg*eJ`g2P1sv0WG0u)JFVwYoRm<-aJ86<^La;xubOaJVj8~A!l2O| zv#*h>Ft3hGM9RIYd#&vhQM1-+gNqBZ-je)}^i3^VZ;Gv}Pf;=1meN-V2eq{CHq1=( zW{_f5M-!f^kg^RFTmKH@gl?)S~fYJQkTe>fPl}mT$teko?6x RSET>|002ovPDHLkV1nw_bd~@B literal 0 HcmV?d00001 diff --git a/TextPixel/bears.txt b/TextPixel/bears.txt new file mode 100644 index 0000000..f56af96 --- /dev/null +++ b/TextPixel/bears.txt @@ -0,0 +1,5 @@ +Once upon a time there +was a book. + +It was about bears. +So many bears. \ No newline at end of file diff --git a/TextPixel/example.png b/TextPixel/example.png new file mode 100644 index 0000000000000000000000000000000000000000..11ca5c9d4e7b3632477dbb7f7d2a648c32c76578 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)O!3HFSxVM}GQfi(qjv*Cuk`p}s{{KJU_2~cZ u&Nu%hi?;tQ&(r)d|L}+Z|LfbX{AYNjERe_Zu;nLE2ZN`ppUXO@geCwr;3Dt< literal 0 HcmV?d00001 diff --git a/TextPixel/textpixel.py b/TextPixel/textpixel.py new file mode 100644 index 0000000..0ea18b4 --- /dev/null +++ b/TextPixel/textpixel.py @@ -0,0 +1,169 @@ +import sys +from PIL import Image as PILImage +import random +import string + +ARGS_ENCODE = ['encode', 'save', 'write'] +ARGS_DECODE = ['decode', 'read'] +# Alternate operation names for commandline use. + +def png_filename(filename): + if filename[-4:].lower() != '.png': + filename += '.png' + return filename + +def encode_character(text, densitymin=0, densitymax=300): + ''' + Given a character, take its ord() value and return an + RGB pixel representation. + + The RGB channels will become the ord value, a number + lower than the ord value, and a number higher than the + ord value, though the order will be randomized. + + `densitymin` and `densitymax` control how much lower and higher + the additional values will be relative to the ord value, in %. + + Returns a tuple (R,G,B) + ''' + + densitymin = abs(densitymin) + densitymax = abs(densitymax) + + asciivalue = ord(text) + percentmin = int((asciivalue * densitymin) / 100) + percentmax = int((asciivalue * densitymax) / 100) + + lowerfill = random.randint(asciivalue - percentmax, asciivalue - percentmin) + upperfill = random.randint(asciivalue + percentmin, asciivalue + percentmax) + + lowerfill = max(0, lowerfill) + upperfill = min(255, upperfill) + + rgb = [lowerfill, asciivalue, upperfill] + random.shuffle(rgb) + return tuple(rgb) + +def encode_string(text, densitymin=0, densitymax=300): + ''' + Given a string `text`, map each character to an RGB + pixel, and return these pixels so they can be written to a file. + + `densitymin` and `densitymax` are passed to encode_character + + Returns a list where + [0] is (width,height), + [1] is a dict of pixels: {(x,y) : (R,G,B)} + ''' + text = text.strip() + + lines = text.split('\n') + encoded_height = len(lines) + encoded_width = max([len(line) for line in lines]) + encoded_pixels = {} + + for y in range(encoded_height): + line = lines[y] + for x in range(len(line)): + character = line[x] + pixel = encode_character(character, densitymin, densitymax) + encoded_pixels[(x,y)] = pixel + + dim = (encoded_width, encoded_height) + out = [dim, encoded_pixels] + return out + +def write_pixels(dimout, filename): + ''' + Given pixel data of the form returned by `encode_string()`, + write it to a PNG file named `filename`. + ''' + + filename = png_filename(filename) + + dim = dimout[0] + encoded_pixels = dimout[1] + + image = PILImage.new('RGBA', dim) + + for pixel in encoded_pixels: + image.putpixel(pixel, encoded_pixels[pixel]) + + image.save(filename) + +############################################################################## + +def decode_pixel(pixel): + ''' + Given a tuple (R,G,B), return the decoded text character. + + The character is the str() for the middle value of the tuple. + ("middle" referring to numeric value, not index) + ''' + + pixel = sorted(list(pixel)) + character = chr(pixel[1]) + return character + +def decode_image(image): + ''' + Given an image, return the decoded string + + `image` may be a string representing the filename + or a PIL Image object + ''' + + if isinstance(image, str): + if image[-4:].lower() != '.png': + image += '.png' + image = png_filename(image) + image = PILImage.open(image) + + width = image.size[0] + height = image.size[1] + + decoded_string = '' + + for y in range(height): + for x in range(width): + pixel = image.getpixel((x,y)) + if pixel[3] == 0: + break + decoded_string += decode_pixel(pixel) + decoded_string += '\n' + decoded_string = decoded_string.strip() + return decoded_string + +def argsfail(): + print('\ninvalid parameters.') + print('> textpixel.py encode text filename') + print('> textpixel.py decode filename') + quit() + +if __name__ == '__main__': + if len(sys.argv) < 3: + argsfail() + + op = sys.argv[1].lower() + + if op in ARGS_ENCODE and len(sys.argv) == 4: + text = sys.argv[2] + if text[-4:].lower() == '.txt': + try: + tfile = open(text, 'r') + text = tfile.read() + tfile.close() + except FileNotFoundError: + pass + + filename = sys.argv[3] + pixels = encode_string(text) + write_pixels(pixels, filename) + print('Done.') + + elif op in ARGS_DECODE and len(sys.argv) == 3: + filename = sys.argv[2] + print(decode_image(filename)) + + else: + argsfail() diff --git a/TotalDL/totaldl.py b/TotalDL/totaldl.py index 516cb1e..23181de 100644 --- a/TotalDL/totaldl.py +++ b/TotalDL/totaldl.py @@ -252,13 +252,15 @@ def handle_twitter(url, customname=None): try: link = pagedata.split('data-url="')[1] link = link.split('"')[0] - handle_master(link, customname=customname) + if link != url: + handle_master(link, customname=customname) return except IndexError: try: link = pagedata.split('data-expanded-url="')[1] link = link.split('"')[0] - handle_master(link, customname=customname) + if link != url: + handle_master(link, customname=customname) return except IndexError: pass @@ -366,6 +368,9 @@ def test_twitter(): # Twitter plain text handle_master('https://twitter.com/cp_orange_x3/status/599700702382817280') + # Twitter plain text + handle_master('https://twitter.com/SyriacMFS/status/556513635913437184') + def test_generic(): # Some link that might work handle_master('https://raw.githubusercontent.com/voussoir/reddit/master/SubredditBirthdays/show/statistics.txt')