gramps/gramps/gen/utils/image.py
2014-08-08 19:39:45 -07:00

296 lines
9.9 KiB
Python

#
# 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
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 )
def crop_percentage_to_pixel(width, height, crop):
return map (int, crop_percentage_to_subpixel(width, height, crop))
#-------------------------------------------------------------------------
#
# resize_to_jpeg
#
#-------------------------------------------------------------------------
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
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
#
#-------------------------------------------------------------------------
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
#
#-------------------------------------------------------------------------
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
#
#-------------------------------------------------------------------------
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
#
#-------------------------------------------------------------------------
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