diff options
Diffstat (limited to 'mediagoblin/media_types')
-rw-r--r-- | mediagoblin/media_types/__init__.py | 20 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/__init__.py | 27 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/asciitoimage.py | 172 | ||||
l--------- | mediagoblin/media_types/ascii/fonts/Inconsolata.otf | 1 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/processing.py | 93 |
5 files changed, 305 insertions, 8 deletions
diff --git a/mediagoblin/media_types/__init__.py b/mediagoblin/media_types/__init__.py index 6f94c714..7b9bf0d7 100644 --- a/mediagoblin/media_types/__init__.py +++ b/mediagoblin/media_types/__init__.py @@ -69,16 +69,20 @@ def get_media_type_and_manager(filename): ''' Get the media type and manager based on a filename ''' - for media_type, manager in get_media_managers(): - if filename.find('.') > 0: - # Get the file extension - ext = os.path.splitext(filename)[1].lower() - else: - raise InvalidFileType( - _('Could not find any file extension in "{filename}"').format( - filename=filename)) + if filename.find('.') > 0: + # Get the file extension + ext = os.path.splitext(filename)[1].lower() + else: + raise Exception( + _(u'Could not extract any file extension from "{filename}"').format( + filename=filename)) + for media_type, manager in get_media_managers(): # Omit the dot from the extension and match it against # the media manager if ext[1:] in manager['accepted_extensions']: return media_type, manager + else: + raise FileTypeNotSupported( + # TODO: Provide information on which file types are supported + _(u'Sorry, I don\'t support that file type :(')) diff --git a/mediagoblin/media_types/ascii/__init__.py b/mediagoblin/media_types/ascii/__init__.py new file mode 100644 index 00000000..21b31d0e --- /dev/null +++ b/mediagoblin/media_types/ascii/__init__.py @@ -0,0 +1,27 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from mediagoblin.media_types.ascii.processing import process_ascii + + +MEDIA_MANAGER = { + "human_readable": "ASCII", + "processor": process_ascii, # alternately a string, + # 'mediagoblin.media_types.image.processing'? + "display_template": "mediagoblin/media_displays/ascii.html", + "default_thumb": "images/media_thumbs/ascii.jpg", + "accepted_extensions": [ + "txt"]} diff --git a/mediagoblin/media_types/ascii/asciitoimage.py b/mediagoblin/media_types/ascii/asciitoimage.py new file mode 100644 index 00000000..39c75a19 --- /dev/null +++ b/mediagoblin/media_types/ascii/asciitoimage.py @@ -0,0 +1,172 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import Image +import ImageFont +import ImageDraw +import logging +import pkg_resources +import os + +_log = logging.getLogger(__name__) + +class AsciiToImage(object): + ''' + Converter of ASCII art into image files, preserving whitespace + + kwargs: + - font: Path to font file + default: fonts/Inconsolata.otf + - font_size: Font size, ``int`` + default: 11 + ''' + + # Font file path + _font = None + + _font_size = 11 + + # ImageFont instance + _if = None + + # ImageFont + _if_dims = None + + # Image instance + _im = None + + def __init__(self, **kw): + if kw.get('font'): + self._font = kw.get('font') + else: + self._font = pkg_resources.resource_filename( + 'mediagoblin.media_types.ascii', + os.path.join('fonts', 'Inconsolata.otf')) + + if kw.get('font_size'): + self._font_size = kw.get('font_size') + + _log.info('Setting font to {0}, size {1}'.format( + self._font, + self._font_size)) + + self._if = ImageFont.truetype( + self._font, + self._font_size) + + # ,-,-^-'-^'^-^'^-'^-. + # ( I am a wall socket )Oo, ___ + # `-.,.-.,.-.-.,.-.--' ' ` + # Get the size, in pixels of the '.' character + self._if_dims = self._if.getsize('.') + # `---' + + def convert(self, text, destination): + # TODO: Detect if text is a file-like, if so, act accordingly + im = self._create_image(text) + + # PIL's Image.save will handle both file-likes and paths + if im.save(destination): + _log.info('Saved image in {0}'.format( + destination)) + + def _create_image(self, text): + ''' + Write characters to a PIL image canvas. + + TODO: + - Character set detection and decoding, + http://pypi.python.org/pypi/chardet + ''' + # TODO: Account for alternative line endings + lines = text.split('\n') + + line_lengths = [len(i) for i in lines] + + # Calculate destination size based on text input and character size + im_dims = ( + max(line_lengths) * self._if_dims[0], + len(line_lengths) * self._if_dims[1]) + + _log.info('Destination image dimensions will be {0}'.format( + im_dims)) + + im = Image.new( + 'RGBA', + im_dims, + (255, 255, 255, 0)) + + draw = ImageDraw.Draw(im) + + char_pos = [0, 0] + + for line in lines: + line_length = len(line) + + _log.debug('Writing line at {0}'.format(char_pos)) + + for _pos in range(0, line_length): + char = line[_pos] + + px_pos = self._px_pos(char_pos) + + _log.debug('Writing character "{0}" at {1} (px pos {2}'.format( + char, + char_pos, + px_pos)) + + draw.text( + px_pos, + char, + font=self._if, + fill=(0, 0, 0, 255)) + + char_pos[0] += 1 + + # Reset X position, increment Y position + char_pos[0] = 0 + char_pos[1] += 1 + + return im + + def _px_pos(self, char_pos): + ''' + Helper function to calculate the pixel position based on + character position and character dimensions + ''' + px_pos = [0, 0] + for index, val in zip(range(0, len(char_pos)), char_pos): + px_pos[index] = char_pos[index] * self._if_dims[index] + + return px_pos + + +if __name__ == "__main__": + import urllib + txt = urllib.urlopen('file:///home/joar/Dropbox/ascii/install-all-the-dependencies.txt') + + _log.setLevel(logging.DEBUG) + logging.basicConfig() + + converter = AsciiToImage() + + converter.convert(txt.read(), '/tmp/test.png') + + ''' + im, x, y, duration = renderImage(h, 10) + print "Rendered image in %.5f seconds" % duration + im.save('tldr.png', "PNG") + ''' diff --git a/mediagoblin/media_types/ascii/fonts/Inconsolata.otf b/mediagoblin/media_types/ascii/fonts/Inconsolata.otf new file mode 120000 index 00000000..4e742b5e --- /dev/null +++ b/mediagoblin/media_types/ascii/fonts/Inconsolata.otf @@ -0,0 +1 @@ +../../../../extlib/inconsolata/Inconsolata.otf
\ No newline at end of file diff --git a/mediagoblin/media_types/ascii/processing.py b/mediagoblin/media_types/ascii/processing.py new file mode 100644 index 00000000..a74690c1 --- /dev/null +++ b/mediagoblin/media_types/ascii/processing.py @@ -0,0 +1,93 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +import asciitoimage +import chardet +import os +import Image + +from mediagoblin import mg_globals as mgg +from mediagoblin.processing import create_pub_filepath, THUMB_SIZE + + +def process_ascii(entry): + ''' + Code to process a txt file + ''' + workbench = mgg.workbench_manager.create_workbench() + # Conversions subdirectory to avoid collisions + conversions_subdir = os.path.join( + workbench.dir, 'conversions') + os.mkdir(conversions_subdir) + + queued_filepath = entry['queued_media_file'] + queued_filename = workbench.localized_file( + mgg.queue_store, queued_filepath, + 'source') + + queued_file = file(queued_filename, 'rb') + + with queued_file: + queued_file_charset = chardet.detect(queued_file.read()) + + queued_file.seek(0) # Rewind the queued file + + thumb_filepath = create_pub_filepath( + entry, 'thumbnail.png') + + tmp_thumb_filename = os.path.join( + conversions_subdir, thumb_filepath[-1]) + + converter = asciitoimage.AsciiToImage() + + thumb = converter._create_image( + queued_file.read()) + + with file(tmp_thumb_filename, 'w') as thumb_file: + thumb.thumbnail(THUMB_SIZE, Image.ANTIALIAS) + thumb.save(thumb_file) + + mgg.public_store.copy_local_to_storage( + tmp_thumb_filename, thumb_filepath) + + queued_file.seek(0) + + original_filepath = create_pub_filepath(entry, queued_filepath[-1]) + + with mgg.public_store.get_file(original_filepath, 'wb') \ + as original_file: + original_file.write(queued_file.read()) + + + queued_file.seek(0) # Rewind *again* + + unicode_filepath = create_pub_filepath(entry, 'unicode.txt') + + with mgg.public_store.get_file(unicode_filepath, 'wb') \ + as unicode_file: + unicode_file.write( + unicode(queued_file.read().decode( + queued_file_charset['encoding'])).encode( + 'ascii', + 'xmlcharrefreplace')) + + mgg.queue_store.delete_file(queued_filepath) + entry['queued_media_file'] = [] + media_files_dict = entry.setdefault('media_files', {}) + media_files_dict['thumb'] = thumb_filepath + media_files_dict['unicode'] = unicode_filepath + media_files_dict['original'] = original_filepath + + entry.save() |