diff --git a/Userland/Applications/FileManager/CMakeLists.txt b/Userland/Applications/FileManager/CMakeLists.txt index d5ef54ae29..3c088bd20a 100644 --- a/Userland/Applications/FileManager/CMakeLists.txt +++ b/Userland/Applications/FileManager/CMakeLists.txt @@ -12,6 +12,7 @@ compile_gml(PropertiesWindowAudioTab.gml PropertiesWindowAudioTabGML.h propertie compile_gml(PropertiesWindowFontTab.gml PropertiesWindowFontTabGML.h properties_window_font_tab_gml) compile_gml(PropertiesWindowGeneralTab.gml PropertiesWindowGeneralTabGML.h properties_window_general_tab_gml) compile_gml(PropertiesWindowImageTab.gml PropertiesWindowImageTabGML.h properties_window_image_tab_gml) +compile_gml(PropertiesWindowPDFTab.gml PropertiesWindowPDFTabGML.h properties_window_pdf_tab_gml) set(SOURCES DesktopWidget.cpp @@ -30,7 +31,8 @@ set(GENERATED_SOURCES PropertiesWindowFontTabGML.h PropertiesWindowGeneralTabGML.h PropertiesWindowImageTabGML.h + PropertiesWindowPDFTabGML.h ) serenity_app(FileManager ICON app-file-manager) -target_link_libraries(FileManager PRIVATE LibArchive LibAudio LibCore LibFileSystem LibGfx LibGUI LibDesktop LibConfig LibMain LibThreading) +target_link_libraries(FileManager PRIVATE LibArchive LibAudio LibConfig LibCore LibDesktop LibFileSystem LibGfx LibGUI LibMain LibPDF LibThreading) diff --git a/Userland/Applications/FileManager/PropertiesWindow.cpp b/Userland/Applications/FileManager/PropertiesWindow.cpp index acf44b3e36..e62d306c79 100644 --- a/Userland/Applications/FileManager/PropertiesWindow.cpp +++ b/Userland/Applications/FileManager/PropertiesWindow.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +240,9 @@ ErrorOr PropertiesWindow::create_file_type_specific_tabs(GUI::TabWidget& t if (mime_type.starts_with("image/"sv)) return create_image_tab(tab_widget, move(mapped_file), mime_type); + if (mime_type == "application/pdf"sv) + return create_pdf_tab(tab_widget, move(mapped_file)); + return {}; } @@ -451,6 +456,59 @@ ErrorOr PropertiesWindow::create_image_tab(GUI::TabWidget& tab_widget, Non return {}; } +ErrorOr PropertiesWindow::create_pdf_tab(GUI::TabWidget& tab_widget, NonnullRefPtr mapped_file) +{ + auto maybe_document = PDF::Document::create(mapped_file->bytes()); + if (maybe_document.is_error()) { + warnln("Failed to open '{}': {}", m_path, maybe_document.error().message()); + return {}; + } + auto document = maybe_document.release_value(); + + if (auto handler = document->security_handler(); handler && !handler->has_user_password()) { + // FIXME: Show a password dialog, once we've switched to lazy-loading + auto tab = TRY(tab_widget.try_add_tab("PDF"_short_string)); + tab->set_text(TRY("PDF is password-protected."_string)); + return {}; + } + + if (auto maybe_error = document->initialize(); maybe_error.is_error()) { + warnln("PDF '{}' seems to be invalid: {}", m_path, maybe_error.error().message()); + return {}; + } + + auto tab = TRY(tab_widget.try_add_tab("PDF"_short_string)); + TRY(tab->load_from_gml(properties_window_pdf_tab_gml)); + + tab->find_descendant_of_type_named("pdf_version")->set_text(TRY(String::formatted("{}.{}", document->version().major, document->version().minor))); + tab->find_descendant_of_type_named("pdf_page_count")->set_text(TRY(String::number(document->get_page_count()))); + + auto maybe_info_dict = document->info_dict(); + if (maybe_info_dict.is_error()) { + warnln("Failed to read InfoDict from '{}': {}", m_path, maybe_info_dict.error().message()); + } else if (maybe_info_dict.value().has_value()) { + auto get_info_string = [](PDF::PDFErrorOr> input) -> ErrorOr { + if (input.is_error()) + return String {}; + if (!input.value().has_value()) + return String {}; + return String::from_deprecated_string(input.value().value()); + }; + + auto info_dict = maybe_info_dict.release_value().release_value(); + tab->find_descendant_of_type_named("pdf_title")->set_text(TRY(get_info_string(info_dict.title()))); + tab->find_descendant_of_type_named("pdf_author")->set_text(TRY(get_info_string(info_dict.author()))); + tab->find_descendant_of_type_named("pdf_subject")->set_text(TRY(get_info_string(info_dict.subject()))); + tab->find_descendant_of_type_named("pdf_keywords")->set_text(TRY(get_info_string(info_dict.keywords()))); + tab->find_descendant_of_type_named("pdf_creator")->set_text(TRY(get_info_string(info_dict.creator()))); + tab->find_descendant_of_type_named("pdf_producer")->set_text(TRY(get_info_string(info_dict.producer()))); + tab->find_descendant_of_type_named("pdf_creation_date")->set_text(TRY(get_info_string(info_dict.creation_date()))); + tab->find_descendant_of_type_named("pdf_modification_date")->set_text(TRY(get_info_string(info_dict.modification_date()))); + } + + return {}; +} + void PropertiesWindow::update() { m_icon->set_bitmap(GUI::FileIconProvider::icon_for_path(make_full_path(m_name), m_mode).bitmap_for_size(32)); diff --git a/Userland/Applications/FileManager/PropertiesWindow.h b/Userland/Applications/FileManager/PropertiesWindow.h index 9b45210f01..a7d63b86f7 100644 --- a/Userland/Applications/FileManager/PropertiesWindow.h +++ b/Userland/Applications/FileManager/PropertiesWindow.h @@ -34,6 +34,7 @@ private: ErrorOr create_audio_tab(GUI::TabWidget&, NonnullRefPtr); ErrorOr create_font_tab(GUI::TabWidget&, NonnullRefPtr, StringView mime_type); ErrorOr create_image_tab(GUI::TabWidget&, NonnullRefPtr, StringView mime_type); + ErrorOr create_pdf_tab(GUI::TabWidget&, NonnullRefPtr); struct PermissionMasks { mode_t read; diff --git a/Userland/Applications/FileManager/PropertiesWindowPDFTab.gml b/Userland/Applications/FileManager/PropertiesWindowPDFTab.gml new file mode 100644 index 0000000000..81a7b6b816 --- /dev/null +++ b/Userland/Applications/FileManager/PropertiesWindowPDFTab.gml @@ -0,0 +1,185 @@ +@GUI::Widget { + layout: @GUI::VerticalBoxLayout { + margins: [8] + spacing: 12 + } + + @GUI::GroupBox { + title: "PDF" + preferred_height: "shrink" + layout: @GUI::VerticalBoxLayout { + margins: [12, 8, 0] + spacing: 2 + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Version:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_version" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Page count:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_page_count" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Title:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_title" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Author:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_author" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Subject:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_subject" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Keywords:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_keywords" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Creator:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_creator" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Producer:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_producer" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Created:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_creation_date" + text_alignment: "TopLeft" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 12 + } + + @GUI::Label { + text: "Modified:" + text_alignment: "TopLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "pdf_modification_date" + text_alignment: "TopLeft" + } + } + } +}