gramps/gramps/gen/utils/image.py
Serge Noiraud 31b80da797
Multiple languages ​​for the narrative web and optional other additions (#1051)
* Navweb: multi-language configuration.
* Navweb: multi-language exec, navigation menu, css
* Navweb: make css responsive for multi-language
* Language menu must work with a non english lang
* Avoid to copy common files for each language
* Use cms and archive in multi-languages
* Add the default index.
* Some cleanup.
* Images: performances improvement with image_size

Need to use the magic library (python-magic, python3-magic, ...)
If this library don't exist on the system, continue to use the Gdk method.

- force archive if we use CMS
- remove some unused code
- some strings are not translatable
- stay on the same page when you change language
- add image in the ancestor tree for all levels and not for the first 5

performances:
- copy images and thumbs only for the first language
* Some minor corrections and performances test
* Update comments for methods and functions
* Narweb: add show tags option
* Try to translate tags and suppress the colon (:)
* Narweb: integrate of webcal for multilang use
For each lang, we use the related calendar if it exists
* Death string only translated for the locale lang
* set correct url for extrapage.
* Add optional toggle for html sections
* show birth and death date if close option selected
* No background in references section with Mainz css
* Remove photo from list incompatible with multilang
* Add the first photo to the place page marker
* Add associated persons.
* Solves 'undefined' in map popup.
* Calendar: Set the background for the current day
* Thumbnail align problem with long description.
* Set the contact page date to the note date
* Add a scroll to top button.
* Increase the nb of generations since we can scroll
* Difficult to see the "go to top" icon.
* Better management for the toggle switch
2020-11-09 12:10:25 +01:00

308 lines
10 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 ..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 end_x-start_x > 0 and end_y-start_y > 0:
img = img.new_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 GLib
try:
import re
import magic
# For performance reasons, we'll try to get image size from magic.
# This avoid to load the image in memory. This is a real improvement
# when we have many big images.
# Used in odfdoc, rtfdoc and webreport and tested with png, gif, jpeg
#
# file size with magic without (Gdk) ratio
# example 1 : 256k 0.00080 0.00575 7
# example 2 : 21M 0.00171 0.55860 326
img = magic.from_file(source)
found = img.find("precision")
if found > 0:
img = img[found:]
size = re.search('(\d+)\s*x\s*(\d+)', img).groups()
return (int(size[0]), int(size[1]))
except ImportError:
# python-magic is not installed. So Trying to get image size with Gdk.
try:
img = GdkPixbuf.Pixbuf.new_from_file(source)
width = img.get_width()
height = img.get_height()
except GLib.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 end_x-start_x > 0 and end_y-start_y > 0:
img = img.new_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 end_x-start_x > 0 and end_y-start_y > 0:
img = img.new_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)
scaled.savev(dest, "jpeg", "", "")
with open(dest, mode='rb') as ofile:
data = ofile.read()
try:
os.unlink(dest)
except:
pass
return data