Commit 5cde3441 authored by Jonas Bushart's avatar Jonas Bushart Committed by Lucas Beeler

Closes #2556

Allows metadata to be optionally stripped when photos are exported from Shotwell.
parent d2da162b
......@@ -9,6 +9,7 @@ Joeny Ang <ang.joeny@gmail.com>
Valentín Barros <valentin@sanva.net>
Philip Beam <psbeam@gmail.com>
Jürg Billeter <j@bitron.ch>
Jonas Bushart <eberhardtjonas@aol.com>
Matthias Clasen <matthias.clasen@gmail.com>
Valentin David <valentin.david@gmail.com>
Josh Freeman <josh@twilightedge.com>
......
......@@ -465,7 +465,7 @@ public abstract class CollectionPage : MediaPage {
AppWindow.get_instance().set_busy_cursor();
photo.export(save_as, scaling, export_params.quality,
photo.get_export_format_for_parameters(export_params), export_params.mode ==
ExportFormatMode.UNMODIFIED);
ExportFormatMode.UNMODIFIED, export_params.export_metadata);
AppWindow.get_instance().set_normal_cursor();
} catch (Error err) {
AppWindow.get_instance().set_normal_cursor();
......
......@@ -140,6 +140,7 @@ public class ExportDialog : Gtk.Dialog {
private Gtk.ComboBox quality_combo;
private Gtk.ComboBox constraint_combo;
private Gtk.ComboBox format_combo;
private Gtk.CheckButton export_metadata;
private Gee.ArrayList<string> format_options = new Gee.ArrayList<string>();
private Gtk.Entry pixels_entry;
private Gtk.Widget ok_button;
......@@ -205,6 +206,10 @@ public class ExportDialog : Gtk.Dialog {
pixels_box.pack_end(pixels_label, false, false, 0);
add_control(pixels_box, 1, 3);
export_metadata = new Gtk.CheckButton.with_label(_("Export metadata"));
add_control(export_metadata, 1, 4);
export_metadata.active = true;
((Gtk.VBox) get_content_area()).add(table);
// add buttons to action area
......@@ -300,6 +305,8 @@ public class ExportDialog : Gtk.Dialog {
assert(scale > 0);
current_scale = scale;
parameters.export_metadata = export_metadata.active;
if (format_combo.get_active_text() == UNMODIFIED_FORMAT_LABEL) {
parameters.mode = current_parameters.mode = ExportFormatMode.UNMODIFIED;
} else if (format_combo.get_active_text() == CURRENT_FORMAT_LABEL) {
......@@ -364,6 +371,8 @@ public class ExportDialog : Gtk.Dialog {
constraint_combo.set_sensitive(false);
quality_combo.set_sensitive(false);
pixels_entry.sensitive = false;
export_metadata.active = false;
export_metadata.sensitive = false;
} else if (format_combo.get_active_text() == CURRENT_FORMAT_LABEL) {
// if the user wishes to export the media in its current format, we allow sizing but
// not JPEG quality customization, because in a batch of many photos, it's not
......@@ -373,7 +382,8 @@ public class ExportDialog : Gtk.Dialog {
// format.
constraint_combo.set_sensitive(true);
quality_combo.set_sensitive(false);
pixels_entry.sensitive = !original;
pixels_entry.sensitive = !original;
export_metadata.sensitive = true;
} else {
// if the user has chosen a specific format, then allow JPEG quality customization if
// the format is JPEG and the user is re-sizing the image, otherwise, disallow JPEG
......@@ -381,6 +391,7 @@ public class ExportDialog : Gtk.Dialog {
constraint_combo.set_sensitive(true);
bool jpeg = get_specified_format() == PhotoFileFormat.JFIF;
quality_combo.sensitive = !original && jpeg;
export_metadata.sensitive = true;
}
}
......
......@@ -15,12 +15,14 @@ public struct ExportFormatParameters {
public ExportFormatMode mode;
public PhotoFileFormat specified_format;
public Jpeg.Quality quality;
public bool export_metadata;
private ExportFormatParameters(ExportFormatMode mode, PhotoFileFormat specified_format,
Jpeg.Quality quality) {
this.mode = mode;
this.specified_format = specified_format;
this.quality = quality;
this.export_metadata = true;
}
public static ExportFormatParameters current() {
......@@ -71,10 +73,11 @@ public class Exporter : Object {
public PhotoFileFormat? format;
public Error? err = null;
public bool direct_copy_unmodified = false;
public bool export_metadata = true;
public ExportJob(Exporter owner, MediaSource media, File dest, Scaling? scaling,
Jpeg.Quality? quality, PhotoFileFormat? format, Cancellable cancellable,
bool direct_copy_unmodified = false) {
bool direct_copy_unmodified = false, bool export_metadata = true) {
base (owner, owner.on_exported, cancellable, owner.on_export_cancelled);
assert(media is Photo || media is Video);
......@@ -85,12 +88,13 @@ public class Exporter : Object {
this.quality = quality;
this.format = format;
this.direct_copy_unmodified = direct_copy_unmodified;
this.export_metadata = export_metadata;
}
public override void execute() {
try {
if (media is Photo) {
((Photo) media).export(dest, scaling, quality, format, direct_copy_unmodified);
((Photo) media).export(dest, scaling, quality, format, direct_copy_unmodified, export_metadata);
} else if (media is Video) {
((Video) media).export(dest);
}
......@@ -283,7 +287,7 @@ public class Exporter : Object {
}
workers.enqueue(new ExportJob(this, source, dest, scaling, export_params.quality,
real_export_format, cancellable, export_params.mode == ExportFormatMode.UNMODIFIED));
real_export_format, cancellable, export_params.mode == ExportFormatMode.UNMODIFIED, export_params.export_metadata));
submitted++;
}
......
......@@ -2915,7 +2915,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
private bool export_fullsized_backing(File file) throws Error {
private bool export_fullsized_backing(File file, bool export_metadata = true) throws Error {
// See if the native reader supports writing ... if no matches, need to fall back
// on a "regular" export, which requires decoding then encoding
PhotoFileReader export_reader = null;
......@@ -2950,8 +2950,8 @@ public abstract class Photo : PhotoSource, Dateable {
FileCopyFlags.OVERWRITE | FileCopyFlags.TARGET_DEFAULT_PERMS, null, null);
// If asking for an full-sized file and there are no alterations (transformations or EXIF)
// *and* this is a copy of the original backing *and* there's no user metadata or title, then done
if (!has_alterations() && is_master && !has_user_generated_metadata() && (get_title() == null))
// *and* this is a copy of the original backing *and* there's no user metadata or title *and* metadata should be exported, then done
if (!has_alterations() && is_master && !has_user_generated_metadata() && (get_title() == null) && export_metadata)
return true;
// copy over relevant metadata if possible, otherwise generate new metadata
......@@ -2966,15 +2966,21 @@ public abstract class Photo : PhotoSource, Dateable {
else
metadata.set_exposure_date_time(null);
metadata.set_title(get_title());
metadata.set_pixel_dimensions(get_dimensions()); // created by sniffing pixbuf not metadata
metadata.set_orientation(get_orientation());
metadata.set_software(Resources.APP_TITLE, Resources.APP_VERSION);
if(export_metadata) {
//set metadata
metadata.set_title(get_title());
metadata.set_pixel_dimensions(get_dimensions()); // created by sniffing pixbuf not metadata
metadata.set_orientation(get_orientation());
metadata.set_software(Resources.APP_TITLE, Resources.APP_VERSION);
if (get_orientation() != get_original_orientation())
metadata.remove_exif_thumbnail();
if (get_orientation() != get_original_orientation())
metadata.remove_exif_thumbnail();
set_user_metadata_for_export(metadata);
set_user_metadata_for_export(metadata);
}
else
//delete metadata
metadata.clear();
writer.write_metadata(metadata);
......@@ -2993,7 +2999,7 @@ public abstract class Photo : PhotoSource, Dateable {
//
// This method is thread-safe.
public void export(File dest_file, Scaling scaling, Jpeg.Quality quality,
PhotoFileFormat export_format, bool direct_copy_unmodified = false) throws Error {
PhotoFileFormat export_format, bool direct_copy_unmodified = false, bool export_metadata = true) throws Error {
if (direct_copy_unmodified) {
get_master_file().copy(dest_file, FileCopyFlags.OVERWRITE |
FileCopyFlags.TARGET_DEFAULT_PERMS, null, null);
......@@ -3006,7 +3012,7 @@ public abstract class Photo : PhotoSource, Dateable {
// the original file and update relevant EXIF.
if (scaling.is_unscaled() && (!has_alterations() || only_metadata_changed()) &&
(export_format == get_file_format()) && (get_file_format() == PhotoFileFormat.JFIF)) {
if (export_fullsized_backing(dest_file))
if (export_fullsized_backing(dest_file, export_metadata))
return;
}
......@@ -3030,21 +3036,28 @@ public abstract class Photo : PhotoSource, Dateable {
if (metadata == null)
metadata = export_format.create_metadata();
metadata.set_title(get_title());
metadata.set_pixel_dimensions(Dimensions.for_pixbuf(pixbuf));
metadata.set_orientation(Orientation.TOP_LEFT);
metadata.set_software(Resources.APP_TITLE, Resources.APP_VERSION);
if (export_metadata) {
//set metadata
metadata.set_title(get_title());
metadata.set_pixel_dimensions(Dimensions.for_pixbuf(pixbuf));
metadata.set_orientation(Orientation.TOP_LEFT);
metadata.set_software(Resources.APP_TITLE, Resources.APP_VERSION);
if (get_exposure_time() != 0)
metadata.set_exposure_date_time(new MetadataDateTime(get_exposure_time()));
else
metadata.set_exposure_date_time(null);
if (get_exposure_time() != 0)
metadata.set_exposure_date_time(new MetadataDateTime(get_exposure_time()));
else
metadata.set_exposure_date_time(null);
metadata.remove_tag("Exif.Iop.RelatedImageWidth");
metadata.remove_tag("Exif.Iop.RelatedImageHeight");
metadata.remove_exif_thumbnail();
metadata.remove_tag("Exif.Iop.RelatedImageWidth");
metadata.remove_tag("Exif.Iop.RelatedImageHeight");
metadata.remove_exif_thumbnail();
if(has_user_generated_metadata())
set_user_metadata_for_export(metadata);
if (has_user_generated_metadata())
set_user_metadata_for_export(metadata);
}
else
//delete metadata
metadata.clear();
export_format.create_metadata_writer(dest_file.get_path()).write_metadata(metadata);
}
......
......@@ -3052,7 +3052,7 @@ public class LibraryPhotoPage : EditingHostPage {
try {
get_photo().export(save_as, scaling, export_params.quality,
get_photo().get_export_format_for_parameters(export_params),
export_params.mode == ExportFormatMode.UNMODIFIED);
export_params.mode == ExportFormatMode.UNMODIFIED, export_params.export_metadata);
} catch (Error err) {
AppWindow.error_message(_("Unable to export %s: %s").printf(save_as.get_path(), err.message));
}
......
......@@ -51,7 +51,7 @@ public class MediaSourcePublishableWrapper : Spit.Publishing.Publishable, GLib.O
try {
Scaling scaling = (content_major_axis > 0) ?
Scaling.for_best_fit(content_major_axis, false) : Scaling.for_original();
photo.export(to_file, scaling, Jpeg.Quality.HIGH, PhotoFileFormat.JFIF);
photo.export(to_file, scaling, Jpeg.Quality.HIGH, PhotoFileFormat.JFIF, false, !strip_metadata);
} catch (Error err) {
throw new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
"unable to serialize photo '%s' for publishing.", photo.get_name());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment