properties_view: Use physical size and resolution
Replace the API that got removed from glycin
This commit is contained in:
parent
ffe42a7c6c
commit
c7223f6f69
11 changed files with 254 additions and 101 deletions
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
use glycin::{Operation, Operations};
|
||||
use gtk::prelude::*;
|
||||
use gufo_common::orientation::Rotation;
|
||||
use gufo::common::orientation::Rotation;
|
||||
|
||||
use crate::deps::*;
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ impl OperationsSnapshot {
|
|||
|
||||
fn rotate(
|
||||
&mut self,
|
||||
rotation: gufo_common::orientation::Rotation,
|
||||
rotation: gufo::common::orientation::Rotation,
|
||||
) -> Result<(), OperationError> {
|
||||
let snapshot = gtk::Snapshot::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use glib::TimeZone;
|
|||
use glycin::{Frame, FrameDetails, ImageDetails, MemoryFormat};
|
||||
use gufo::common::datetime::DateTime;
|
||||
use gufo::common::orientation::Orientation;
|
||||
use gufo::common::physical_dimension;
|
||||
|
||||
use crate::deps::*;
|
||||
use crate::util;
|
||||
|
|
@ -169,16 +170,6 @@ impl Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn dimensions_inch(&self) -> Option<(f64, f64)> {
|
||||
self.image_info.as_ref().and_then(|x| x.dimensions_inch())
|
||||
}
|
||||
|
||||
pub fn dimensions_text(&self) -> Option<String> {
|
||||
self.image_info
|
||||
.as_ref()
|
||||
.and_then(|x| x.info_dimensions_text().map(|x| x.to_string()))
|
||||
}
|
||||
|
||||
pub fn file_name(&self) -> Option<String> {
|
||||
self.file_info.as_ref().map(|x| x.display_name.to_string())
|
||||
}
|
||||
|
|
@ -436,4 +427,12 @@ impl Metadata {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixel_density(&self) -> Option<physical_dimension::PixelDensity> {
|
||||
self.frame_info.as_ref().and_then(|x| x.pixel_density())
|
||||
}
|
||||
|
||||
pub fn physical_size(&self) -> Option<physical_dimension::PhysicalSize> {
|
||||
self.frame_info.as_ref().and_then(|x| x.physical_size())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
src/util.rs
27
src/util.rs
|
|
@ -267,3 +267,30 @@ pub enum ErrorType {
|
|||
Loader,
|
||||
General,
|
||||
}
|
||||
|
||||
pub fn locale_uses_inch() -> bool {
|
||||
// Use inch for USA and Liberia
|
||||
if let Some(unit_locale) = getlocale(gettextrs::LocaleCategory::LcMeasurement)
|
||||
&& let Some(locale) = unit_locale
|
||||
.split(|x| *x == b'_')
|
||||
.nth(1)
|
||||
.and_then(|x| x.get(0..2))
|
||||
&& (locale == b"US" || locale == b"LR")
|
||||
{
|
||||
dbg!(true)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Upstream to gettext if possible
|
||||
fn getlocale(category: gettextrs::LocaleCategory) -> Option<Vec<u8>> {
|
||||
unsafe {
|
||||
let ret = gettext_sys::setlocale(category as i32, std::ptr::null());
|
||||
if ret.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(std::ffi::CStr::from_ptr(ret).to_bytes().to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,14 +254,14 @@ mod imp {
|
|||
|
||||
fn apply_rotate_cw(&self) {
|
||||
self.apply_crop();
|
||||
self.add_operation(Operation::Rotate(gufo_common::orientation::Rotation::_270));
|
||||
self.add_operation(Operation::Rotate(gufo::common::orientation::Rotation::_270));
|
||||
|
||||
self.reset_selection();
|
||||
}
|
||||
|
||||
fn apply_rotate_ccw(&self) {
|
||||
self.apply_crop();
|
||||
self.add_operation(Operation::Rotate(gufo_common::orientation::Rotation::_90));
|
||||
self.add_operation(Operation::Rotate(gufo::common::orientation::Rotation::_90));
|
||||
|
||||
self.reset_selection();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ use adw::prelude::*;
|
|||
use adw::subclass::prelude::*;
|
||||
use glib::subclass::Signal;
|
||||
use glib::{Properties, SignalGroup};
|
||||
use gufo_common::orientation::Orientation;
|
||||
use gufo::common::orientation::Orientation;
|
||||
|
||||
use crate::decoder::tiling::SharedFrameBuffer;
|
||||
use crate::decoder::{self, Decoder, DecoderUpdate, tiling};
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ impl LpImage {
|
|||
zoom_animation.play();
|
||||
}
|
||||
|
||||
if let Ok(r) = gufo_common::orientation::Rotation::try_from(angle)
|
||||
&& r != gufo_common::orientation::Rotation::_0
|
||||
if let Ok(r) = gufo::common::orientation::Rotation::try_from(angle)
|
||||
&& r != gufo::common::orientation::Rotation::_0
|
||||
{
|
||||
tracing::debug!("Editing image to rotate by {r:?}");
|
||||
let operation = glycin::Operation::Rotate(r);
|
||||
|
|
|
|||
|
|
@ -201,6 +201,8 @@ impl Unit {
|
|||
}
|
||||
|
||||
mod imp {
|
||||
use crate::util;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate, Properties)]
|
||||
|
|
@ -388,13 +390,7 @@ mod imp {
|
|||
.set_value(size_unit.round(obj.original_size().0 * obj.width_unit_factor()));
|
||||
|
||||
// Default to inch for USA and Liberia
|
||||
if let Some(unit_locale) = getlocale(gettextrs::LocaleCategory::LcMeasurement)
|
||||
&& let Some(locale) = unit_locale
|
||||
.split(|x| *x == b'_')
|
||||
.nth(1)
|
||||
.and_then(|x| x.get(0..2))
|
||||
&& (locale == b"US" || locale == b"LR")
|
||||
{
|
||||
if util::locale_uses_inch() {
|
||||
imp.margin_unit.set_selected(Unit::Inch as u32);
|
||||
imp.size_unit.set_selected(Unit::Inch as u32);
|
||||
}
|
||||
|
|
@ -525,13 +521,22 @@ impl LpPrint {
|
|||
|
||||
pub fn original_size(&self) -> (f64, f64) {
|
||||
let image = self.image();
|
||||
let (x_pixels, y_pixels) = image.image_size();
|
||||
let metadata = image.metadata();
|
||||
if let Some((w, h)) = metadata.dimensions_inch() {
|
||||
|
||||
if let Some(physical_size) = metadata.physical_size().map(|x| x.dpi()).or_else(|| {
|
||||
metadata
|
||||
.pixel_density()
|
||||
.map(|x| x.physical_size(x_pixels as u32, y_pixels as u32))
|
||||
}) {
|
||||
let dpi = self.dpi();
|
||||
|
||||
((w * dpi).round(), (h * dpi).round())
|
||||
(
|
||||
(physical_size.x.value() * dpi).round(),
|
||||
(physical_size.y.value() * dpi).round(),
|
||||
)
|
||||
} else {
|
||||
let (w, h) = self.image().image_size();
|
||||
let (w, h) = image.image_size();
|
||||
|
||||
(w as f64, h as f64)
|
||||
}
|
||||
|
|
@ -968,15 +973,3 @@ impl LpPrint {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Upstream to gettext if possible
|
||||
pub fn getlocale(category: gettextrs::LocaleCategory) -> Option<Vec<u8>> {
|
||||
unsafe {
|
||||
let ret = gettext_sys::setlocale(category as i32, std::ptr::null());
|
||||
if ret.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(std::ffi::CStr::from_ptr(ret).to_bytes().to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ use crate::widgets::image::LpImage;
|
|||
const FALLBACK: &str = "–";
|
||||
|
||||
mod imp {
|
||||
use gufo::common::physical_dimension::PhysicalDimensionUnit;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate, Properties)]
|
||||
|
|
@ -54,6 +56,10 @@ mod imp {
|
|||
#[template_child]
|
||||
image_size: TemplateChild<adw::ActionRow>,
|
||||
#[template_child]
|
||||
physical_size: TemplateChild<adw::ActionRow>,
|
||||
#[template_child]
|
||||
pixel_density: TemplateChild<adw::ActionRow>,
|
||||
#[template_child]
|
||||
image_format: TemplateChild<adw::ActionRow>,
|
||||
#[template_child]
|
||||
file_size: TemplateChild<adw::ActionRow>,
|
||||
|
|
@ -219,6 +225,8 @@ mod imp {
|
|||
|
||||
// Image info
|
||||
Self::update_row(&self.image_size, self.image_size());
|
||||
Self::update_row(&self.physical_size, self.physical_size());
|
||||
Self::update_row(&self.pixel_density, self.pixel_density());
|
||||
Self::update_row(&self.image_format, self.format_name());
|
||||
Self::update_row(&self.file_size, metadata.file_size());
|
||||
|
||||
|
|
@ -289,30 +297,113 @@ mod imp {
|
|||
|
||||
fn image_size(&self) -> Option<String> {
|
||||
let obj = self.obj();
|
||||
let image = obj.image()?;
|
||||
let physical_size = image.metadata().physical_size();
|
||||
|
||||
if let Some(image) = obj.image() {
|
||||
match image.metadata().dimensions_text() {
|
||||
Some(string) => Some(string.to_string()),
|
||||
_ => {
|
||||
let (width, height) = image.image_size();
|
||||
if width > 0 && height > 0 {
|
||||
Some(
|
||||
// Translators: Image "<width> x <height>"
|
||||
gettext_f(
|
||||
r"{}\u{202F}\u{D7}\u{202F}{}",
|
||||
[width.to_string(), height.to_string()],
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
if physical_size.is_none() {
|
||||
let (width, height) = image.image_size();
|
||||
if width > 0 && height > 0 {
|
||||
Some(
|
||||
// Translators: Image "<width> x <height>"
|
||||
gettext_f(
|
||||
r"{}\u{202F}\u{D7}\u{202F}{}",
|
||||
[width.to_string(), height.to_string()],
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> Option<String> {
|
||||
let obj = self.obj();
|
||||
let image = obj.image()?;
|
||||
let (width, height) = image.image_size();
|
||||
let metadata = image.metadata();
|
||||
|
||||
let default_unit = if util::locale_uses_inch() {
|
||||
PhysicalDimensionUnit::Inch
|
||||
} else {
|
||||
PhysicalDimensionUnit::Centimeter
|
||||
};
|
||||
|
||||
let physical_size = metadata.physical_size().or_else(|| {
|
||||
metadata.pixel_density().map(|x| {
|
||||
x.physical_size(width as u32, height as u32)
|
||||
.convert(default_unit)
|
||||
})
|
||||
})?;
|
||||
|
||||
const ROUND: f64 = 100.;
|
||||
let x = (physical_size.x.value() * ROUND).round() / ROUND;
|
||||
let y = (physical_size.y.value() * ROUND).round() / ROUND;
|
||||
|
||||
let unit = |x| {
|
||||
match x {
|
||||
PhysicalDimensionUnit::Centimeter => {
|
||||
// Translators: Shorthand for centimeter
|
||||
gettext("\u{202F}cm")
|
||||
}
|
||||
PhysicalDimensionUnit::Inch => {
|
||||
// Translators: Shorthand for inch
|
||||
gettext("\u{2033}")
|
||||
}
|
||||
PhysicalDimensionUnit::Meter => {
|
||||
// Translators: Shorthand for meter
|
||||
gettext("\u{202F}m")
|
||||
}
|
||||
PhysicalDimensionUnit::Millimeter => {
|
||||
// Translators: Shorthand for millimeter
|
||||
gettext("\u{202F}mm")
|
||||
}
|
||||
PhysicalDimensionUnit::Pica => {
|
||||
// Translators: Shorthand for pica (unit)
|
||||
gettext("\u{202F}pc")
|
||||
}
|
||||
PhysicalDimensionUnit::Point => {
|
||||
// Translators: Shorthand for pica (unit)
|
||||
gettext("\u{202F}pt")
|
||||
}
|
||||
x => x.shorthand().to_string(),
|
||||
}
|
||||
};
|
||||
|
||||
let x_unit = unit(physical_size.x.unit());
|
||||
let y_unit = unit(physical_size.y.unit());
|
||||
|
||||
Some(
|
||||
// Translators: Image "<width> x <height>"
|
||||
gettext_f(
|
||||
r"{}{}\u{202F}\u{D7}\u{202F}{}{}",
|
||||
[x.to_string(), x_unit, y.to_string(), y_unit],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn pixel_density(&self) -> Option<String> {
|
||||
let obj = self.obj();
|
||||
let image = obj.image()?;
|
||||
let metadata = image.metadata();
|
||||
let pixel_density = metadata.pixel_density()?.dpi();
|
||||
|
||||
const ROUND: f64 = 10.;
|
||||
let x = (pixel_density.x().value() * ROUND).round() / ROUND;
|
||||
let y = (pixel_density.y().value() * ROUND).round() / ROUND;
|
||||
|
||||
Some(if x == y {
|
||||
gettext_f(r"{}\u{202F}DPI", [x.to_string()])
|
||||
} else {
|
||||
// Translators: Image "<x-resolution> x <y-resolution>"
|
||||
gettext_f(
|
||||
r"{}\u{202F}DPI\u{202F}\u{D7}\u{202F}{}\u{202F}DPI",
|
||||
[x.to_string(), y.to_string()],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn file(&self) -> Option<gio::File> {
|
||||
self.image.borrow().as_ref().and_then(|x| x.file())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,26 @@
|
|||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow" id="physical_size">
|
||||
<property name="title" translatable="yes">Physical Size</property>
|
||||
<property name="subtitle-selectable">True</property>
|
||||
<property name="use-markup">False</property>
|
||||
<style>
|
||||
<class name="property" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow" id="pixel_density">
|
||||
<property name="title" translatable="yes">Resolution</property>
|
||||
<property name="subtitle-selectable">True</property>
|
||||
<property name="use-markup">False</property>
|
||||
<style>
|
||||
<class name="property" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow" id="image_format">
|
||||
<property name="title" translatable="yes">Image Format</property>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue