diff --git a/fanficfare/story.py b/fanficfare/story.py index 14036dfc..9c3270b4 100644 --- a/fanficfare/story.py +++ b/fanficfare/story.py @@ -57,7 +57,7 @@ imagetypes = { try: from calibre.utils.magick import Image from .six import BytesIO - from gif import GifInfo, CHECK_IS_ANIMATED + # from gif import GifInfo, CHECK_IS_ANIMATED convtype = {'jpg':'JPG', 'png':'PNG'} def convert_image(url,data,sizes,grayscale, @@ -74,8 +74,9 @@ try: nwidth, nheight = sizes scaled, nwidth, nheight = fit_image(owidth, oheight, nwidth, nheight) - if normalize_format_name(img.format)=="gif" and GifInfo(BytesIO(data),CHECK_IS_ANIMATED).frameCount > 1: - raise exceptions.RejectImage("Animated gifs come out poorly--not going to use it.") + # Calibre's Image seems to have improved since this was added in Apr 2015 + # if normalize_format_name(img.format)=="gif" and GifInfo(BytesIO(data),CHECK_IS_ANIMATED).frameCount > 1: + # raise exceptions.RejectImage("Animated gifs come out poorly--not going to use it.") if scaled: img.size = (nwidth, nheight) @@ -164,7 +165,7 @@ def no_convert_image(url,data): # parameter. ext = url[url.rfind('.')+1:].lower() if ext not in imagetypes: - logger.debug("no_convert_image url:%s - no known extension"%url) + logger.debug("no_convert_image url:%s - no known extension -- using .jpg"%url) # doesn't have extension? use jpg. ext='jpg' diff --git a/included_dependencies/gif.py b/included_dependencies/gif.py deleted file mode 100644 index 406bab2c..00000000 --- a/included_dependencies/gif.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -A pure Python GIF metadata extractor. -Supports adjustable detail to fine-tune performance. - -Example code and full epydoc docstrings included. - -Uses: - - Identifying whether a GIF is static or animated. - - Extracting the dimensions, pixel aspect ratio, number of frames, loop count, - global palette or palette size, and background color. - - Extracting comments and other plaintext. - - Testing for various structural errors. - -TODO: - - Provide basic support for XMP Metadata extraction - - http://en.wikipedia.org/wiki/Extensible_Metadata_Platform#Location_in_file_types - - http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp - - Generate test GIF with http://code.google.com/p/python-xmp-toolkit/ - -Changelog: - - 0.2.2: Audited the code and made some corrections. - - 0.2.1: 40% speed improvement (went from 15 to 9 seconds for 1000 images) - - 0.2.0: Feature-complete - - 0.1.0: Initial release -""" - -__appname__ = "gif.py" -__author__ = "Stephan Sokolow (deitarion/SSokolow)" -__version__ = "0.2.2" -__license__ = "PSF License 2.4 or higher (The Python License)" - -#{ Check Types (enum, numerical ordering is significant) -CHECK_IS_GIF_FILE = 0 #: Just check for a valid GIF header. -CHECK_IS_ANIMATED = 1 #: Check whether the file has more than one frame. -CHECK_COUNT_FRAMES = 2 #: Count the number of frames in the file. -CHECK_PARSE_PALETTE = 3 #: Parse the palette and resolve the background color. -CHECK_READ_COMMENTS = 4 #: Load comments (can sometimes be large) into the L{GifInfo} object. -CHECK_READ_ALL_TEXT = 5 #: Also load the contents of Plain Text extension blocks. - -CHECK_ALL = CHECK_READ_ALL_TEXT #: alias to allow for future modifications - -#{ Warning Codes (bitfield) -WARN_NONE = 0 #: No warnings -WARN_BAD_IMG = 1 #: Corruption (of the [sub]block size field(s)) or truncation detected in an image block -WARN_BAD_EXT = 2 #: Corruption (of the [sub]block size field(s)) or truncation detected in an extension block -WARN_BAD_SIZE = 4 #: An image block specifies dimensions exceeding the global canvas size -WARN_BAD_BGCOLOR = 8 #: The background color index specified is greater than the palette size -WARN_EOF = 16 #: File is missing it's trailer. (Corrupt elsewhere, truncated, or breaking spec by using EOF as the terminator.) -WARN_TRUNC = 32 #: File is definitively either truncated or corrupt. (An EOF was encountered part-way through a structure.) -WARN_LOOP_POS = 64 #: Netscape Application Extension block (animation-control) was present but not first in the file. -#} - -import struct - -#{ Structures used by GifInfo -gifHeaderStruct = struct.Struct('= CHECK_PARSE_PALETTE: - self.palette = [] - for pos in range(0, self.paletteSize): - self.palette.append(gifColorTripleStruct.unpack_from(rawPalette, pos * 3)) - - if self.paletteSize and bgColor > self.paletteSize: - self.warnFlags = self.warnFlags | WARN_BAD_BGCOLOR - elif self.palette: - self.bgColor = self.palette[bgColor] - - # Iterate blocks - self.firstBlock = True - blocktype = self._read(fh, 1) - while not blocktype == chr(0x3B) and not self.warnFlags & WARN_EOF: - self._blockHandlers.get(blocktype, lambda x, y:'')(self, fh) - if self.checkLevel <= CHECK_IS_ANIMATED and self.frameCount > 1: - return - - self.firstBlock = False - blocktype = self._read(fh, 1) - - del self.firstBlock - - def _handleImageBlock(self, fh): - """""" - self.frameCount += 1 - try: - x, y, w, h, LCTF_Byte = gifImageStruct.unpack(self._read(fh, gifImageStruct.size)) - except: - self.warnFlags = self.warnFlags | WARN_EOF | WARN_TRUNC - return - - if x + w > self.width or y + h > self.height: - self.warnFlags = self.warnFlags | WARN_BAD_SIZE - - self._getPalette(fh, LCTF_Byte) # Skip the local color table if present - fh.read(1) # Skip the LZW minimum code size. - - # Skip content and test for the block terminator - if not self._skipSubBlocks(fh): # For example, if it's a zero-length string like EOF would return. - self.warnFlags = self.warnFlags | WARN_BAD_IMG - - def _handleGenericExtensionBlock(self, fh): - """@todo: Rewrite this so extension block types have method handlers.""" - try: - extType, blkSize = gifExtenStruct.unpack(self._read(fh, gifExtenStruct.size)) - except: - self.warnFlags = self.warnFlags | WARN_EOF | WARN_TRUNC - return - startOffset = fh.tell() - - if extType == 0x01 and self.checkLevel >= CHECK_READ_ALL_TEXT: # Plain Text Block - self._read(fh, gifPlaintextStruct.size) - self.otherText = self.otherText or [] - blkSize = self._read(fh, 1) - while blkSize and blkSize != '\x00': - self.otherText.append(self._read(fh, ord(blkSize))) - blkSize = self._read(fh, 1) - elif extType == 0xFE and self.checkLevel >= CHECK_READ_COMMENTS: # Comment Block - self.comments = self.comments or [] - blkSize = self._read(fh, 1) - while blkSize and blkSize != '\x00': - self.comments.append(self._read(fh, ord(blkSize))) - blkSize = self._read(fh, 1) - elif extType == 0xFF: # Application Block - if blkSize == 0x0B and self._read(fh, blkSize) == "NETSCAPE2.0": - try: - a, b, self.loopCount = gifNetscapeStruct.unpack(self._read(fh, gifNetscapeStruct.size)) - except: - self.warnFlags = self.warnFlags | WARN_EOF | WARN_TRUNC - return - - if a != 3 and b != 1: - self.warnFlags = self.warnFlags | WARN_BAD_EXT - - if not self.firstBlock: - self.warnFlags = self.warnFlags | WARN_LOOP_POS - else: - fh.seek( startOffset + blkSize ) # Skip the contents - - # Test for the block terminator - if not self._skipSubBlocks(fh): - self.warnFlags = self.warnFlags | WARN_BAD_EXT - - def _getPalette(self, handle, bitfield): - """Using the size value from C{bitfield}, - load the palette at C{handle}'s current file pointer position.""" - if bitfield & int("10000000", 2): - nBits = bitfield & int("00000111", 2) - tableSize = 3 * 2**( nBits + 1 ) - return handle.read(tableSize) - else: - return '' - - def _read(self, handle, size): - """Attempt to read the specified number of bytes. Set L{WARN_EOF} if - fewer are received.""" - content = handle.read(size) - if len(content) < size: - self.warnFlags = self.warnFlags | WARN_EOF - return content - - def _skipSubBlocks(self, handle): - """Skip sub-blocks beginning at the current file pointer position - using fseek.""" - offset = handle.tell() - blkSize = handle.read(1) - while blkSize and blkSize != '\x00': - offset += ord(blkSize) + 1 - handle.seek(offset) - blkSize = handle.read(1) - return blkSize - - _blockHandlers = { - chr(0x2C) : _handleImageBlock, - chr(0x21) : _handleGenericExtensionBlock, - } - -def gif_is_animated(path): - """A simple convenience function for testing whether a GIF is animated. - @rtype: C{bool} - """ - return GifInfo(file(path,'rb'), CHECK_IS_ANIMATED).frameCount > 1 - -if __name__ == '__main__': - from optparse import OptionParser - parser = OptionParser(description=__doc__.split('\n\n')[0], - version="%%prog v%s" % __version__, usage="%prog ...") - - opts, args = parser.parse_args() - - if args: - for fpath in args: - try: - info = GifInfo(fpath, CHECK_COUNT_FRAMES) - warnFlags = ( - (info.warnFlags & WARN_BAD_IMG and 'I' or ' ') + - (info.warnFlags & WARN_BAD_EXT and 'X' or ' ') + - (info.warnFlags & WARN_BAD_SIZE and 'C' or ' ') + - (info.warnFlags & WARN_BAD_BGCOLOR and 'B' or ' ') + - (info.warnFlags & WARN_EOF and 'E' or ' ') + - (info.warnFlags & WARN_TRUNC and 'T' or ' ') + - (info.warnFlags & WARN_LOOP_POS and 'L' or ' ') - ) - print "[%s](%3s Frames): %s" % (warnFlags, info.frameCount, info.path) - except BadHeaderException, err: - print "%s: %s" % (str(err), fpath) - print "\nWarning Flags:" - print " I = Image Chunk Corruption/Truncation" - print " X = Extension Chunk Corruption/Truncation" - print " C = Image Chunk Dimensions Exceed Global Canvas" - print " B = Bad Background Color (Index Exceeds Palette Size)" - print " E = Unexpected EOF Encountered (Missing Image Terminator)" - print " T = EOF Encountered Within A Block Header (Corrupt or Truncated File)" - print " L = Loop-control block misplaced within the file" - print - print "Note: A nearly-threefold speed-up can be had by using CHECK_IS_ANIMATED rather than CHECK_COUNT_FRAMES" diff --git a/makeplugin.py b/makeplugin.py index 72b69cbc..7790c524 100644 --- a/makeplugin.py +++ b/makeplugin.py @@ -35,9 +35,9 @@ if __name__=="__main__": exclude=exclude) os.chdir('../included_dependencies') - files=['gif.py','bs4','chardet','html2text'] + files=['bs4','chardet','html2text'] # calibre has it's own copies of these that precedence anyway: - # 'six.py','html5lib','webencodings' + # 'html5lib','webencodings' # webencodings is only needed by versions of html5lib after 0.9x7 # 'a' for append createZipFile("../"+filename,"a",