From 5f82ab51d5a756d4f32e935da23d188d8e1eaa83 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Fri, 12 Mar 2021 11:13:56 +0100 Subject: [PATCH 01/17] Update the firmware patch --- contrib/firmware.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/firmware.patch b/contrib/firmware.patch index 87fbd8d1..15b7d3bd 100644 --- a/contrib/firmware.patch +++ b/contrib/firmware.patch @@ -1,6 +1,6 @@ diff -ru a/etc/init.d/rcS b/etc/init.d/rcS ---- a/etc/init.d/rcS 2020-11-15 11:47:09.000000000 +0100 -+++ b/etc/init.d/rcS 2020-11-15 11:47:01.000000000 +0100 +--- a/etc/init.d/rcS 2021-03-12 11:12:44.000000000 +0100 ++++ b/etc/init.d/rcS 2021-03-12 11:12:59.000000000 +0100 @@ -78,7 +78,12 @@ FS_CORRUPT=0 dosfsck -a -w /dev/mmcblk0p3 || dosfsck -a -w /dev/mmcblk0p3 || dosfsck -a -w /dev/mmcblk0p3 || dosfsck -a -w /dev/mmcblk0p3 || FS_CORRUPT=1 @@ -15,7 +15,7 @@ diff -ru a/etc/init.d/rcS b/etc/init.d/rcS if [ $? != 0 ] || [ $FS_CORRUPT == 1 ] || [ $FORCE_FACTORY_RESET == 1 ]; then case $PRODUCT in kraken|phoenix) -@@ -196,7 +201,12 @@ +@@ -198,7 +203,12 @@ /bin/dbus-daemon --system & export DBUS_SESSION_BUS_ADDRESS=`/bin/dbus-daemon --session --print-address --fork` From 44181cdc9295a127693ca18679df4f11c0b83c77 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Wed, 7 Apr 2021 10:15:06 +0200 Subject: [PATCH 02/17] Intermissions: add support for book covers --- src/app.rs | 17 +++----- src/emulator.rs | 90 +++++++++++++++++++++++++++------------- src/settings/mod.rs | 55 +++++++++++++++++++++--- src/view/home/mod.rs | 19 +-------- src/view/intermission.rs | 84 +++++++++++++++++-------------------- src/view/mod.rs | 2 - src/view/reader/mod.rs | 3 +- 7 files changed, 157 insertions(+), 113 deletions(-) diff --git a/src/app.rs b/src/app.rs index a806ad9a..d8c9ad51 100644 --- a/src/app.rs +++ b/src/app.rs @@ -32,7 +32,7 @@ use crate::input::{DeviceEvent, PowerSource, ButtonCode, ButtonStatus, VAL_RELEA use crate::input::{raw_events, device_events, usb_events, display_rotate_event, button_scheme_event}; use crate::gesture::{GestureEvent, gesture_events}; use crate::helpers::{load_json, load_toml, save_toml, IsHidden}; -use crate::settings::{ButtonScheme, Settings, SETTINGS_PATH, RotationLock}; +use crate::settings::{ButtonScheme, Settings, SETTINGS_PATH, RotationLock, IntermKind}; use crate::frontlight::{Frontlight, StandardFrontlight, NaturalFrontlight, PremixedFrontlight}; use crate::lightsensor::{LightSensor, KoboLightSensor}; use crate::battery::{Battery, KoboBattery}; @@ -40,7 +40,7 @@ use crate::geom::{Rectangle, DiagDir, Region}; use crate::view::home::Home; use crate::view::reader::Reader; use crate::view::dialog::Dialog; -use crate::view::intermission::{Intermission, IntermKind}; +use crate::view::intermission::Intermission; use crate::view::notification::Notification; use crate::device::{CURRENT_DEVICE, Orientation, FrontlightKind}; use crate::library::Library; @@ -446,6 +446,7 @@ pub fn run() -> Result<(), Error> { } else if tasks.iter().any(|task| task.id == TaskId::Suspend) { resume(TaskId::Suspend, &mut tasks, view.as_mut(), &tx, &mut rq, &mut context); } else { + view.handle_event(&Event::Suspend, &tx, &mut bus, &mut rq, &mut context); let interm = Intermission::new(context.fb.rect(), IntermKind::Suspend, &context); rq.add(RenderData::new(interm.id(), *interm.rect(), UpdateMode::Full)); schedule_task(TaskId::PrepareSuspend, Event::PrepareSuspend, @@ -465,6 +466,7 @@ pub fn run() -> Result<(), Error> { continue; } + view.handle_event(&Event::Suspend, &tx, &mut bus, &mut rq, &mut context); let interm = Intermission::new(context.fb.rect(), IntermKind::Suspend, &context); rq.add(RenderData::new(interm.id(), *interm.rect(), UpdateMode::Full)); schedule_task(TaskId::PrepareSuspend, Event::PrepareSuspend, @@ -754,6 +756,7 @@ pub fn run() -> Result<(), Error> { .ok(); context.online = false; } + let interm = Intermission::new(context.fb.rect(), IntermKind::Share, &context); rq.add(RenderData::new(interm.id(), *interm.rect(), UpdateMode::Full)); view.children_mut().push(Box::new(interm) as Box); @@ -980,15 +983,6 @@ pub fn run() -> Result<(), Error> { context.fb.toggle_dithered(); rq.add(RenderData::new(view.id(), context.fb.rect(), UpdateMode::Full)); }, - Event::Select(EntryId::ToggleIntermissionImage(ref kind, ref path)) => { - let full_path = context.library.home.join(path); - let key = kind.key(); - if context.settings.intermission_images.get(key) == Some(&full_path) { - context.settings.intermission_images.remove(key); - } else { - context.settings.intermission_images.insert(key.to_string(), full_path); - } - }, Event::Select(EntryId::Rotate(n)) if n != context.display.rotation && view.might_rotate() => { updating.retain(|tok, _| context.fb.wait(*tok).is_err()); if let Ok(dims) = context.fb.set_rotation(n) { @@ -1070,6 +1064,7 @@ pub fn run() -> Result<(), Error> { } let seconds = 60 * context.settings.auto_suspend as u64; if inactive_since.elapsed() > Duration::from_secs(seconds) { + view.handle_event(&Event::Suspend, &tx, &mut bus, &mut rq, &mut context); let interm = Intermission::new(context.fb.rect(), IntermKind::Suspend, &context); rq.add(RenderData::new(interm.id(), *interm.rect(), UpdateMode::Full)); schedule_task(TaskId::PrepareSuspend, Event::PrepareSuspend, diff --git a/src/emulator.rs b/src/emulator.rs index 4c561fde..6cd09925 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -31,7 +31,7 @@ use anyhow::{Error, Context as ResultExt}; use fxhash::FxHashMap; use chrono::Local; use sdl2::event::Event as SdlEvent; -use sdl2::keyboard::{Scancode, Keycode}; +use sdl2::keyboard::{Scancode, Keycode, Mod}; use sdl2::render::{WindowCanvas, BlendMode}; use sdl2::pixels::{Color as SdlColor, PixelFormatEnum}; use sdl2::mouse::MouseState; @@ -48,6 +48,7 @@ use crate::view::notification::Notification; use crate::view::dialog::Dialog; use crate::view::frontlight::FrontlightWindow; use crate::view::menu::{Menu, MenuKind}; +use crate::view::intermission::Intermission; use crate::view::dictionary::Dictionary; use crate::view::calculator::Calculator; use crate::view::sketch::Sketch; @@ -56,7 +57,7 @@ use crate::view::rotation_values::RotationValues; use crate::view::common::{locate, locate_by_id, transfer_notifications, overlapping_rectangle}; use crate::view::common::{toggle_input_history_menu, toggle_keyboard_layout_menu}; use crate::helpers::{load_toml, save_toml}; -use crate::settings::{Settings, SETTINGS_PATH}; +use crate::settings::{Settings, SETTINGS_PATH, IntermKind}; use crate::geom::{Rectangle, Axis}; use crate::gesture::{GestureEvent, gesture_events}; use crate::device::CURRENT_DEVICE; @@ -91,6 +92,17 @@ fn seconds(timestamp: u32) -> f64 { timestamp as f64 / 1000.0 } +impl IntermKind { + pub fn from_scancode(sc: Scancode) -> Option { + match sc { + Scancode::S => Some(IntermKind::Suspend), + Scancode::P => Some(IntermKind::PowerOff), + Scancode::C => Some(IntermKind::Share), + _ => None, + } + } +} + #[inline] pub fn device_event(event: SdlEvent) -> Option { match event { @@ -287,39 +299,61 @@ fn main() -> Result<(), Error> { if let Some(sdl_evt) = event_pump.wait_event_timeout(20) { match sdl_evt { SdlEvent::Quit { .. } | - SdlEvent::KeyDown { keycode: Some(Keycode::Escape), .. } => { + SdlEvent::KeyDown { keycode: Some(Keycode::Escape), keymod: Mod::NOMOD, .. } => { view.handle_event(&Event::Back, &tx, &mut VecDeque::new(), &mut RenderQueue::new(), &mut context); while let Some(mut view) = history.pop() { view.handle_event(&Event::Back, &tx, &mut VecDeque::new(), &mut RenderQueue::new(), &mut context); } break; }, - SdlEvent::KeyDown { scancode: Some(scancode), .. } => { - match scancode { - Scancode::LeftBracket => { - let rot = (3 + context.display.rotation) % 4; - ty.send(DeviceEvent::RotateScreen(rot)).ok(); - }, - Scancode::RightBracket => { - let rot = (5 + context.display.rotation) % 4; - ty.send(DeviceEvent::RotateScreen(rot)).ok(); - }, - Scancode::S => { - tx.send(Event::Select(EntryId::TakeScreenshot)).ok(); + SdlEvent::KeyDown { scancode: Some(scancode), keymod, .. } => { + match keymod { + Mod::NOMOD => { + match scancode { + Scancode::LeftBracket => { + let rot = (3 + context.display.rotation) % 4; + ty.send(DeviceEvent::RotateScreen(rot)).ok(); + }, + Scancode::RightBracket => { + let rot = (5 + context.display.rotation) % 4; + ty.send(DeviceEvent::RotateScreen(rot)).ok(); + }, + Scancode::S => { + tx.send(Event::Select(EntryId::TakeScreenshot)).ok(); + }, + Scancode::I | Scancode::O => { + let mouse_state = MouseState::new(&event_pump); + let x = mouse_state.x() as i32; + let y = mouse_state.y() as i32; + let center = pt!(x, y); + if scancode == Scancode::I { + tx.send(Event::Gesture(GestureEvent::Spread { center, + factor: 2.0, + axis: Axis::Diagonal })).ok(); + } else { + tx.send(Event::Gesture(GestureEvent::Pinch { center, + factor: 0.5, + axis: Axis::Diagonal })).ok(); + } + }, + _ => (), + } }, - Scancode::I | Scancode::O => { - let mouse_state = MouseState::new(&event_pump); - let x = mouse_state.x() as i32; - let y = mouse_state.y() as i32; - let center = pt!(x, y); - if scancode == Scancode::I { - tx.send(Event::Gesture(GestureEvent::Spread { center, - factor: 2.0, - axis: Axis::Diagonal })).ok(); - } else { - tx.send(Event::Gesture(GestureEvent::Pinch { center, - factor: 0.5, - axis: Axis::Diagonal })).ok(); + Mod::LSHIFTMOD | Mod::RSHIFTMOD => { + match scancode { + Scancode::S | Scancode::P | Scancode::C => { + if let Some(index) = locate::(view.as_ref()) { + let rect = *view.child(index).rect(); + view.children_mut().remove(index); + rq.add(RenderData::expose(rect, UpdateMode::Full)); + } else if let Some(kind) = IntermKind::from_scancode(scancode) { + view.handle_event(&Event::Suspend, &tx, &mut VecDeque::new(), &mut RenderQueue::new(), &mut context); + let interm = Intermission::new(context.fb.rect(), kind, &context); + rq.add(RenderData::new(interm.id(), *interm.rect(), UpdateMode::Full)); + view.children_mut().push(Box::new(interm) as Box); + } + }, + _ => (), } }, _ => (), diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 63858c38..f1d94897 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -1,10 +1,11 @@ mod preset; use std::env; +use std::ops::Index; use std::fmt::{self, Debug}; use std::path::PathBuf; use std::collections::BTreeMap; -use fxhash::{FxHashMap, FxHashSet}; +use fxhash::FxHashSet; use serde::{Serialize, Deserialize}; use crate::metadata::{SortMethod, TextAlign}; use crate::frontlight::LightLevels; @@ -18,6 +19,8 @@ pub const SETTINGS_PATH: &str = "Settings.toml"; pub const DEFAULT_FONT_PATH: &str = "/mnt/onboard/fonts"; pub const INTERNAL_CARD_ROOT: &str = "/mnt/onboard"; pub const EXTERNAL_CARD_ROOT: &str = "/mnt/sd"; +pub const LOGO_SPECIAL_PATH: &str = "logo:"; +pub const COVER_SPECIAL_PATH: &str = "cover:"; // Default font size in points. pub const DEFAULT_FONT_SIZE: f32 = 11.0; // Default margin width in millimeters. @@ -50,6 +53,44 @@ impl fmt::Display for ButtonScheme { } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum IntermKind { + Suspend, + PowerOff, + Share, +} + +impl IntermKind { + pub fn text(&self) -> &str { + match self { + IntermKind::Suspend => "Sleeping", + IntermKind::PowerOff => "Powered off", + IntermKind::Share => "Shared", + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Intermissions { + suspend: PathBuf, + power_off: PathBuf, + share: PathBuf, +} + +impl Index for Intermissions { + type Output = PathBuf; + + fn index(&self, key: IntermKind) -> &Self::Output { + match key { + IntermKind::Suspend => &self.suspend, + IntermKind::PowerOff => &self.power_off, + IntermKind::Share => &self.share, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, rename_all = "kebab-case")] pub struct Settings { @@ -68,8 +109,7 @@ pub struct Settings { pub date_format: String, #[serde(skip_serializing_if = "Vec::is_empty")] pub libraries: Vec, - #[serde(skip_serializing_if = "FxHashMap::is_empty")] - pub intermission_images: FxHashMap, + pub intermissions: Intermissions, #[serde(skip_serializing_if = "Vec::is_empty")] pub frontlight_presets: Vec, pub home: HomeSettings, @@ -349,8 +389,7 @@ impl Default for ImportSettings { startup_trigger: true, extract_epub_metadata: true, allowed_kinds: ["pdf", "djvu", "epub", "fb2", - "xps", "oxps", "html", "htm", - "cbz", "png", "jpg", "jpeg"].iter().map(|k| k.to_string()).collect(), + "xps", "oxps", "cbz"].iter().map(|k| k.to_string()).collect(), } } } @@ -400,7 +439,11 @@ impl Default for Settings { auto_power_off: 3, time_format: "%H:%M".to_string(), date_format: "%A, %B %-d, %Y".to_string(), - intermission_images: FxHashMap::default(), + intermissions: Intermissions { + suspend: PathBuf::from(LOGO_SPECIAL_PATH), + power_off: PathBuf::from(LOGO_SPECIAL_PATH), + share: PathBuf::from(LOGO_SPECIAL_PATH), + }, home: HomeSettings::default(), reader: ReaderSettings::default(), import: ImportSettings::default(), diff --git a/src/view/home/mod.rs b/src/view/home/mod.rs index 992672d0..054be251 100644 --- a/src/view/home/mod.rs +++ b/src/view/home/mod.rs @@ -34,7 +34,6 @@ use crate::view::menu::{Menu, MenuKind}; use crate::view::menu_entry::MenuEntry; use crate::view::search_bar::SearchBar; use crate::view::notification::Notification; -use crate::view::intermission::IntermKind; use super::top_bar::TopBar; use self::address_bar::AddressBar; use self::navigation_bar::NavigationBar; @@ -865,7 +864,6 @@ impl Home { let book_index = self.book_index(index); let info = &self.visible_books[book_index]; let path = &info.file.path; - let full_path = context.library.home.join(path); let mut entries = Vec::new(); @@ -893,22 +891,8 @@ impl Home { EntryId::SetStatus(path.clone(), *s))) .collect(); entries.push(EntryKind::SubMenu("Mark As".to_string(), submenu)); - - { - let images = &context.settings.intermission_images; - let submenu = [IntermKind::Suspend, - IntermKind::PowerOff, - IntermKind::Share].iter().map(|k| { - EntryKind::CheckBox(k.label().to_string(), - EntryId::ToggleIntermissionImage(*k, path.clone()), - images.get(k.key()) == Some(&full_path)) - }).collect::>(); - - - entries.push(EntryKind::SubMenu("Set As".to_string(), submenu)) - } - entries.push(EntryKind::Separator); + let selected_library = context.settings.selected_library; let libraries = context.settings.libraries.iter().enumerate() .filter(|(index, _)| *index != selected_library) @@ -1067,7 +1051,6 @@ impl Home { } let mut trash = Library::new(trash_path, LibraryMode::Database); context.library.move_to(path, &mut trash)?; - context.settings.intermission_images.retain(|_, path| path != &full_path); let (mut files, _) = trash.list(&trash.home, None, false); let mut size = files.iter().map(|info| info.file.size).sum::(); if size > context.settings.home.max_trash_size { diff --git a/src/view/intermission.rs b/src/view/intermission.rs index 5a544002..a61b7f14 100644 --- a/src/view/intermission.rs +++ b/src/view/intermission.rs @@ -1,10 +1,12 @@ use std::path::PathBuf; use crate::device::CURRENT_DEVICE; -use crate::document::pdf::PdfOpener; +use crate::document::{Location, open}; use crate::geom::Rectangle; use crate::font::{Fonts, font_from_style, DISPLAY_STYLE}; use super::{View, Event, Hub, Bus, Id, ID_FEEDER, RenderQueue}; use crate::framebuffer::Framebuffer; +use crate::settings::{IntermKind, LOGO_SPECIAL_PATH, COVER_SPECIAL_PATH}; +use crate::metadata::{SortMethod, BookQuery, sort}; use crate::color::{TEXT_NORMAL, TEXT_INVERTED_HARD}; use crate::app::Context; @@ -19,48 +21,28 @@ pub struct Intermission { pub enum Message { Text(String), Image(PathBuf), + Cover(PathBuf), } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum IntermKind { - Suspend, - PowerOff, - Share, -} - -impl IntermKind { - pub fn text(&self) -> &str { - match self { - IntermKind::Suspend => "Sleeping", - IntermKind::PowerOff => "Powered off", - IntermKind::Share => "Shared", - } - } - - pub fn label(&self) -> &str { - match self { - IntermKind::Suspend => "Suspend Image", - IntermKind::PowerOff => "Power Off Image", - IntermKind::Share => "Share Image", - } - } - - pub fn key(&self) -> &str { - match self { - IntermKind::Suspend => "suspend", - IntermKind::PowerOff => "power-off", - IntermKind::Share => "share", - } - } -} - - impl Intermission { pub fn new(rect: Rectangle, kind: IntermKind, context: &Context) -> Intermission { - let message = if let Some(path) = context.settings.intermission_images.get(kind.key()) { - Message::Image(path.clone()) - } else { - Message::Text(kind.text().to_string()) + let path = &context.settings.intermissions[kind]; + let message = match path.to_str() { + Some(LOGO_SPECIAL_PATH) => Message::Text(kind.text().to_string()), + Some(COVER_SPECIAL_PATH) => { + let query = BookQuery { + reading: Some(true), + .. Default::default() + }; + let (mut files, _) = context.library.list(&context.library.home, Some(&query), false); + sort(&mut files, SortMethod::Opened, true); + if !files.is_empty() { + Message::Cover(context.library.home.join(&files[0].file.path)) + } else { + Message::Text(kind.text().to_string()) + } + }, + _ => Message::Image(path.clone()), }; Intermission { id: ID_FEEDER.next(), @@ -109,11 +91,10 @@ impl View for Intermission { font.render(fb, scheme[1], &plan, pt!(dx, dy)); - let doc = PdfOpener::new().and_then(|o| o.open("icons/dodecahedron.svg")).unwrap(); - let page = doc.page(0).unwrap(); - let (width, height) = page.dims(); + let mut doc = open("icons/dodecahedron.svg").unwrap(); + let (width, height) = doc.dims(0).unwrap(); let scale = (plan.width as f32 / width.max(height) as f32) / 4.0; - let pixmap = page.pixmap(scale).unwrap(); + let (pixmap, _) = doc.pixmap(Location::Exact(0), scale).unwrap(); let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2; let dy = dy + 2 * x_height; let pt = self.rect.min + pt!(dx, dy); @@ -121,13 +102,12 @@ impl View for Intermission { fb.draw_blended_pixmap(&pixmap, pt, scheme[1]); }, Message::Image(ref path) => { - if let Some(doc) = PdfOpener::new().and_then(|o| o.open(path)) { - if let Some(page) = doc.page(0) { - let (width, height) = page.dims(); + if let Some(mut doc) = open(path) { + if let Some((width, height)) = doc.dims(0) { let w_ratio = self.rect.width() as f32 / width; let h_ratio = self.rect.height() as f32 / height; let scale = w_ratio.min(h_ratio); - if let Some(pixmap) = page.pixmap(scale) { + if let Some((pixmap, _)) = doc.pixmap(Location::Exact(0), scale) { let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2; let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2; let pt = self.rect.min + pt!(dx, dy); @@ -136,6 +116,16 @@ impl View for Intermission { } } }, + Message::Cover(ref path) => { + if let Some(mut doc) = open(path) { + if let Some(pixmap) = doc.preview_pixmap(self.rect.width() as f32, self.rect.height() as f32) { + let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2; + let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2; + let pt = self.rect.min + pt!(dx, dy); + fb.draw_pixmap(&pixmap, pt); + } + } + }, } } diff --git a/src/view/mod.rs b/src/view/mod.rs index 9bd5a467..097fab99 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -62,7 +62,6 @@ use crate::input::{DeviceEvent, FingerStatus}; use crate::gesture::GestureEvent; use self::calculator::LineOrigin; use self::key::KeyKind; -use self::intermission::IntermKind; use crate::app::Context; // Border thicknesses in pixels, at 300 DPI. @@ -503,7 +502,6 @@ pub enum EntryId { ToggleSelectDirectory(PathBuf), SetStatus(PathBuf, SimpleStatus), SearchAuthor(String), - ToggleIntermissionImage(IntermKind, PathBuf), RemovePreset(usize), FirstColumn(FirstColumn), SecondColumn(SecondColumn), diff --git a/src/view/reader/mod.rs b/src/view/reader/mod.rs index 19e31080..d257e91e 100644 --- a/src/view/reader/mod.rs +++ b/src/view/reader/mod.rs @@ -3645,7 +3645,8 @@ impl View for Reader { Event::Select(EntryId::Quit) | Event::Select(EntryId::Reboot) | Event::Select(EntryId::RebootInNickel) | - Event::Back => { + Event::Back | + Event::Suspend => { self.quit(context); false }, From fb45bb0a761424eb022b161af31bff8591c5993f Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Mon, 19 Apr 2021 10:12:45 +0200 Subject: [PATCH 03/17] Add support for copying documents --- src/library.rs | 118 +++++++++++++++++++++++++++++++++++++++---- src/view/home/mod.rs | 32 +++++++++--- src/view/mod.rs | 1 + 3 files changed, 134 insertions(+), 17 deletions(-) diff --git a/src/library.rs b/src/library.rs index efe2e50d..4a5e31e1 100644 --- a/src/library.rs +++ b/src/library.rs @@ -3,11 +3,12 @@ use std::str::FromStr; use std::time::{SystemTime, Duration}; use std::path::{PathBuf, Path}; use std::collections::BTreeSet; +use std::io::{Error as IoError, ErrorKind}; use walkdir::WalkDir; use indexmap::IndexMap; use fxhash::{FxHashMap, FxHashSet, FxBuildHasher}; use chrono::{Local, TimeZone}; -use filetime::{FileTime, set_file_handle_times}; +use filetime::{FileTime, set_file_mtime, set_file_handle_times}; use anyhow::{Error, format_err}; use crate::metadata::{Info, ReaderInfo, FileInfo, BookQuery, SimpleStatus, SortMethod}; use crate::metadata::{sort, sorter, extract_metadata_from_epub}; @@ -40,7 +41,9 @@ impl Library { let path = home.as_ref().join(METADATA_FILENAME); match load_json(&path) { Err(e) => { - eprintln!("Can't load database: {:#}.", e); + if e.downcast_ref::().map(|e| e.kind()) != Some(ErrorKind::NotFound) { + eprintln!("Can't load database: {:#}.", e); + } IndexMap::with_capacity_and_hasher(0, FxBuildHasher::default()) }, Ok(v) => v, @@ -410,16 +413,93 @@ impl Library { Ok(()) } + pub fn copy_to>(&mut self, path: P, other: &mut Library) -> Result<(), Error> { + let src = self.home.join(path.as_ref()); + + if !src.exists() { + return Err(format_err!("can't copy non-existing file {}", path.as_ref().display())); + } + + let md = src.metadata()?; + let fp = self.paths.get(path.as_ref()).cloned() + .or_else(|| md.fingerprint(self.fat32_epoch).ok()) + .ok_or_else(|| format_err!("can't get fingerprint of {}", path.as_ref().display()))?; + + let mut dest = other.home.join(path.as_ref()); + if let Some(parent) = dest.parent() { + fs::create_dir_all(parent)?; + } + + if dest.exists() { + let prefix = Local::now().format("%Y%m%d_%H%M%S "); + let name = dest.file_name().and_then(|name| name.to_str()) + .map(|name| prefix.to_string() + name) + .ok_or_else(|| format_err!("can't compute new name for {}", dest.display()))?; + dest.set_file_name(name); + } + + fs::copy(&src, &dest)?; + let mtime = FileTime::from_last_modification_time(&md); + set_file_mtime(&dest, mtime)?; + + let rsp_src = self.reading_state_path(fp); + if rsp_src.exists() { + let rsp_dest = other.reading_state_path(fp); + fs::copy(&rsp_src, &rsp_dest)?; + } + + let tpp_src = self.thumbnail_preview_path(fp); + if tpp_src.exists() { + let tpp_dest = other.thumbnail_preview_path(fp); + fs::copy(&tpp_src, &tpp_dest)?; + } + + if other.mode == LibraryMode::Database { + let info = self.db.get(&fp).cloned() + .or_else(|| + self.reading_states.get(&fp).cloned() + .map(|reader_info| Info { + file: FileInfo { + size: md.len(), + kind: file_kind(&dest).unwrap_or_default(), + .. Default::default() + }, + reader: Some(reader_info), + .. Default::default() + }) + ); + if let Some(mut info) = info { + let dest_path = dest.strip_prefix(&other.home)?; + info.file.path = dest_path.to_path_buf(); + other.db.insert(fp, info); + other.paths.insert(dest_path.to_path_buf(), fp); + other.has_db_changed = true; + } + } else { + let reader_info = self.reading_states.get(&fp).cloned() + .or_else(|| self.db.get(&fp).cloned() + .and_then(|info| info.reader)); + if let Some(reader_info) = reader_info { + other.reading_states.insert(fp, reader_info); + } + } + + other.modified_reading_states.insert(fp); + + Ok(()) + } + pub fn move_to>(&mut self, path: P, other: &mut Library) -> Result<(), Error> { - if !self.home.join(path.as_ref()).exists() { + let src = self.home.join(path.as_ref()); + + if !src.exists() { return Err(format_err!("can't move non-existing file {}", path.as_ref().display())); } - let fp = self.paths.get(path.as_ref()).cloned().or_else(|| { - self.home.join(path.as_ref()) - .metadata().ok() - .and_then(|md| md.fingerprint(self.fat32_epoch).ok()) - }).ok_or_else(|| format_err!("can't get fingerprint of {}", path.as_ref().display()))?; + let md = src.metadata()?; + let fp = self.paths.get(path.as_ref()).cloned() + .or_else(|| md.fingerprint(self.fat32_epoch).ok()) + .ok_or_else(|| format_err!("can't get fingerprint of {}", path.as_ref().display()))?; let src = self.home.join(path.as_ref()); let mut dest = other.home.join(path.as_ref()); @@ -449,8 +529,21 @@ impl Library { fs::rename(&tpp_src, &tpp_dest)?; } - if self.mode == LibraryMode::Database { - if let Some(mut info) = self.db.shift_remove(&fp) { + if other.mode == LibraryMode::Database { + let info = self.db.shift_remove(&fp) + .or_else(|| + self.reading_states.remove(&fp) + .map(|reader_info| Info { + file: FileInfo { + size: md.len(), + kind: file_kind(&dest).unwrap_or_default(), + .. Default::default() + }, + reader: Some(reader_info), + .. Default::default() + }) + ); + if let Some(mut info) = info { let dest_path = dest.strip_prefix(&other.home)?; info.file.path = dest_path.to_path_buf(); other.db.insert(fp, info); @@ -460,7 +553,10 @@ impl Library { other.has_db_changed = true; } } else { - if let Some(reader_info) = self.reading_states.remove(&fp) { + let reader_info = self.reading_states.remove(&fp) + .or_else(|| self.db.shift_remove(&fp) + .and_then(|info| info.reader)); + if let Some(reader_info) = reader_info { other.reading_states.insert(fp, reader_info); } } diff --git a/src/view/home/mod.rs b/src/view/home/mod.rs index 054be251..b6e8c635 100644 --- a/src/view/home/mod.rs +++ b/src/view/home/mod.rs @@ -896,13 +896,19 @@ impl Home { let selected_library = context.settings.selected_library; let libraries = context.settings.libraries.iter().enumerate() .filter(|(index, _)| *index != selected_library) - .map(|(index, lib)| { - EntryKind::Command(lib.name.clone(), - EntryId::MoveTo(path.clone(), index)) - }).collect::>(); + .map(|(index, lib)| (index, lib.name.clone())) + .collect::>(); if !libraries.is_empty() { - entries.push(EntryKind::SubMenu("Move To".to_string(), libraries)); - + let copy_to = libraries.iter().map(|(index, name)| { + EntryKind::Command(name.clone(), + EntryId::CopyTo(path.clone(), *index)) + }).collect::>(); + let move_to = libraries.iter().map(|(index, name)| { + EntryKind::Command(name.clone(), + EntryId::MoveTo(path.clone(), *index)) + }).collect::>(); + entries.push(EntryKind::SubMenu("Copy To".to_string(), copy_to)); + entries.push(EntryKind::SubMenu("Move To".to_string(), move_to)); } entries.push(EntryKind::Command("Remove".to_string(), @@ -1072,6 +1078,14 @@ impl Home { Ok(()) } + fn copy_to(&mut self, path: &Path, index: usize, context: &mut Context) -> Result<(), Error> { + let library_settings = &context.settings.libraries[index]; + let mut library = Library::new(&library_settings.path, library_settings.mode); + context.library.copy_to(path, &mut library)?; + library.flush(); + Ok(()) + } + fn move_to(&mut self, path: &Path, index: usize, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) -> Result<(), Error> { let library_settings = &context.settings.libraries[index]; let mut library = Library::new(&library_settings.path, library_settings.mode); @@ -1553,6 +1567,12 @@ impl View for Home { .ok(); true }, + Event::Select(EntryId::CopyTo(ref path, index)) => { + self.copy_to(path, index, context) + .map_err(|e| eprintln!("Can't copy document: {:#}.", e)) + .ok(); + true + }, Event::Select(EntryId::MoveTo(ref path, index)) => { self.move_to(path, index, hub, rq, context) .map_err(|e| eprintln!("Can't move document: {:#}.", e)) diff --git a/src/view/mod.rs b/src/view/mod.rs index 097fab99..f021d4c9 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -496,6 +496,7 @@ pub enum EntryId { ReverseOrder, EmptyTrash, Remove(PathBuf), + CopyTo(PathBuf, usize), MoveTo(PathBuf, usize), AddDirectory(PathBuf), SelectDirectory(PathBuf), From 6a4e0e5cc68ffdd6f528e04a493a7e3a1733e846 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Mon, 19 Apr 2021 12:13:45 +0200 Subject: [PATCH 04/17] Add support for renaming documents --- src/library.rs | 24 +++++++++++++++++++ src/view/home/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/view/mod.rs | 3 +++ 3 files changed, 84 insertions(+) diff --git a/src/library.rs b/src/library.rs index 4a5e31e1..9b34b7ca 100644 --- a/src/library.rs +++ b/src/library.rs @@ -371,6 +371,30 @@ impl Library { self.has_db_changed = true; } + pub fn rename>(&mut self, path: P, file_name: &str) -> Result<(), Error> { + let src = self.home.join(path.as_ref()); + + let fp = self.paths.remove(path.as_ref()).or_else(|| { + src.metadata().ok() + .and_then(|md| md.fingerprint(self.fat32_epoch).ok()) + }).ok_or_else(|| format_err!("can't get fingerprint of {}", path.as_ref().display()))?; + + let mut dest = src.clone(); + dest.set_file_name(file_name); + fs::rename(&src, &dest)?; + + if self.mode == LibraryMode::Database { + let new_path = dest.strip_prefix(&self.home)?; + self.paths.insert(new_path.to_path_buf(), fp); + if let Some(info) = self.db.get_mut(&fp) { + info.file.path = new_path.to_path_buf(); + self.has_db_changed = true; + } + } + + Ok(()) + } + pub fn remove>(&mut self, path: P) -> Result<(), Error> { let full_path = self.home.join(path.as_ref()); diff --git a/src/view/home/mod.rs b/src/view/home/mod.rs index b6e8c635..661e2e11 100644 --- a/src/view/home/mod.rs +++ b/src/view/home/mod.rs @@ -64,6 +64,7 @@ pub struct Home { reverse_order: bool, visible_books: Metadata, current_directory: PathBuf, + target_document: Option, background_fetchers: FxHashMap, } @@ -200,6 +201,7 @@ impl Home { reverse_order, visible_books, current_directory, + target_document: None, background_fetchers: FxHashMap::default(), }) } @@ -763,6 +765,36 @@ impl Home { } } + fn toggle_rename_document(&mut self, enable: Option, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) { + if let Some(index) = locate_by_id(self, ViewId::RenameDocument) { + if let Some(true) = enable { + return; + } + self.target_document = None; + rq.add(RenderData::expose(*self.child(index).rect(), UpdateMode::Gui)); + self.children.remove(index); + if let Some(ViewId::RenameDocumentInput) = self.focus { + self.toggle_keyboard(false, true, Some(ViewId::RenameDocumentInput), hub, rq, context); + } + } else { + if let Some(false) = enable { + return; + } + let mut ren_doc = NamedInput::new("Rename document".to_string(), + ViewId::RenameDocument, + ViewId::RenameDocumentInput, + 21, context); + if let Some(text) = self.target_document.as_ref() + .and_then(|path| path.file_name()) + .and_then(|file_name| file_name.to_str()) { + ren_doc.set_text(text, rq, context); + } + rq.add(RenderData::new(ren_doc.id(), *ren_doc.rect(), UpdateMode::Gui)); + hub.send(Event::Focus(Some(ViewId::RenameDocumentInput))).ok(); + self.children.push(Box::new(ren_doc) as Box); + } + } + fn toggle_go_to_page(&mut self, enable: Option, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) { if let Some(index) = locate_by_id(self, ViewId::GoToPage) { if let Some(true) = enable { @@ -911,6 +943,8 @@ impl Home { entries.push(EntryKind::SubMenu("Move To".to_string(), move_to)); } + entries.push(EntryKind::Command("Rename".to_string(), + EntryId::Rename(path.clone()))); entries.push(EntryKind::Command("Remove".to_string(), EntryId::Remove(path.clone()))); @@ -1048,6 +1082,12 @@ impl Home { self.children.push(Box::new(notif) as Box); } + fn rename(&mut self, path: &Path, file_name: &str, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) -> Result<(), Error> { + context.library.rename(path, file_name)?; + self.refresh_visibles(true, false, hub, rq, context); + Ok(()) + } + fn remove(&mut self, path: &Path, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) -> Result<(), Error> { let full_path = context.library.home.join(path); if full_path.exists() { @@ -1461,6 +1501,10 @@ impl View for Home { self.toggle_go_to_page(Some(false), hub, rq, context); true }, + Event::Close(ViewId::RenameDocument) => { + self.toggle_rename_document(Some(false), hub, rq, context); + true + }, Event::Select(EntryId::Sort(sort_method)) => { let selected_library = context.settings.selected_library; context.settings.libraries[selected_library].sort_method = sort_method; @@ -1548,6 +1592,14 @@ impl View for Home { } true }, + Event::Submit(ViewId::RenameDocumentInput, ref file_name) => { + if let Some(ref path) = self.target_document.take() { + self.rename(path, file_name, hub, rq, context) + .map_err(|e| eprintln!("Can't rename document: {:#}.", e)) + .ok(); + } + true + }, Event::NavigationBarResized(_) => { self.adjust_shelf_top_edge(); self.update_shelf(true, hub, rq, context); @@ -1561,6 +1613,11 @@ impl View for Home { self.empty_trash(hub, rq, context); true }, + Event::Select(EntryId::Rename(ref path)) => { + self.target_document = Some(path.clone()); + self.toggle_rename_document(Some(true), hub, rq, context); + true + }, Event::Select(EntryId::Remove(ref path)) | Event::FetcherRemoveDocument(_, ref path) => { self.remove(path, hub, rq, context) .map_err(|e| eprintln!("Can't remove document: {:#}.", e)) diff --git a/src/view/mod.rs b/src/view/mod.rs index f021d4c9..7d91c926 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -389,6 +389,8 @@ pub enum ViewId { MarginCropperMenu, SearchMenu, SketchMenu, + RenameDocument, + RenameDocumentInput, GoToPage, GoToPageInput, GoToResultsPage, @@ -495,6 +497,7 @@ pub enum EntryId { Sort(SortMethod), ReverseOrder, EmptyTrash, + Rename(PathBuf), Remove(PathBuf), CopyTo(PathBuf, usize), MoveTo(PathBuf, usize), From 97a19898aa9f2905b8daa949d652ec8b5b30133f Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Tue, 20 Apr 2021 21:17:23 +0200 Subject: [PATCH 05/17] Tighten the criterion for rectangle line-sameness Fixes #163. --- src/view/reader/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/view/reader/mod.rs b/src/view/reader/mod.rs index d257e91e..f8a7184a 100644 --- a/src/view/reader/mod.rs +++ b/src/view/reader/mod.rs @@ -2711,7 +2711,8 @@ impl View for Reader { let mut rect = rects[i].0; while rects[i].1 < start_high { let next_rect = rects[i+1].0; - if rect.min.y < next_rect.max.y && next_rect.min.y < rect.max.y { + if rect.max.y.min(next_rect.max.y) - rect.min.y.max(next_rect.min.y) > + rect.height().min(next_rect.height()) as i32 / 2 { if rects[i+1].1 == start_high { if rect.min.x < next_rect.min.x { rect.max.x = next_rect.min.x; @@ -2738,7 +2739,8 @@ impl View for Reader { let mut rect = rects[i].0; while rects[i].1 > end_low { let prev_rect = rects[i-1].0; - if rect.min.y < prev_rect.max.y && prev_rect.min.y < rect.max.y { + if rect.max.y.min(prev_rect.max.y) - rect.min.y.max(prev_rect.min.y) > + rect.height().min(prev_rect.height()) as i32 / 2 { if rects[i-1].1 == end_low { if rect.min.x > prev_rect.min.x { rect.min.x = prev_rect.max.x; @@ -3692,7 +3694,8 @@ impl View for Reader { fb.invert_region(search_rect); } if let Some(last) = last_rect { - if rect.min.y < last.max.y && last.min.y < rect.max.y && (last.max.x < rect.min.x || rect.max.x < last.min.x) { + if rect.max.y.min(last.max.y) - rect.min.y.max(last.min.y) > rect.height().min(last.height()) as i32 / 2 && + (last.max.x < rect.min.x || rect.max.x < last.min.x) { let space = if last.max.x < rect.min.x { rect![last.max.x, (last.min.y + rect.min.y) / 2, rect.min.x, (last.max.y + rect.max.y) / 2] @@ -3722,7 +3725,9 @@ impl View for Reader { fb.shift_region(sel_rect, drift); } if let Some(last) = last_rect { - if rect.min.y < last.max.y && last.min.y < rect.max.y && (last.max.x < rect.min.x || rect.max.x < last.min.x) { + // Are `rect` and `last` on the same line? + if rect.max.y.min(last.max.y) - rect.min.y.max(last.min.y) > rect.height().min(last.height()) as i32 / 2 && + (last.max.x < rect.min.x || rect.max.x < last.min.x) { let space = if last.max.x < rect.min.x { rect![last.max.x, (last.min.y + rect.min.y) / 2, rect.min.x, (last.max.y + rect.max.y) / 2] @@ -3750,7 +3755,8 @@ impl View for Reader { fb.invert_region(sel_rect); } if let Some(last) = last_rect { - if rect.min.y < last.max.y && last.min.y < rect.max.y && (last.max.x < rect.min.x || rect.max.x < last.min.x) { + if rect.max.y.min(last.max.y) - rect.min.y.max(last.min.y) > rect.height().min(last.height()) as i32 / 2 && + (last.max.x < rect.min.x || rect.max.x < last.min.x) { let space = if last.max.x < rect.min.x { rect![last.max.x, (last.min.y + rect.min.y) / 2, rect.min.x, (last.max.y + rect.max.y) / 2] From a158a47e072fb49173950c8ecd2451b06f5b7a3c Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Thu, 22 Apr 2021 23:06:16 +0200 Subject: [PATCH 06/17] Don't break up text material around HTML entities --- src/document/html/engine.rs | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index 5a913d05..41b49e4c 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -705,34 +705,11 @@ impl Engine { } }, Node::Text(TextData { offset, text }) => { - let mut index = 0; - while let Some(start_delta) = text[index..].find('&') { - if start_delta > 0 { - inlines.push(InlineMaterial::Text(TextMaterial { - offset: *offset + index, - text: text[index..index+start_delta].to_string(), - style: parent_style.clone(), - })); - } - index += start_delta; - if let Some(end_delta) = text[index..].find(';') { - inlines.push(InlineMaterial::Text(TextMaterial { - offset: *offset + index, - text: decode_entities(&text[index..=index+end_delta]).into_owned(), - style: parent_style.clone(), - })); - index += end_delta + 1; - } else { - break; - } - } - if index < text.len() { - inlines.push(InlineMaterial::Text(TextMaterial { - offset: *offset + index, - text: text[index..].to_string(), - style: parent_style.clone(), - })); - } + inlines.push(InlineMaterial::Text(TextMaterial { + offset: *offset, + text: decode_entities(&text).into_owned(), + style: parent_style.clone(), + })); }, Node::Whitespace(TextData { offset, text }) => { inlines.push(InlineMaterial::Text(TextMaterial { From 576c7adb0ea40afb43d2df394836925d01223891 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Mon, 26 Apr 2021 21:53:27 +0200 Subject: [PATCH 07/17] Apply UAX 14 as early as possible --- src/document/html/engine.rs | 358 ++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 203 deletions(-) diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index 41b49e4c..1787bce4 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -741,12 +741,9 @@ impl Engine { items.push(ParagraphItem::Glue { width: 0, stretch: big_stretch, shrink: 0 }); } - let mut last_c = None; - for m in inlines.iter() { match m { InlineMaterial::Image(ImageMaterial { offset, path, style }) => { - last_c = None; let (mut width, mut height) = (style.width, style.height); let mut scale = 1.0; let dpi = self.dpi; @@ -791,168 +788,137 @@ impl Engine { } }, InlineMaterial::Text(TextMaterial { offset, text, style }) => { - let mut buf = String::new(); let font_size = (style.font_size * 64.0) as u32; + let mut start_index = 0; + for (end_index, _is_hardbreak) in LineBreakIterator::new(&text) { + for chunk in text[start_index..end_index].split_inclusive(char::is_whitespace) { + if let Some((i, c)) = chunk.char_indices().next_back() { + let j = i + if c.is_whitespace() { 0 } else { c.len_utf8() }; + if j > 0 { + let buf = &text[start_index..start_index+j]; + let local_offset = offset + start_index; + let mut plan = { + let font = self.fonts.as_mut().unwrap() + .get_mut(style.font_kind, + style.font_style, + style.font_weight); + font.set_size(font_size, self.dpi); + font.plan(buf, None, style.font_features.as_deref()) + }; + plan.space_out(style.letter_spacing); + + items.push(ParagraphItem::Box { + width: plan.width, + data: ParagraphElement::Text(TextElement { + offset: local_offset, + language: style.language.clone(), + text: buf.to_string(), + plan, + font_features: style.font_features.clone(), + font_kind: style.font_kind, + font_style: style.font_style, + font_weight: style.font_weight, + vertical_align: style.vertical_align, + letter_spacing: style.letter_spacing, + font_size, + color: style.color, + uri: style.uri.clone(), + }), + }); + } + if c.is_whitespace() { + if c == '\n' && parent_style.retain_whitespace { + let stretch = if parent_style.text_align == TextAlign::Center { big_stretch } else { line_width }; - for (i, c) in text.char_indices() { - if c.is_whitespace() { - if !buf.is_empty() { - let local_offset = offset + i - buf.len() + 1; - let mut plan = { - let font = self.fonts.as_mut().unwrap() - .get_mut(style.font_kind, - style.font_style, - style.font_weight); - font.set_size(font_size, self.dpi); - font.plan(&buf, None, style.font_features.as_deref()) - }; - plan.space_out(style.letter_spacing); - - items.push(ParagraphItem::Box { - width: plan.width, - data: ParagraphElement::Text(TextElement { - offset: local_offset, - language: style.language.clone(), - text: buf, - plan, - font_features: style.font_features.clone(), - font_kind: style.font_kind, - font_style: style.font_style, - font_weight: style.font_weight, - vertical_align: style.vertical_align, - letter_spacing: style.letter_spacing, - font_size, - color: style.color, - uri: style.uri.clone(), - }), - }); - - buf = String::new(); - } - - if c == '\n' && parent_style.retain_whitespace { - let stretch = if parent_style.text_align == TextAlign::Center { big_stretch } else { line_width }; + items.push(ParagraphItem::Penalty { penalty: INFINITE_PENALTY, width: 0, flagged: false }); + items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); - items.push(ParagraphItem::Penalty { penalty: INFINITE_PENALTY, width: 0, flagged: false }); - items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + items.push(ParagraphItem::Penalty { width: 0, penalty: -INFINITE_PENALTY, flagged: false }); - items.push(ParagraphItem::Penalty { width: 0, penalty: -INFINITE_PENALTY, flagged: false }); + if parent_style.text_align == TextAlign::Center { + items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); + items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); + items.push(ParagraphItem::Glue { width: 0, stretch: big_stretch, shrink: 0 }); + } + start_index += chunk.len(); + continue; + } - if parent_style.text_align == TextAlign::Center { - items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); - items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); - items.push(ParagraphItem::Glue { width: 0, stretch: big_stretch, shrink: 0 }); - } - last_c = Some(c); - continue; - } + let last_c = text[..start_index+i].chars().next_back(); - if !parent_style.retain_whitespace && (c == ' ' || c.is_control()) && - (last_c.map(|c| c == ' ' || c.is_control()) == Some(true)) { - last_c = Some(c); - continue; - } - - let mut width = if let Some(index) = FONT_SPACES.chars().position(|x| x == c) { - space_plan.glyph_advance(index) - } else if let Some(ratio) = WORD_SPACE_RATIOS.get(&c) { - (space_plan.glyph_advance(0) as f32 * ratio) as i32 - } else if let Some(ratio) = EM_SPACE_RATIOS.get(&c) { - pt_to_px(style.font_size * ratio, self.dpi).round() as i32 - } else { - space_plan.glyph_advance(0) - }; + if !parent_style.retain_whitespace && (c == ' ' || c.is_control()) && + (last_c.map(|c| c == ' ' || c.is_control()) == Some(true)) { + start_index += chunk.len(); + continue; + } - width += 2 * style.letter_spacing; + let mut width = if let Some(index) = FONT_SPACES.chars().position(|x| x == c) { + space_plan.glyph_advance(index) + } else if let Some(ratio) = WORD_SPACE_RATIOS.get(&c) { + (space_plan.glyph_advance(0) as f32 * ratio) as i32 + } else if let Some(ratio) = EM_SPACE_RATIOS.get(&c) { + pt_to_px(style.font_size * ratio, self.dpi).round() as i32 + } else { + space_plan.glyph_advance(0) + }; - let (stretch, shrink) = if style.font_kind != FontKind::Monospace { - (width / 2, width / 3) - } else { - (0, 0) - }; + width += 2 * style.letter_spacing; - if parent_style.retain_whitespace && last_c == Some('\n') { - items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); - } + let (stretch, shrink) = if style.font_kind != FontKind::Monospace { + (width / 2, width / 3) + } else { + (0, 0) + }; - let is_unbreakable = c == '\u{00A0}' || c == '\u{202F}' || c == '\u{2007}'; + if parent_style.retain_whitespace && last_c == Some('\n') { + items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); + } - if is_unbreakable { - items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); - } + let is_unbreakable = c == '\u{00A0}' || c == '\u{202F}' || c == '\u{2007}'; - match parent_style.text_align { - TextAlign::Justify => { - items.push(ParagraphItem::Glue { width, stretch, shrink }); - }, - TextAlign::Center => { if is_unbreakable { - items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); - } else { - let stretch = 3 * width; - items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); - items.push(ParagraphItem::Penalty { width: 0, penalty: 0, flagged: false }); - items.push(ParagraphItem::Glue { width, stretch: -2 * stretch, shrink: 0 }); - items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); - items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); } - }, - TextAlign::Left | TextAlign::Right => { - if is_unbreakable { - items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); - } else { - let stretch = 3 * width; - items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); - items.push(ParagraphItem::Penalty { width: 0, penalty: 0, flagged: false }); - items.push(ParagraphItem::Glue { width, stretch: -stretch, shrink: 0 }); + + match parent_style.text_align { + TextAlign::Justify => { + items.push(ParagraphItem::Glue { width, stretch, shrink }); + }, + TextAlign::Center => { + if is_unbreakable { + items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); + } else { + let stretch = 3 * width; + items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + items.push(ParagraphItem::Penalty { width: 0, penalty: 0, flagged: false }); + items.push(ParagraphItem::Glue { width, stretch: -2 * stretch, shrink: 0 }); + items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); + items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); + items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + } + }, + TextAlign::Left | TextAlign::Right => { + if is_unbreakable { + items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); + } else { + let stretch = 3 * width; + items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + items.push(ParagraphItem::Penalty { width: 0, penalty: 0, flagged: false }); + items.push(ParagraphItem::Glue { width, stretch: -stretch, shrink: 0 }); + } + }, } - }, + } else if end_index < text.len() { + let penalty = if c == '-' { HYPHEN_PENALTY } else { 0 }; + let flagged = penalty > 0; + items.push(ParagraphItem::Penalty { width: 0, penalty, flagged }); + } } - - } else { - buf.push(c); + start_index += chunk.len(); } - - last_c = Some(c); - } - - // TODO: Find a way to integrate this into the main loop? - if !buf.is_empty() { - let local_offset = offset + text.char_indices().last().map(|(i, _)| i).unwrap_or(text.len() - 1) - buf.len() + 1; - let font_size = (style.font_size * 64.0) as u32; - let mut plan = { - let font = self.fonts.as_mut().unwrap() - .get_mut(style.font_kind, - style.font_style, - style.font_weight); - font.set_size(font_size, self.dpi); - font.plan(&buf, None, style.font_features.as_deref()) - }; - plan.space_out(style.letter_spacing); - items.push(ParagraphItem::Box { - width: plan.width, - data: ParagraphElement::Text(TextElement { - offset: local_offset, - language: style.language.clone(), - text: buf, - plan, - font_features: style.font_features.clone(), - font_kind: style.font_kind, - font_style: style.font_style, - font_weight: style.font_weight, - vertical_align: style.vertical_align, - letter_spacing: style.letter_spacing, - font_size, - color: style.color, - uri: style.uri.clone(), - }), - }); } }, InlineMaterial::LineBreak => { - last_c = None; - let stretch = if parent_style.text_align == TextAlign::Center { big_stretch } else { line_width }; items.push(ParagraphItem::Penalty { penalty: INFINITE_PENALTY, width: 0, flagged: false }); @@ -1129,8 +1095,7 @@ impl Engine { None }; - // Insert optional breaks. - items = self.insert_breaks(dictionary, items, &mut hyph_indices); + items = self.hyphenate_paragraph(dictionary, items, &mut hyph_indices); bps = total_fit(&items, &line_lengths, stretch_tolerance, 0); } @@ -1461,14 +1426,13 @@ impl Engine { } } - fn insert_breaks(&mut self, dictionary: Option<&Standard>, items: Vec>, hyph_indices: &mut Vec<[usize; 2]>) -> Vec> { + fn hyphenate_paragraph(&mut self, dictionary: Option<&Standard>, items: Vec>, hyph_indices: &mut Vec<[usize; 2]>) -> Vec> { let mut hyph_items = Vec::with_capacity(items.len()); for itm in items { match itm { ParagraphItem::Box { data: ParagraphElement::Text(ref element), .. } => { let text = &element.text; - let mut start_index = 0; let hyphen_width = if dictionary.is_some() { let font = self.fonts.as_mut().unwrap() .get_mut(element.font_kind, element.font_style, element.font_weight); @@ -1477,67 +1441,55 @@ impl Engine { } else { 0 }; - for (end_index, is_hardbreak) in LineBreakIterator::new(text) { - let chunk = &text[start_index..end_index]; - // Hyphenate. - if let Some(dict) = dictionary { - let mut index_before = chunk.find(char::is_alphabetic).unwrap_or_else(|| chunk.len()); - if index_before > 0 { - let subelem = self.box_from_chunk(&chunk[0..index_before], - start_index, - &element); - hyph_items.push(subelem); - } + if let Some(dict) = dictionary { + let mut index_before = text.find(char::is_alphabetic).unwrap_or_else(|| text.len()); + if index_before > 0 { + let subelem = self.box_from_chunk(&text[0..index_before], + 0, + &element); + hyph_items.push(subelem); + } - let mut index_after = chunk[index_before..].find(|c: char| !c.is_alphabetic()) - .map(|i| index_before + i) - .unwrap_or_else(|| chunk.len()); - while index_before < index_after { - let mut index = 0; - let subchunk = &chunk[index_before..index_after]; - let len_before = hyph_items.len(); - for segment in dict.hyphenate(subchunk).iter().segments() { - - let subelem = self.box_from_chunk(segment, - start_index + index_before + index, - &element); - hyph_items.push(subelem); - index += segment.len(); - if index < subchunk.len() { - hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: HYPHEN_PENALTY, flagged: true }); - } + let mut index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) + .map(|i| index_before + i) + .unwrap_or_else(|| text.len()); + while index_before < index_after { + let mut index = 0; + let chunk = &text[index_before..index_after]; + let len_before = hyph_items.len(); + for segment in dict.hyphenate(chunk).iter().segments() { + let subelem = self.box_from_chunk(segment, + index_before + index, + &element); + hyph_items.push(subelem); + index += segment.len(); + if index < chunk.len() { + hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: HYPHEN_PENALTY, flagged: true }); } - let len_after = hyph_items.len(); - if len_after > 1 + len_before { - hyph_indices.push([len_before, len_after]); - } - index_before = chunk[index_after..].find(char::is_alphabetic) - .map(|i| index_after + i) - .unwrap_or_else(|| chunk.len()); - if index_before > index_after { - let subelem = self.box_from_chunk(&chunk[index_after..index_before], - start_index + index_after, - &element); - hyph_items.push(subelem); - } - - index_after = chunk[index_before..].find(|c: char| !c.is_alphabetic()) - .map(|i| index_before + i) - .unwrap_or_else(|| chunk.len()); } - } else { - let subelem = self.box_from_chunk(chunk, start_index, &element); - hyph_items.push(subelem); - } - if !is_hardbreak { - let penalty = if chunk.ends_with('-') { HYPHEN_PENALTY } else { 0 }; - let flagged = penalty > 0; - hyph_items.push(ParagraphItem::Penalty { width: 0, penalty, flagged }); + let len_after = hyph_items.len(); + if len_after > 1 + len_before { + hyph_indices.push([len_before, len_after]); + } + index_before = text[index_after..].find(char::is_alphabetic) + .map(|i| index_after + i) + .unwrap_or_else(|| text.len()); + if index_before > index_after { + let subelem = self.box_from_chunk(&text[index_after..index_before], + index_after, + &element); + hyph_items.push(subelem); + } + + index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) + .map(|i| index_before + i) + .unwrap_or_else(|| text.len()); } - start_index = end_index; + } else { + let subelem = self.box_from_chunk(text, 0, &element); + hyph_items.push(subelem); } - }, _ => { hyph_items.push(itm) }, } From c5ca5cefbdfe8cfc7262397ac1631730f6ea17ff Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Wed, 28 Apr 2021 14:04:19 +0200 Subject: [PATCH 08/17] Compute the space plan for the current style And not for the parent style, since the font size might differ. Also add support for the `word-spacing` CSS property. --- src/document/html/engine.rs | 51 ++++++++++++++++++++++++------------- src/document/html/layout.rs | 9 +++++++ src/document/html/parse.rs | 14 +++++++++- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index 1787bce4..e13df504 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -14,14 +14,17 @@ use crate::document::pdf::PdfOpener; use crate::unit::{mm_to_px, pt_to_px}; use crate::geom::{Point, Vec2, Rectangle, Edge}; use crate::settings::{DEFAULT_FONT_SIZE, DEFAULT_MARGIN_WIDTH, DEFAULT_TEXT_ALIGN, DEFAULT_LINE_HEIGHT}; -use super::parse::{parse_display, parse_edge, parse_float, parse_text_align, parse_text_indent, parse_width, parse_height, parse_inline_material}; -use super::parse::{parse_font_kind, parse_font_style, parse_font_weight, parse_font_size, parse_font_features, parse_font_variant, parse_letter_spacing}; +use super::parse::{parse_display, parse_edge, parse_float, parse_text_align, parse_text_indent}; +use super::parse::{parse_width, parse_height, parse_inline_material, parse_font_kind, parse_font_style}; +use super::parse::{parse_font_weight, parse_font_size, parse_font_features, parse_font_variant}; +use super::parse::{parse_letter_spacing, parse_word_spacing}; use super::parse::{parse_line_height, parse_vertical_align, parse_color, parse_list_style_type}; use super::dom::{Node, ElementData, TextData}; use super::layout::{StyleData, InlineMaterial, TextMaterial, ImageMaterial}; use super::layout::{GlueMaterial, PenaltyMaterial, ChildArtifact, SiblingStyle, LoopContext}; use super::layout::{RootData, DrawState, DrawCommand, TextCommand, ImageCommand, FontKind, Fonts}; -use super::layout::{TextAlign, ParagraphElement, TextElement, ImageElement, Display, Float, ListStyleType, LineStats}; +use super::layout::{TextAlign, ParagraphElement, TextElement, ImageElement, Display, Float}; +use super::layout::{WordSpacing, ListStyleType, LineStats}; use super::layout::{hyph_lang, collapse_margins, DEFAULT_HYPH_LANG, HYPHENATION_PATTERNS}; use super::layout::{EM_SPACE_RATIOS, WORD_SPACE_RATIOS, FONT_SPACES}; use super::style::{Stylesheet, specified_values}; @@ -178,6 +181,10 @@ impl Engine { .and_then(|value| parse_letter_spacing(value, style.font_size, self.font_size, self.dpi)) .unwrap_or(parent_style.letter_spacing); + style.word_spacing = props.get("word-spacing") + .and_then(|value| parse_word_spacing(value, style.font_size, self.font_size, self.dpi)) + .unwrap_or(parent_style.word_spacing); + style.vertical_align = props.get("vertical-align") .and_then(|value| parse_vertical_align(value, style.font_size, self.font_size, style.line_height, self.dpi)) .unwrap_or(parent_style.vertical_align); @@ -619,6 +626,10 @@ impl Engine { .and_then(|value| parse_letter_spacing(value, style.font_size, self.font_size, self.dpi)) .unwrap_or(parent_style.letter_spacing); + style.word_spacing = props.get("word-spacing") + .and_then(|value| parse_word_spacing(value, style.font_size, self.font_size, self.dpi)) + .unwrap_or(parent_style.word_spacing); + style.vertical_align = props.get("vertical-align") .and_then(|value| parse_vertical_align(value, style.font_size, self.font_size, style.line_height, self.dpi)) .unwrap_or(parent_style.vertical_align); @@ -724,18 +735,16 @@ impl Engine { fn make_paragraph_items(&mut self, inlines: &[InlineMaterial], parent_style: &StyleData, line_width: i32, resource_fetcher: &mut dyn ResourceFetcher) -> (Vec>, Vec) { let mut items = Vec::new(); let mut floats = Vec::new(); - let font_size = (parent_style.font_size * 64.0) as u32; - let space_plan = { + let big_stretch = 3 * { + let font_size = (parent_style.font_size * 64.0) as u32; let font = self.fonts.as_mut().unwrap() .get_mut(parent_style.font_kind, parent_style.font_style, parent_style.font_weight); font.set_size(font_size, self.dpi); - font.plan(" 0.", None, None) + font.plan(" ", None, None).width }; - let big_stretch = 3 * space_plan.glyph_advance(0); - if parent_style.text_align == TextAlign::Center { items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); items.push(ParagraphItem::Glue { width: 0, stretch: big_stretch, shrink: 0 }); @@ -789,6 +798,14 @@ impl Engine { }, InlineMaterial::Text(TextMaterial { offset, text, style }) => { let font_size = (style.font_size * 64.0) as u32; + let space_plan = { + let font = self.fonts.as_mut().unwrap() + .get_mut(parent_style.font_kind, + parent_style.font_style, + parent_style.font_weight); + font.set_size(font_size, self.dpi); + font.plan(" 0.", None, None) + }; let mut start_index = 0; for (end_index, _is_hardbreak) in LineBreakIterator::new(&text) { for chunk in text[start_index..end_index].split_inclusive(char::is_whitespace) { @@ -862,13 +879,11 @@ impl Engine { space_plan.glyph_advance(0) }; - width += 2 * style.letter_spacing; - - let (stretch, shrink) = if style.font_kind != FontKind::Monospace { - (width / 2, width / 3) - } else { - (0, 0) - }; + width += match style.word_spacing { + WordSpacing::Normal => 0, + WordSpacing::Length(l) => l, + WordSpacing::Ratio(r) => (r * width as f32) as i32, + } + style.letter_spacing; if parent_style.retain_whitespace && last_c == Some('\n') { items.push(ParagraphItem::Box { width: 0, data: ParagraphElement::Nothing }); @@ -882,10 +897,10 @@ impl Engine { match parent_style.text_align { TextAlign::Justify => { - items.push(ParagraphItem::Glue { width, stretch, shrink }); + items.push(ParagraphItem::Glue { width, stretch: width/2, shrink: width/3 }); }, TextAlign::Center => { - if is_unbreakable { + if style.font_kind == FontKind::Monospace { items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); } else { let stretch = 3 * width; @@ -898,7 +913,7 @@ impl Engine { } }, TextAlign::Left | TextAlign::Right => { - if is_unbreakable { + if style.font_kind == FontKind::Monospace { items.push(ParagraphItem::Glue { width, stretch: 0, shrink: 0 }); } else { let stretch = 3 * width; diff --git a/src/document/html/layout.rs b/src/document/html/layout.rs index aa727655..3eafbbf5 100644 --- a/src/document/html/layout.rs +++ b/src/document/html/layout.rs @@ -66,11 +66,19 @@ pub struct StyleData { pub font_features: Option>, pub color: u8, pub letter_spacing: i32, + pub word_spacing: WordSpacing, pub vertical_align: i32, pub list_style_type: Option, pub uri: Option, } +#[derive(Debug, Copy, Clone)] +pub enum WordSpacing { + Normal, + Length(i32), + Ratio(f32), +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Float { Left, @@ -186,6 +194,7 @@ impl Default for StyleData { font_features: None, color: BLACK, letter_spacing: 0, + word_spacing: WordSpacing::Normal, vertical_align: 0, list_style_type: None, uri: None, diff --git a/src/document/html/parse.rs b/src/document/html/parse.rs index 4b8fe331..76af32f7 100644 --- a/src/document/html/parse.rs +++ b/src/document/html/parse.rs @@ -1,6 +1,7 @@ use fxhash::FxHashSet; use regex::Regex; -use super::layout::{FontKind, FontStyle, FontWeight, TextAlign, Display, Float, ListStyleType}; +use super::layout::{FontKind, FontStyle, FontWeight, WordSpacing}; +use super::layout::{TextAlign, Display, Float, ListStyleType}; use super::layout::{InlineMaterial, GlueMaterial, PenaltyMaterial}; use crate::geom::Edge; use crate::unit::{pt_to_px, pc_to_px, mm_to_px, in_to_px}; @@ -122,6 +123,17 @@ pub fn parse_letter_spacing(value: &str, em: f32, rem: f32, dpi: u16) -> Option< } } +pub fn parse_word_spacing(value: &str, em: f32, rem: f32, dpi: u16) -> Option { + if value == "normal" { + Some(WordSpacing::Normal) + } else if let Some(percent) = value.strip_suffix('%') { + percent.parse::().ok() + .map(|v| WordSpacing::Ratio(v / 100.0)) + } else { + parse_length(value, em, rem, dpi).map(WordSpacing::Length) + } +} + pub fn parse_vertical_align(value: &str, em: f32, rem: f32, line_height: i32, dpi: u16) -> Option { if value == "baseline" { Some(0) From f04778d8a6508b6d274a7a1787d4a0f9f6b16165 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Wed, 28 Apr 2021 20:59:10 +0200 Subject: [PATCH 09/17] Properly retrieve the last character --- src/document/html/engine.rs | 12 +++++++++--- src/document/html/layout.rs | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index e13df504..663d12b8 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -750,8 +750,8 @@ impl Engine { items.push(ParagraphItem::Glue { width: 0, stretch: big_stretch, shrink: 0 }); } - for m in inlines.iter() { - match m { + for (index, mater) in inlines.iter().enumerate() { + match mater { InlineMaterial::Image(ImageMaterial { offset, path, style }) => { let (mut width, mut height) = (style.width, style.height); let mut scale = 1.0; @@ -861,7 +861,13 @@ impl Engine { continue; } - let last_c = text[..start_index+i].chars().next_back(); + let last_c = text[..start_index+i].chars().next_back().or_else(|| { + if index > 0 { + inlines[index-1].text().and_then(|text| text.chars().next_back()) + } else { + None + } + }); if !parent_style.retain_whitespace && (c == ' ' || c.is_control()) && (last_c.map(|c| c == ' ' || c.is_control()) == Some(true)) { diff --git a/src/document/html/layout.rs b/src/document/html/layout.rs index 3eafbbf5..2fcd3e44 100644 --- a/src/document/html/layout.rs +++ b/src/document/html/layout.rs @@ -220,6 +220,13 @@ impl InlineMaterial { _ => None, } } + + pub fn text(&self) -> Option<&str> { + match self { + InlineMaterial::Text(TextMaterial { ref text, .. }) => Some(text), + _ => None, + } + } } #[derive(Debug, Clone)] From acce3b6653256a7308e8fe616272423003c3fa88 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Thu, 29 Apr 2021 09:32:40 +0200 Subject: [PATCH 10/17] Avoid needlessly rebuilding the cache --- src/view/reader/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/reader/mod.rs b/src/view/reader/mod.rs index f8a7184a..342f1c5b 100644 --- a/src/view/reader/mod.rs +++ b/src/view/reader/mod.rs @@ -230,7 +230,6 @@ impl Reader { let (width, height) = context.display.dims; let font_size = info.reader.as_ref().and_then(|r| r.font_size) .unwrap_or(settings.reader.font_size); - let first_location = doc.resolve_location(Location::Exact(0))?; doc.layout(width, height, font_size, CURRENT_DEVICE.dpi); @@ -262,6 +261,7 @@ impl Reader { doc.set_text_align(text_align); } + let first_location = doc.resolve_location(Location::Exact(0))?; let mut view_port = ViewPort::default(); let mut contrast = Contrast::default(); let pages_count = doc.pages_count(); From b09a291efab53c4c4520c4c9fa2e21a7f4b28d16 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Fri, 30 Apr 2021 14:07:45 +0200 Subject: [PATCH 11/17] Don't add the remaining markers to an empty page --- src/document/html/engine.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index 663d12b8..c87a3c9b 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -1403,9 +1403,17 @@ impl Engine { } } - while let Some(offset) = markers.get(markers_index) { - page.push(DrawCommand::Marker(root_data.start_offset + *offset)); - markers_index += 1; + let last_page = if !page.is_empty() { + Some(&mut page) + } else { + display_list.iter_mut().rev().find(|page| !page.is_empty()) + }; + + if let Some(last_page) = last_page { + while let Some(offset) = markers.get(markers_index) { + last_page.push(DrawCommand::Marker(root_data.start_offset + *offset)); + markers_index += 1; + } } rects.push(page_rect.take()); From e231bfc63d574345f32285aa4bfbe22286278316 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 1 May 2021 11:48:32 +0200 Subject: [PATCH 12/17] Convert StarDict dictionaries in `plato.sh` --- contrib/config-sample.sh | 8 ++++++++ contrib/convert-dictionary.sh | 26 ++++++++++++++++++++++++++ contrib/plato.sh | 26 ++++++++++++++++++-------- 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 contrib/config-sample.sh create mode 100755 contrib/convert-dictionary.sh diff --git a/contrib/config-sample.sh b/contrib/config-sample.sh new file mode 100644 index 00000000..4b9cbcf4 --- /dev/null +++ b/contrib/config-sample.sh @@ -0,0 +1,8 @@ +# Don't set the framebuffer's depth. +# unset PLATO_SET_FRAMEBUFFER_DEPTH + +# Don't convert StarDict dictionaries. +# unset PLATO_CONVERT_DICTIONARIES + +# Disable hyphenation. +# [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns diff --git a/contrib/convert-dictionary.sh b/contrib/convert-dictionary.sh new file mode 100755 index 00000000..8f31d4ab --- /dev/null +++ b/contrib/convert-dictionary.sh @@ -0,0 +1,26 @@ +#! /bin/sh +# Converts a StarDict dictionary to the dictd format. +# The first argument must be the path to the IFO file. + +trap 'exit 1' ERR + +base=${1%.*} +bindir=bin/utils +short_name=$(grep '^bookname=' "$1" | cut -d '=' -f 2) +url=$(grep '^website=' "$1" | cut -d '=' -f 2) + +echo "Converting ${short_name} (${1})." + +[ -e "${base}.dict.dz" ] && "$bindir"/dictzip -d "${base}.dict.dz" + +args="${base}.dict" + +[ -e "${base}.syn" ] && args="$args ${base}.syn" + +# shellcheck disable=SC2086 +"$bindir"/sdunpack $args < "${base}.idx" > "${base}.txt" +"$bindir"/dictfmt --quiet --utf8 --index-keep-orig --headword-separator '|' -s "$short_name" -u "$url" -t "$base" < "${base}.txt" +"$bindir"/dictzip "${base}.dict" + +rm "$1" "${base}.idx" "${base}.txt" +[ -e "${base}.syn" ] && rm "${base}.syn" diff --git a/contrib/plato.sh b/contrib/plato.sh index 3579d9f4..f0e1c9f4 100755 --- a/contrib/plato.sh +++ b/contrib/plato.sh @@ -3,6 +3,12 @@ WORKDIR=$(dirname "$0") cd "$WORKDIR" || exit 1 +PLATO_SET_FRAMEBUFFER_DEPTH=1 +PLATO_CONVERT_DICTIONARIES=1 + +# shellcheck disable=SC1091 +[ -e config.sh ] && . config.sh + if [ "$PLATO_STANDALONE" ] ; then # Stop the animation started by rcS REM_TRIES=10 @@ -63,14 +69,18 @@ export LD_LIBRARY_PATH="libs:${LD_LIBRARY_PATH}" [ -e info.log ] && [ "$(stat -c '%s' info.log)" -gt $((1<<18)) ] && mv info.log archive.log -case "${PRODUCT}:${MODEL_NUMBER}" in - storm:*|frost:*|nova:*|snow:378|star:379) - unset ORIG_BPP - ;; - *) - ORIG_BPP=$(./bin/utils/fbdepth -g) - ;; -esac +[ "$PLATO_CONVERT_DICTIONARIES" ] && find dictionaries -name '*.ifo' -exec ./convert-dictionary.sh {} \; + +if [ "$PLATO_SET_FRAMEBUFFER_DEPTH" ] ; then + case "${PRODUCT}:${MODEL_NUMBER}" in + storm:*|frost:*|nova:*|snow:378|star:379) + unset ORIG_BPP + ;; + *) + ORIG_BPP=$(./bin/utils/fbdepth -g) + ;; + esac +fi [ "$ORIG_BPP" ] && ./bin/utils/fbdepth -q -d 8 From 21fc68e54c2f4a57b305f92963cb6687c198c04d Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 1 May 2021 11:50:47 +0200 Subject: [PATCH 13/17] Update third-party dependency: HarfBuzz --- thirdparty/download.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/download.sh b/thirdparty/download.sh index 12c1fdda..e66b9019 100755 --- a/thirdparty/download.sh +++ b/thirdparty/download.sh @@ -11,7 +11,7 @@ declare -A urls=( ["jbig2dec"]="https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs9533/jbig2dec-0.19.tar.gz" # Fonts ["freetype2"]="https://download.savannah.gnu.org/releases/freetype/freetype-2.10.4.tar.gz" - ["harfbuzz"]="https://github.com/harfbuzz/harfbuzz/archive/2.7.4.tar.gz" + ["harfbuzz"]="https://github.com/harfbuzz/harfbuzz/archive/2.8.0.tar.gz" # Documents ["gumbo"]="https://github.com/google/gumbo-parser/archive/v0.10.1.tar.gz" ["djvulibre"]="http://downloads.sourceforge.net/djvu/djvulibre-3.5.28.tar.gz" From ac2aa8dcde330eecc45720ea2a088c0d9f021416 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Mon, 19 Apr 2021 21:24:07 +0200 Subject: [PATCH 14/17] Add paragraph breaker settings Fixes #161. --- src/document/djvu.rs | 6 ++++++ src/document/epub/mod.rs | 10 ++++++++++ src/document/html/engine.rs | 26 ++++++++++++++++++++------ src/document/html/mod.rs | 10 ++++++++++ src/document/mod.rs | 2 ++ src/document/pdf.rs | 6 ++++++ src/settings/mod.rs | 20 ++++++++++++++++++++ src/view/reader/mod.rs | 14 ++++++++++++++ 8 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/document/djvu.rs b/src/document/djvu.rs index e0c02c8f..cf54b32a 100644 --- a/src/document/djvu.rs +++ b/src/document/djvu.rs @@ -249,6 +249,12 @@ impl Document for DjvuDocument { fn set_line_height(&mut self, _line_height: f32) { } + + fn set_hyphen_penalty(&mut self, _hyphen_penalty: i32) { + } + + fn set_stretch_tolerance(&mut self, _stretch_tolerance: f32) { + } } impl DjvuDocument { diff --git a/src/document/epub/mod.rs b/src/document/epub/mod.rs index 33f3915a..cbcc5506 100644 --- a/src/document/epub/mod.rs +++ b/src/document/epub/mod.rs @@ -847,6 +847,16 @@ impl Document for EpubDocument { self.cache.clear(); } + fn set_hyphen_penalty(&mut self, hyphen_penalty: i32) { + self.engine.set_hyphen_penalty(hyphen_penalty); + self.cache.clear(); + } + + fn set_stretch_tolerance(&mut self, stretch_tolerance: f32) { + self.engine.set_stretch_tolerance(stretch_tolerance); + self.cache.clear(); + } + fn title(&self) -> Option { self.metadata("dc:title") } diff --git a/src/document/html/engine.rs b/src/document/html/engine.rs index c87a3c9b..d278783b 100644 --- a/src/document/html/engine.rs +++ b/src/document/html/engine.rs @@ -13,6 +13,7 @@ use crate::document::{Document, Location}; use crate::document::pdf::PdfOpener; use crate::unit::{mm_to_px, pt_to_px}; use crate::geom::{Point, Vec2, Rectangle, Edge}; +use crate::settings::{HYPHEN_PENALTY, STRETCH_TOLERANCE}; use crate::settings::{DEFAULT_FONT_SIZE, DEFAULT_MARGIN_WIDTH, DEFAULT_TEXT_ALIGN, DEFAULT_LINE_HEIGHT}; use super::parse::{parse_display, parse_edge, parse_float, parse_text_align, parse_text_indent}; use super::parse::{parse_width, parse_height, parse_inline_material, parse_font_kind, parse_font_style}; @@ -32,8 +33,6 @@ use super::style::{Stylesheet, specified_values}; const DEFAULT_DPI: u16 = 300; const DEFAULT_WIDTH: u32 = 1404; const DEFAULT_HEIGHT: u32 = 1872; -const HYPHEN_PENALTY: i32 = 50; -const STRETCH_TOLERANCE: f32 = 1.26; pub type Page = Vec; @@ -43,7 +42,12 @@ pub trait ResourceFetcher { // TODO: Add min_font_size. pub struct Engine { + // The fonts used for each CSS font family. fonts: Option, + // The penalty for lines ending with a hyphen. + hyphen_penalty: i32, + // The stretching/shrinking allowed for word spaces. + stretch_tolerance: f32, // Page margins in pixels. pub margin: Edge, // Font size in points. @@ -65,6 +69,8 @@ impl Engine { Engine { fonts: None, + hyphen_penalty: HYPHEN_PENALTY, + stretch_tolerance: STRETCH_TOLERANCE, margin, font_size: DEFAULT_FONT_SIZE, text_align: DEFAULT_TEXT_ALIGN, @@ -81,6 +87,14 @@ impl Engine { } } + pub fn set_hyphen_penalty(&mut self, hyphen_penalty: i32) { + self.hyphen_penalty = hyphen_penalty; + } + + pub fn set_stretch_tolerance(&mut self, stretch_tolerance: f32) { + self.stretch_tolerance = stretch_tolerance; + } + pub fn set_margin(&mut self, margin: &Edge) { self.margin = *margin; } @@ -930,7 +944,7 @@ impl Engine { }, } } else if end_index < text.len() { - let penalty = if c == '-' { HYPHEN_PENALTY } else { 0 }; + let penalty = if c == '-' { self.hyphen_penalty } else { 0 }; let flagged = penalty > 0; items.push(ParagraphItem::Penalty { width: 0, penalty, flagged }); } @@ -987,7 +1001,7 @@ impl Engine { }; let stretch_tolerance = if style.text_align == TextAlign::Justify { - STRETCH_TOLERANCE + self.stretch_tolerance } else { 10.0 }; @@ -1151,7 +1165,7 @@ impl Engine { } } - bps = standard_fit(&items, &line_lengths, STRETCH_TOLERANCE); + bps = standard_fit(&items, &line_lengths, self.stretch_tolerance); } // Remove unselected optional hyphens (prevents broken ligatures). @@ -1494,7 +1508,7 @@ impl Engine { hyph_items.push(subelem); index += segment.len(); if index < chunk.len() { - hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: HYPHEN_PENALTY, flagged: true }); + hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: self.hyphen_penalty, flagged: true }); } } let len_after = hyph_items.len(); diff --git a/src/document/html/mod.rs b/src/document/html/mod.rs index da4919f5..5cdfe3d2 100644 --- a/src/document/html/mod.rs +++ b/src/document/html/mod.rs @@ -405,6 +405,16 @@ impl Document for HtmlDocument { self.pages.clear(); } + fn set_hyphen_penalty(&mut self, hyphen_penalty: i32) { + self.engine.set_hyphen_penalty(hyphen_penalty); + self.pages.clear(); + } + + fn set_stretch_tolerance(&mut self, stretch_tolerance: f32) { + self.engine.set_stretch_tolerance(stretch_tolerance); + self.pages.clear(); + } + fn title(&self) -> Option { self.content.find("head") .and_then(Node::children) diff --git a/src/document/mod.rs b/src/document/mod.rs index 537de9e3..ca44b358 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -105,6 +105,8 @@ pub trait Document: Send+Sync { fn set_margin_width(&mut self, width: i32); fn set_text_align(&mut self, text_align: TextAlign); fn set_line_height(&mut self, line_height: f32); + fn set_hyphen_penalty(&mut self, hyphen_penalty: i32); + fn set_stretch_tolerance(&mut self, stretch_tolerance: f32); fn title(&self) -> Option; fn author(&self) -> Option; diff --git a/src/document/pdf.rs b/src/document/pdf.rs index 7bdb8320..b4e27998 100644 --- a/src/document/pdf.rs +++ b/src/document/pdf.rs @@ -265,6 +265,12 @@ impl Document for PdfDocument { fn set_line_height(&mut self, _line_height: f32) { } + + fn set_hyphen_penalty(&mut self, _hyphen_penalty: i32) { + } + + fn set_stretch_tolerance(&mut self, _stretch_tolerance: f32) { + } } impl<'a> PdfPage<'a> { diff --git a/src/settings/mod.rs b/src/settings/mod.rs index f1d94897..2aaaedf9 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -31,6 +31,8 @@ pub const DEFAULT_LINE_HEIGHT: f32 = 1.2; pub const DEFAULT_FONT_FAMILY: &str = "Libertinus Serif"; // Default text alignment. pub const DEFAULT_TEXT_ALIGN: TextAlign = TextAlign::Left; +pub const HYPHEN_PENALTY: i32 = 50; +pub const STRETCH_TOLERANCE: f32 = 1.26; #[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] @@ -319,9 +321,17 @@ pub struct ReaderSettings { pub margin_width: i32, pub line_height: f32, pub dithered_kinds: FxHashSet, + pub paragraph_breaker: ParagraphBreakerSettings, pub refresh_rate: RefreshRateSettings, } +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(default, rename_all = "kebab-case")] +pub struct ParagraphBreakerSettings { + pub hyphen_penalty: i32, + pub stretch_tolerance: f32, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, rename_all = "kebab-case")] pub struct BatterySettings { @@ -363,6 +373,15 @@ impl Default for HomeSettings { } } +impl Default for ParagraphBreakerSettings { + fn default() -> Self { + ParagraphBreakerSettings { + hyphen_penalty: HYPHEN_PENALTY, + stretch_tolerance: STRETCH_TOLERANCE, + } + } +} + impl Default for ReaderSettings { fn default() -> Self { ReaderSettings { @@ -377,6 +396,7 @@ impl Default for ReaderSettings { margin_width: DEFAULT_MARGIN_WIDTH, line_height: DEFAULT_LINE_HEIGHT, dithered_kinds: ["cbz", "png", "jpg", "jpeg"].iter().map(|k| k.to_string()).collect(), + paragraph_breaker: ParagraphBreakerSettings::default(), refresh_rate: RefreshRateSettings::default(), } } diff --git a/src/view/reader/mod.rs b/src/view/reader/mod.rs index 342f1c5b..faa2524d 100644 --- a/src/view/reader/mod.rs +++ b/src/view/reader/mod.rs @@ -41,6 +41,7 @@ use crate::view::menu::{Menu, MenuKind}; use crate::view::notification::Notification; use crate::settings::{guess_frontlight, FinishedAction, SouthEastCornerAction}; use crate::settings::{DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, DEFAULT_LINE_HEIGHT, DEFAULT_MARGIN_WIDTH}; +use crate::settings::{HYPHEN_PENALTY, STRETCH_TOLERANCE}; use crate::frontlight::LightLevels; use crate::gesture::GestureEvent; use crate::document::{Document, open, Location, TextLocation, BoundedText, Neighbors, BYTES_PER_PAGE}; @@ -261,7 +262,20 @@ impl Reader { doc.set_text_align(text_align); } + let hyphen_penalty = settings.reader.paragraph_breaker.hyphen_penalty; + + if hyphen_penalty != HYPHEN_PENALTY { + doc.set_hyphen_penalty(hyphen_penalty); + } + + let stretch_tolerance = settings.reader.paragraph_breaker.stretch_tolerance; + + if stretch_tolerance != STRETCH_TOLERANCE { + doc.set_stretch_tolerance(stretch_tolerance); + } + let first_location = doc.resolve_location(Location::Exact(0))?; + let mut view_port = ViewPort::default(); let mut contrast = Contrast::default(); let pages_count = doc.pages_count(); From 6a4bd190894e06c06d139f7fc10c58e3a72cd5f4 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 1 May 2021 16:15:23 +0200 Subject: [PATCH 15/17] Provide an example configuration file --- contrib/Settings-sample.toml | 171 +++++++++++++++++++++++++++++++++++ dist.sh | 1 + doc/GUIDE.md | 10 +- src/settings/mod.rs | 2 +- 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 contrib/Settings-sample.toml diff --git a/contrib/Settings-sample.toml b/contrib/Settings-sample.toml new file mode 100644 index 00000000..518ffe3a --- /dev/null +++ b/contrib/Settings-sample.toml @@ -0,0 +1,171 @@ +# selected-library = 0 +# Possible values: "English", "Russian". +keyboard-layout = "English" +frontlight = true +wifi = false +# Handle the Sleep Cover event. +sleep-cover = true +# Automatically enters shared mode when connected to a computer. +auto-share = false +# Defines how the back and forward buttons are mapped to the +# *page forward* and *page backward* actions. +# Possible values: "natural", "inverted". +button-scheme = "natural" +# The number of minutes of inactivity after which a device +# will automatically go to sleep. *Zero* means *never*. +auto-suspend = 30 +# The delay, in days, after which a suspended device +# will power off. *Zero* means *never*. +auto-power-off = 3 +# Formats used for the clock and the clock's pop-up menu. +# The available specifiers are described at: +# https://docs.rs/chrono/latest/chrono/format/strftime/index.html +time-format = "%H:%M" +date-format = "%A, %B %-d, %Y" + +# You can create libraries by adding further [[libraries]] entries. +[[libraries]] +name = "On Board" +path = "/mnt/onboard" +# Possible values: "database", "filesystem". +mode = "database" +sort-method = "opened" +first-column = "title-and-author" +second-column = "progress" +thumbnail-previews = true + +# This example fetcher retrieves articles through the *Wallabag* protocol. +# See `doc/ARTICLE_FETCHER.md` on how to configure it. +[[libraries.hooks]] +path = "Articles" +program = "bin/article_fetcher/article_fetcher" +sort-method = "added" +first-column = "title-and-author" +second-column = "progress" + +# Remove this entry if you don't have an external card slot +# on your device. +[[libraries]] +name = "Removable" +path = "/mnt/sd" +mode = "database" +sort-method = "opened" +first-column = "title-and-author" +second-column = "progress" +thumbnail-previews = true + +# Defines the images displayed when entering an intermission. +# Possible values: "logo:", "cover:", "/path/to/image/file". +# If a relative file path is given, it will be relative to +# the installation directory. +[intermissions] +suspend = "logo:" +power-off = "logo:" +share = "logo:" + +[home] +# Show the address bar that display the path of the current directory. +address-bar = false +# Show the navigation bar that displays the directory hierarchy. +navigation-bar = true +# The maximum number of levels shown above the current directory. +max-levels = 3 +# The size limit, in bytes, of the trash. Once the limit is reached, +# documents will be automatically removed until the invariant is restored. +max-trash-size = 33_554_432 + +[reader] +# How to react when a book is finished. +# Possible values: "notify", "close". +finished = "close" +# The action triggered when tapping the south-east corner. +# Possible values: "go-to-page", "next-page". +south-east-corner = "go-to-page" +# The width ratio, relative to `min(W, H) / 2`, of the strip and corner touch regions. +# Launch the *Touch Events* application to display the current touch regions. +strip-width = 0.6 +corner-width = 0.4 +# The path for the user's font directory. +font-path = "/mnt/onboard/fonts" +# The default serif font. +font-family = "Libertinus Serif" +# The default font size, in points. +font-size = 11.0 +# The default text alignment. +# Possible values: "left", "right", "center", "justify". +text-align = "left" +# The default margin width, in millimeters. +margin-width = 8 +# The default line height, in ems. +line-height = 1.2 +# The file extensions of the files that will automatically be dithered +# when opened for the first time. +dithered-kinds = ["cbz", "jpg", "png", "jpeg"] + +[reader.paragraph-breaker] +# The penalty for hyphenated lines. The maximum value is 10_000. +hyphen-penalty = 50 +# The stretch/shrink tolerance of inter-word spaces. +stretch-tolerance = 1.26 + +# Refresh the screen every `regular` page turns when the colors aren't inverted, +# and every `inverted` page turns when they are. *Zero* means *never*. +[reader.refresh-rate] +regular = 8 +inverted = 2 + +[import] +# Start the import process when the device is unplugged from a computer. +unshare-trigger = true +# Start the import process when *Plato* starts. +startup-trigger = true +# Extract the metadata of EPUB documents. +extract-epub-metadata = true +# The file extensions of the file that will be considered during the +# import process. +allowed-kinds = ["djvu", "xps", "fb2", "pdf", "oxps", "cbz", "epub"] + +[dictionary] +# The default font size and margin width, for the Dictionary application. +# The units are the same as in the `[reader]` section. +font-size = 11.0 +margin-width = 4 + +[sketch] +# The path to a directory where the sketches will be saved. +# Relative paths are relative to the current library's path. +save-path = "Sketches" +# Create a notification when a sketch is successfully saved. +notify-success = true + +[sketch.pen] +# The diameter of the pen tip, in pixels. +size = 2 +# Vary the diameter according to the pen's velocity. +dynamic = true +# The current pen color. +# Possible values: 0 … 255. +color = 0 +# The pen speeds, in pixels per seconds, that clamps the pen's speed. +# min-speed = 36 +# max-speed = 1800 + +[calculator] +# The default font size and margin width, for the Calculator application. +# The units are the same as in the `[reader]` section. +font-size = 8.0 +margin-width = 2 +# The number of remembered inputs. +history-size = 4_096 + +[battery] +# Warn about the battery level being low, when the level +# goes below `warn` percents. +warn = 10.0 +# Shut the device down when the battery level goes below +# `power-off` percents. +power-off = 3.0 + +[frontlight-levels] +intensity = 0.0 +warmth = 0.0 diff --git a/dist.sh b/dist.sh index c7a2a6e7..c320e379 100755 --- a/dist.sh +++ b/dist.sh @@ -38,6 +38,7 @@ find dist/keyboard-layouts -name '*-user.json' -delete find dist/hyphenation-patterns -name '*.bounds' -delete cp target/arm-unknown-linux-gnueabihf/release/plato dist/ cp contrib/*.sh dist +cp contrib/Settings-sample.toml dist cp LICENSE-AGPLv3 dist patchelf --remove-rpath dist/libs/* diff --git a/doc/GUIDE.md b/doc/GUIDE.md index cdbb7104..5cbf6fc1 100644 --- a/doc/GUIDE.md +++ b/doc/GUIDE.md @@ -6,7 +6,15 @@ Pick one of the [one-click install packages](https://www.mobileread.com/forums/s The settings are saved in and read from `Settings.toml`. You can edit this file when *Plato* isn't running or is in shared mode. You can enter the shared mode by connecting your device to a computer. -The default ePUB stylesheet, `css/epub.css`, can be overridden via `css/epub-user.css`. +You can also edit `Settings-sample.toml` and rename it to `Settings.toml` before you first run *Plato*. + +`plato.sh` has a few settings that you can override by with `config.sh` (use `config-sample.sh` as a starting point). + +The following style sheets : `css/{epub,html,dictionary}.css` can be overridden via `css/{epub,html,dictionary}-user.css`. + +The hyphenation bounds for a particular language can be overridden by creating a file name `LANGUAGE_CODE.bounds` in the `hyphenation-patterns` directory. The content of this file must the minimum number of letters before the hyphenation point relative to the beginning and end of the word, separated by a space. You can disable hyphenation all together by uncommenting the corresponding line in `config.sh`. + +Dictionaries in the *StarDict* and *dictd* formats can be placed in the `dictionaries` directory. *Plato* doesn't support *StarDict* natively and will therefore convert all the *StarDict* dictionaries it might find in the `dictionaries` directory during startup. You can disable this behavior by uncommenting the corresponding line in `config.sh`. ## Upgrade diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 2aaaedf9..2db5e4ec 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -385,7 +385,7 @@ impl Default for ParagraphBreakerSettings { impl Default for ReaderSettings { fn default() -> Self { ReaderSettings { - finished: FinishedAction::Notify, + finished: FinishedAction::Close, south_east_corner: SouthEastCornerAction::GoToPage, strip_width: 0.6, corner_width: 0.4, From 3528eda234525ce59ffc3ad721d4134b15d28f18 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 1 May 2021 16:25:59 +0200 Subject: [PATCH 16/17] Update dependencies --- Cargo.lock | 276 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 22 ++--- 2 files changed, 151 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d1aea11..5bc734a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" version = "1.2.0" @@ -8,18 +14,18 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aho-corasick" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "atlatl" @@ -46,11 +52,10 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bincode" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "byteorder", "serde", ] @@ -62,9 +67,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bstr" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" dependencies = [ "memchr", ] @@ -77,9 +82,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" @@ -194,14 +199,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.14" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "crc32fast", "libc", - "miniz_oxide", + "miniz_oxide 0.4.4", ] [[package]] @@ -222,30 +227,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" [[package]] name = "futures-io" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" [[package]] name = "futures-macro" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -255,21 +260,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" [[package]] name = "futures-task" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" [[package]] name = "futures-util" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" dependencies = [ "futures-core", "futures-io", @@ -316,9 +321,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78" +checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" dependencies = [ "bytes", "fnv", @@ -350,9 +355,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ "bytes", "fnv", @@ -361,31 +366,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ "bytes", "http", + "pin-project-lite", ] [[package]] name = "httparse" -version = "1.3.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437" [[package]] name = "httpdate" -version = "0.3.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +checksum = "05842d0d43232b23ccb7060ecb0f0626922c21f30012e97b767b30afd4a5d4b9" [[package]] name = "hyper" -version = "0.14.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54" dependencies = [ "bytes", "futures-channel", @@ -422,9 +428,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ "matches", "unicode-bidi", @@ -456,9 +462,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -499,9 +505,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.88" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "log" @@ -520,9 +526,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "mime" @@ -539,11 +545,21 @@ dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", @@ -554,11 +570,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi", ] @@ -620,9 +635,9 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "paragraph-breaker" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01f29233c60acd0193f58a59496360316b2478492580969b37eab5d8c1adff" +checksum = "df3365bc53d974e212e903f93bb377be189c8f9ae3409fbbb60e5f284dfd73d3" [[package]] name = "percent-encoding" @@ -632,18 +647,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ "proc-macro2", "quote", @@ -719,7 +734,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -736,9 +751,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -769,36 +784,35 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +checksum = "1efb2352a0f4d4b128f734b5c44c79ff80117351138733f12f982fe3e2b13343" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +checksum = "00efb87459ba4f6fb2169d20f68565555688e1250ee6825cdf6254f8b48fafb2" [[package]] name = "reqwest" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0460542b551950620a3648c6aa23318ac6b3cd779114bd873209e6e8b5eb1c34" +checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124" dependencies = [ "base64", "bytes", @@ -847,9 +861,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64", "log", @@ -875,9 +889,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", "untrusted", @@ -885,9 +899,9 @@ dependencies = [ [[package]] name = "sdl2" -version = "0.34.3" +version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" +checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" dependencies = [ "bitflags", "lazy_static", @@ -897,9 +911,9 @@ dependencies = [ [[package]] name = "sdl2-sys" -version = "0.34.3" +version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" +checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" dependencies = [ "cfg-if 0.1.10", "libc", @@ -914,18 +928,18 @@ checksum = "c3bdcf1faee4966686e4545ccb1441ccf81f27a97fe16ad280a405a2f371aebc" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -957,9 +971,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7f3f92a1da3d6b1d32245d0cbcbbab0cfc45996d8df619c42bccfa6d2bbb5f" +checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac" dependencies = [ "libc", "signal-hook-registry", @@ -976,17 +990,16 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi", ] @@ -999,9 +1012,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.62" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" +checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" dependencies = [ "proc-macro2", "quote", @@ -1028,15 +1041,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" -dependencies = [ - "once_cell", -] - [[package]] name = "time" version = "0.1.44" @@ -1050,9 +1054,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" dependencies = [ "tinyvec_macros", ] @@ -1075,9 +1079,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" +checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" dependencies = [ "autocfg", "bytes", @@ -1101,9 +1105,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.3" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" +checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" dependencies = [ "bytes", "futures-core", @@ -1130,9 +1134,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1141,9 +1145,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" dependencies = [ "lazy_static", ] @@ -1156,9 +1160,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" dependencies = [ "matches", ] @@ -1180,9 +1184,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "untrusted" @@ -1210,9 +1214,9 @@ checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", @@ -1237,9 +1241,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if 1.0.0", "serde", @@ -1249,9 +1253,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1264,9 +1268,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.21" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" +checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1276,9 +1280,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1286,9 +1290,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1299,15 +1303,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -1325,9 +1329,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ "webpki", ] @@ -1380,9 +1384,9 @@ checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "zip" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8264fcea9b7a036a4a5103d7153e988dbc2ebbafb34f68a3c2d404b6b82d74b6" +checksum = "9c83dc9b784d252127720168abd71ea82bf8c3d96b17dc565b5e2a02854f2b27" dependencies = [ "byteorder", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index b4b5c02b..8b67d7e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,28 +27,28 @@ required-features = ["fetcher"] bitflags = "1.2.1" downcast-rs = "1.2.0" lazy_static = "1.4.0" -libc = "0.2.88" +libc = "0.2.94" png = "0.16.8" -regex = "1.4.3" -serde = { version = "1.0.124", features = ["derive"] } +regex = "1.5.2" +serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.64" titlecase = "1.1.0" unicode-normalization = "0.1.17" toml = "0.5.8" -zip = "0.5.11" +zip = "0.5.12" kl-hyphenate = "0.7.3" entities = "1.0.1" -paragraph-breaker = "0.4.3" +paragraph-breaker = "0.4.4" xi-unicode = "0.3.0" septem = "1.1.0" -byteorder = "1.4.2" +byteorder = "1.4.3" flate2 = "1.0.14" levenshtein = "1.0.5" nix = "0.20.0" indexmap = { version = "1.6.2", features = ["serde-1"] } -anyhow = "1.0.38" +anyhow = "1.0.40" thiserror = "1.0.24" -walkdir = "2.3.1" +walkdir = "2.3.2" globset = "0.4.6" filetime = "0.2.14" fxhash = "0.2.1" @@ -61,7 +61,7 @@ version = "0.2.21" optional = true [dependencies.reqwest] -version = "0.11.1" +version = "0.11.3" features = ["rustls-tls", "json", "blocking"] default-features = false optional = true @@ -71,11 +71,11 @@ features = ["serde"] version = "0.4.19" [dependencies.sdl2] -version = "0.34.3" +version = "0.34.5" optional = true [dependencies.signal-hook] -version = "0.3.6" +version = "0.3.8" optional = true [features] From 6b09c2b197bac1b0fb2b0090a0c3055322d7693a Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 1 May 2021 18:53:00 +0200 Subject: [PATCH 17/17] Version 0.9.17 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc734a6..03005613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,7 +685,7 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "plato" -version = "0.9.16" +version = "0.9.17" dependencies = [ "anyhow", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 8b67d7e2..fd23ad29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Bastien Dejean "] name = "plato" -version = "0.9.16" +version = "0.9.17" edition = "2018" [[bin]]