1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 from mediagoblin
.tools
.extlib
.EXIF
import process_file
, Ratio
18 from mediagoblin
.processing
import BadMediaFail
19 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
21 # A list of tags that should be stored for faster access
31 'EXIF ISOSpeedRatings',
36 def exif_image_needs_rotation(exif_tags
):
38 Returns True if EXIF orientation requires rotation
40 return 'Image Orientation' in exif_tags \
41 and exif_tags
['Image Orientation'].values
[0] != 1
44 def exif_fix_image_orientation(im
, exif_tags
):
46 Translate any EXIF orientation to raw orientation
49 - REDUCES IMAGE QUALITY by recompressig it
55 if 'Image Orientation' in exif_tags
:
60 orientation
= exif_tags
['Image Orientation'].values
[0]
61 if orientation
in rotation_map
.keys():
63 rotation_map
[orientation
])
68 def extract_exif(filename
):
70 Returns EXIF tags found in file at ``filename``
75 image
= open(filename
)
76 exif_tags
= process_file(image
)
78 raise BadMediaFail(_('Could not read the image file.'))
85 Clean the result from anything the database cannot handle
87 # Discard any JPEG thumbnail, for database compatibility
88 # and that I cannot see a case when we would use it.
89 # It takes up some space too.
91 'Thumbnail JPEGInterchangeFormatLength',
93 'Thumbnail JPEGInterchangeFormat']
97 for key
, value
in exif
.items():
98 if not key
in disabled_tags
:
99 clean_exif
[key
] = _ifd_tag_to_dict(value
)
104 def _ifd_tag_to_dict(tag
):
106 Takes an IFD tag object from the EXIF library and converts it to a dict
107 that can be stored as JSON in the database.
110 'printable': tag
.printable
,
112 'field_type': tag
.field_type
,
113 'field_offset': tag
.field_offset
,
114 'field_length': tag
.field_length
,
117 if isinstance(tag
.printable
, str):
118 # Force it to be decoded as UTF-8 so that it'll fit into the DB
119 data
['printable'] = tag
.printable
.decode('utf8', 'replace')
121 if type(tag
.values
) == list:
123 for val
in tag
.values
:
124 if isinstance(val
, Ratio
):
125 data
['values'].append(
128 data
['values'].append(val
)
130 if isinstance(tag
.values
, str):
131 # Force UTF-8, so that it fits into the DB
132 data
['values'] = tag
.values
.decode('utf8', 'replace')
134 data
['values'] = tag
.values
139 def _ratio_to_list(ratio
):
140 return [ratio
.num
, ratio
.den
]
143 def get_useful(tags
):
145 for key
, tag
in tags
.items():
146 if key
in USEFUL_TAGS
:
152 def get_gps_data(tags
):
154 Processes EXIF data returned by EXIF.py
158 if not 'Image GPSInfo' in tags
:
163 'latitude': tags
['GPS GPSLatitude'],
164 'longitude': tags
['GPS GPSLongitude']}
166 for key
, dat
in dms_data
.items():
169 float(v
[0].num
) / float(v
[0].den
) \
170 + (float(v
[1].num
) / float(v
[1].den
) / 60) \
171 + (float(v
[2].num
) / float(v
[2].den
) / (60 * 60))
174 if tags
['GPS GPSLatitudeRef'].values
== 'S':
175 gps_data
['latitude'] /= -1
177 if tags
['GPS GPSLongitudeRef'].values
== 'W':
178 gps_data
['longitude'] /= -1
184 gps_data
['direction'] = (
186 float(d
.num
) / float(d
.den
)
187 )(tags
['GPS GPSImgDirection'].values
[0])
192 gps_data
['altitude'] = (
194 float(a
.num
) / float(a
.den
)
195 )(tags
['GPS GPSAltitude'].values
[0])