Source code for gramps.gen.utils.image

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006  Donald N. Allingham
# Copyright (C) 2011       Adam Stein <adam@csh.rit.edu>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

"""
Image manipulation routines.
"""

#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import sys
import tempfile

#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------

#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from ..constfunc import conv_to_unicode
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext

[docs]def crop_percentage_to_subpixel(width, height, crop): """ Convert from Gramps cropping coordinates [0, 100] to pixels, given image width and height. No rounding to pixel resolution. """ return ( crop[0]/100.0*width, crop[1]/100.0*height, crop[2]/100.0*width, crop[3]/100.0*height )
[docs]def crop_percentage_to_pixel(width, height, crop): return map (int, crop_percentage_to_subpixel(width, height, crop)) #------------------------------------------------------------------------- # # resize_to_jpeg # #-------------------------------------------------------------------------
[docs]def resize_to_jpeg(source, destination, width, height, crop=None): """ Create the destination, derived from the source, resizing it to the specified size, while converting to JPEG. :param source: source image file, in any format that gtk recognizes :type source: unicode :param destination: destination image file, output written in jpeg format :type destination: unicode :param width: desired width of the destination image :type width: int :param height: desired height of the destination image :type height: int :param crop: cropping coordinates :type crop: array of integers ([start_x, start_y, end_x, end_y]) """ from gi.repository import GdkPixbuf img = GdkPixbuf.Pixbuf.new_from_file(source) if crop: (start_x, start_y, end_x, end_y ) = crop_percentage_to_pixel( img.get_width(), img.get_height(), crop) if sys.version_info[0] < 3: img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) else: img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) # Need to keep the ratio intact, otherwise scaled images look stretched # if the dimensions aren't close in size (width, height) = image_actual_size(width, height, img.get_width(), img.get_height()) scaled = img.scale_simple(int(width), int(height), GdkPixbuf.InterpType.BILINEAR) scaled.savev(destination, "jpeg" "", "") #------------------------------------------------------------------------- # # image_dpi # #-------------------------------------------------------------------------
MM_PER_INCH = 25.4
[docs]def image_dpi(source): """ Return the dpi found in the image header. Use a sensible default of the screen DPI or 96.0 dpi if N/A. :param source: source image file, in any format that PIL recognizes :type source: unicode :rtype: int :returns: (x_dpi, y_dpi) """ try: import PIL.Image except ImportError: import logging logging.warning(_("WARNING: PIL module not loaded. " "Image cropping in report files will be impaired.")) else: try: img = PIL.Image.open(source) except IOError: pass else: try: dpi = img.info["dpi"] return dpi except (AttributeError, KeyError): pass try: from gi.repository import Gdk s = Gdk.Display.get_default().get_default_screen() dpi = ( s.get_width() * MM_PER_INCH / s.get_width_mm(), s.get_height() * MM_PER_INCH / s.get_height_mm() ) except: dpi = (96.0,96.0) #LibOO 3.6 assumes this if image contains no DPI info # This isn't safe even within a single platform (Windows), but we # can't do better if all of the above failed. See bug# 7290. return dpi #------------------------------------------------------------------------- # # image_size # #-------------------------------------------------------------------------
[docs]def image_size(source): """ Return the width and size of the specified image. :param source: source image file, in any format that gtk recongizes :type source: unicode :rtype: tuple(int, int) :returns: a tuple consisting of the width and height """ from gi.repository import GdkPixbuf from gi.repository import GObject try: img = GdkPixbuf.Pixbuf.new_from_file(source) width = img.get_width() height = img.get_height() except GObject.GError: width = 0 height = 0 return (width, height) #------------------------------------------------------------------------- # # image_actual_size # #-------------------------------------------------------------------------
[docs]def image_actual_size(x_cm, y_cm, x, y): """ Calculate what the actual width & height of the image should be. :param x_cm: width in centimeters :type source: int :param y_cm: height in centimeters :type source: int :param x: desired width in pixels :type source: int :param y: desired height in pixels :type source: int :rtype: tuple(int, int) :returns: a tuple consisting of the width and height in centimeters """ ratio = float(x_cm)*float(y)/(float(y_cm)*float(x)) if ratio < 1: act_width = x_cm act_height = y_cm*ratio else: act_height = y_cm act_width = x_cm/ratio return (act_width, act_height) #------------------------------------------------------------------------- # # resize_to_buffer # #-------------------------------------------------------------------------
[docs]def resize_to_buffer(source, size, crop=None): """ Loads the image and resizes it. Instead of saving the file, the data is returned in a buffer. :param source: source image file, in any format that gtk recognizes :type source: unicode :param size: desired size of the destination image ([width, height]) :type size: list :param crop: cropping coordinates :type crop: array of integers ([start_x, start_y, end_x, end_y]) :rtype: buffer of data :returns: raw data """ from gi.repository import GdkPixbuf img = GdkPixbuf.Pixbuf.new_from_file(source) if crop: (start_x, start_y, end_x, end_y ) = crop_percentage_to_pixel( img.get_width(), img.get_height(), crop) if sys.version_info[0] < 3: img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) else: img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) # Need to keep the ratio intact, otherwise scaled images look stretched # if the dimensions aren't close in size (size[0], size[1]) = image_actual_size(size[0], size[1], img.get_width(), img.get_height()) scaled = img.scale_simple(int(size[0]), int(size[1]), GdkPixbuf.InterpType.BILINEAR) return scaled #------------------------------------------------------------------------- # # resize_to_jpeg_buffer # #-------------------------------------------------------------------------
[docs]def resize_to_jpeg_buffer(source, size, crop=None): """ Loads the image, converting the file to JPEG, and resizing it. Instead of saving the file, the data is returned in a buffer. :param source: source image file, in any format that gtk recognizes :type source: unicode :param size: desired size of the destination image ([width, height]) :type size: list :param crop: cropping coordinates :type crop: array of integers ([start_x, start_y, end_x, end_y]) :rtype: buffer of data :returns: jpeg image as raw data """ from gi.repository import GdkPixbuf filed, dest = tempfile.mkstemp() img = GdkPixbuf.Pixbuf.new_from_file(source) if crop: (start_x, start_y, end_x, end_y ) = crop_percentage_to_pixel( img.get_width(), img.get_height(), crop) if sys.version_info[0] < 3: img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) else: img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y) # Need to keep the ratio intact, otherwise scaled images look stretched # if the dimensions aren't close in size (size[0], size[1]) = image_actual_size(size[0], size[1], img.get_width(), img.get_height()) scaled = img.scale_simple(int(size[0]), int(size[1]), GdkPixbuf.InterpType.BILINEAR) os.close(filed) dest = conv_to_unicode(dest, None) scaled.savev(dest, "jpeg", "", "") ofile = open(dest, mode='rb') data = ofile.read() ofile.close() try: os.unlink(dest) except: pass return data