Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Optional: Automatically load Nix development environment when entering this directory
# Requires direnv (https://direnv.net/) and nix-direnv for automatic shell activation
# If you don't use direnv, run 'nix develop' manually instead
use flake
49 changes: 45 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
permissions:
contents: write
jobs:
build:
build-windows:
runs-on: windows-latest
steps:
- name: Checkout repository
Expand Down Expand Up @@ -64,11 +64,51 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: paperback-build
name: paperback-windows
path: |
build/paperback.zip
build/paperback_setup.exe
retention-days: 30

build-linux-flatpak:
runs-on: ubuntu-latest
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-49
options: --privileged
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Generate cargo sources for Flatpak
run: |
pip install aiohttp tomlkit
curl -sSL https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/cargo/flatpak-cargo-generator.py -o flatpak-cargo-generator.py
python3 flatpak-cargo-generator.py lib/Cargo.lock -o cargo-sources.json
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: paperback.flatpak
manifest-path: io.github.trypsynth.Paperback.yaml
cache-key: flatpak-builder-${{ github.sha }}

release:
needs: [build-windows, build-linux-flatpak]
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download Windows artifacts
uses: actions/download-artifact@v4
with:
name: paperback-windows
path: windows-build
- name: Download Linux Flatpak
uses: actions/download-artifact@v4
with:
name: paperback-x86_64.flatpak
path: linux-build
- name: Get latest tag reachable from HEAD
id: get_tag
shell: bash
Expand Down Expand Up @@ -99,8 +139,9 @@ jobs:
## Commits since last release
${{ steps.release_notes.outputs.commits }}
files: |
build/paperback.zip
build/paperback_setup.exe
windows-build/paperback.zip
windows-build/paperback_setup.exe
linux-build/paperback.flatpak
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ build/
lib/target/
vcpkg/bin/
web/_site/
result
.direnv/
.flatpak-builder/
*.flatpak
repo-flatpak/
cargo-sources.json
102 changes: 89 additions & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.21)
if(POLICY CMP0144)
cmake_policy(SET CMP0144 NEW)
endif()

# Option to use system libraries instead of vcpkg (for Nix, Linux distros, etc.)
option(USE_SYSTEM_LIBS "Use system libraries instead of vcpkg" OFF)

if(NOT USE_SYSTEM_LIBS)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/bin/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
set(VCPKG_MANIFEST_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/vcpkg/triplets")
Expand All @@ -12,6 +17,7 @@ elseif(APPLE)
else()
set(VCPKG_TARGET_TRIPLET "x64-linux" CACHE STRING "Vcpkg triplet")
endif()
endif()
project(paperback VERSION 0.6.1 LANGUAGES CXX)

if("${PROJECT_VERSION_TWEAK}" STREQUAL "")
Expand All @@ -29,8 +35,28 @@ if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

find_package(Gettext REQUIRED)
find_package(wxWidgets CONFIG REQUIRED COMPONENTS webview)
find_program(CLANG_FORMAT_EXE NAMES clang-format)
if(CLANG_FORMAT_EXE)
file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/app/*.cpp ${CMAKE_SOURCE_DIR}/app/*.hpp)
add_custom_target(format COMMAND ${CLANG_FORMAT_EXE} -i ${ALL_SOURCE_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
endif()
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
if(CLANG_TIDY_EXE)
file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/app/*.cpp ${CMAKE_SOURCE_DIR}/app/*.hpp)
add_custom_target(lint COMMAND ${CLANG_TIDY_EXE} -warnings-as-errors=* --header-filter=^${CMAKE_SOURCE_DIR}/app/ --extra-arg=-std=c++20 ${ALL_SOURCE_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} USES_TERMINAL)
endif()

if(USE_SYSTEM_LIBS)
# Find system libraries (for Nix, system package managers, etc.)
# Most dependencies are now handled by the Rust library
find_package(wxWidgets REQUIRED base core net webview)
find_library(PDFIUM_LIBRARY NAMES pdfium REQUIRED)
find_path(PDFIUM_INCLUDE_DIR NAMES fpdfview.h REQUIRED)
else()
find_package(wxWidgets CONFIG REQUIRED COMPONENTS webview)
endif()

find_package(Gettext)

if(GETTEXT_FOUND)
find_program(XGETTEXT_EXECUTABLE NAMES xgettext)
Expand Down Expand Up @@ -149,17 +175,33 @@ else()
endif()
add_dependencies(paperback libpaperback_rust)
target_include_directories(paperback PRIVATE ${CMAKE_SOURCE_DIR}/app ${CMAKE_SOURCE_DIR} ${PAPERBACK_GENERATED_DIR} ${RUST_TARGET_DIR}/cxxbridge)
target_link_directories(paperback PRIVATE ${PDFIUM_LIB_DIR})
target_link_directories(paperback PRIVATE "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib")
target_link_libraries(paperback PRIVATE
${RUST_LIB_OUTPUT}
pdfium
wx::base
wx::core
wx::net
wx::webview
)
if (WIN32)

if(USE_SYSTEM_LIBS)
target_include_directories(paperback PRIVATE
${PDFIUM_INCLUDE_DIR}
${wxWidgets_INCLUDE_DIRS}
)
target_compile_options(paperback PRIVATE ${wxWidgets_CXX_FLAGS})
target_compile_definitions(paperback PRIVATE ${wxWidgets_DEFINITIONS})
target_link_libraries(paperback PRIVATE
${RUST_LIB_OUTPUT}
${PDFIUM_LIBRARY}
${wxWidgets_LIBRARIES}
)
else()
target_link_directories(paperback PRIVATE ${PDFIUM_LIB_DIR})
target_link_directories(paperback PRIVATE "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib")
target_link_libraries(paperback PRIVATE
${RUST_LIB_OUTPUT}
pdfium
wx::base
wx::core
wx::net
wx::webview
)
endif()

if(WIN32)
target_link_libraries(paperback PRIVATE bcrypt ntdll)
endif()

Expand Down Expand Up @@ -238,3 +280,37 @@ if(WIN32)
else()
add_custom_target(release DEPENDS package)
endif()

# Installation rules (Linux desktop integration)
if(UNIX AND NOT APPLE)
# Install binary
install(TARGETS paperback DESTINATION bin)

# Install desktop file
install(FILES ${CMAKE_SOURCE_DIR}/paperback.desktop
DESTINATION share/applications)

# Install icons
foreach(size 16 32 48 64 128 256)
install(FILES ${CMAKE_SOURCE_DIR}/icons/hicolor/${size}x${size}/apps/paperback.png
DESTINATION share/icons/hicolor/${size}x${size}/apps)
endforeach()

# Install documentation
if(TARGET doc)
install(FILES ${CMAKE_BINARY_DIR}/readme.html
DESTINATION share/doc/paperback)
endif()

# Install translations
if(MO_FILES)
foreach(mo_file ${MO_FILES})
get_filename_component(mo_dir ${mo_file} DIRECTORY)
get_filename_component(lang_dir ${mo_dir} DIRECTORY)
get_filename_component(lang ${lang_dir} NAME)
install(FILES ${mo_file}
DESTINATION share/locale/${lang}/LC_MESSAGES
RENAME paperback.mo)
endforeach()
endif()
endif()
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

## Building

### Windows (VCPKG)

We use VCPKG for managing dependencies. Currently we manage our own VCPKG installation through a submodule. As such, make sure to clone Paperback recursively:

```batch
Expand Down Expand Up @@ -45,6 +47,50 @@ Optional tools:
* `gettext` tools (`xgettext`, `msgfmt`, `msgmerge`) on your `PATH` to generate the translation template and compile translations.
* InnoSetup installed to create the installer with the `release` target.

### Linux

For building with CMake, you'll need CMake 3.21+, a C++20 compiler, and dependencies:
- wxWidgets 3.2+
- chmlib, lexbor, mbedtls, pdfium, pugixml, nlohmann-json

```bash
cmake -B build -DUSE_SYSTEM_LIBS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
sudo cmake --install build
```

Optional tools:
- `pandoc` for HTML readme generation
- `gettext` tools for translations

### Linux (Nix)

**Run directly:**
```bash
nix run github:trypsynth/paperback
```

**Install to profile:**
```bash
nix profile install github:trypsynth/paperback
```

**Build from source:**
```bash
# Clone repository
git clone --recursive https://github.com/trypsynth/paperback
cd paperback

# Build and run
nix run .#paperback

# Or build specific outputs:
nix build .#paperback # Nix derivation

# Build Flatpak:
flatpak-builder --force-clean --repo=repo build io.github.trypsynth.Paperback.yaml
```

## Contributing

Contributions are welcome! Whether through issues, pull requests, discussions, or other means, your interest is most certainly appreciated.
Expand Down
8 changes: 4 additions & 4 deletions app/config_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ wxArrayString to_wx_array(const rust::Vec<rust::String>& rust_vec) {
return result;
}

std::vector<long> to_long_vector(const rust::Vec<long long>& values) {
std::vector<long> to_long_vector(const rust::Vec<std::int64_t>& values) {
std::vector<long> result(values.size());
std::transform(values.begin(), values.end(), result.begin(), [](long long value) {
std::transform(values.begin(), values.end(), result.begin(), [](std::int64_t value) {
return static_cast<long>(value);
});
return result;
Expand Down Expand Up @@ -116,10 +116,10 @@ long config_manager::get_document_position(const wxString& path) const {

void config_manager::set_navigation_history(const wxString& path, const std::vector<long>& history, size_t history_index) {
if (!is_initialized()) return;
rust::Vec<long long> rust_history;
rust::Vec<std::int64_t> rust_history;
rust_history.reserve(history.size());
std::transform(history.begin(), history.end(), std::back_inserter(rust_history), [](long entry) {
return static_cast<long long>(entry);
return static_cast<std::int64_t>(entry);
});
rust::Slice<const std::int64_t> history_slice(rust_history.data(), rust_history.size());
config_manager_set_navigation_history(backend_mut(), to_utf8(path), history_slice, history_index);
Expand Down
2 changes: 1 addition & 1 deletion app/dialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ void bookmark_dialog::repopulate_list(long current_pos) {
}
}

document_info_dialog::document_info_dialog(wxWindow* parent, session_document* session_doc, const wxString& file_path, config_manager& cfg_mgr) : dialog(parent, _("Document Info"), dialog_button_config::ok_only), config_mgr{cfg_mgr}, doc_path{file_path} {
document_info_dialog::document_info_dialog(wxWindow* parent, session_document* session_doc, const wxString& file_path, [[maybe_unused]] config_manager& cfg_mgr) : dialog(parent, _("Document Info"), dialog_button_config::ok_only), doc_path{file_path} {
constexpr int info_width = 600;
constexpr int info_height = 400;
info_text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(info_width, info_height), wxTE_MULTILINE | wxTE_READONLY);
Expand Down
1 change: 0 additions & 1 deletion app/dialogs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ class document_info_dialog : public dialog {

private:
wxTextCtrl* info_text_ctrl{nullptr};
config_manager& config_mgr;
wxString doc_path;
};

Expand Down
30 changes: 2 additions & 28 deletions app/document_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,12 @@ bool supports_feature(uint32_t flags, uint32_t feature) {
return (flags & feature) != 0;
}

constexpr uint32_t PARSER_SUPPORTS_SECTIONS = 1 << 0;
constexpr uint32_t PARSER_SUPPORTS_TOC = 1 << 1;
constexpr uint32_t PARSER_SUPPORTS_PAGES = 1 << 2;
constexpr uint32_t PARSER_SUPPORTS_LISTS = 1 << 3;

int to_rust_marker(marker_type type) {
return static_cast<int>(type);
}

std::vector<long> to_long_vector(const rust::Vec<long long>& values) {
std::vector<long> result(values.size());
std::transform(values.begin(), values.end(), result.begin(), [](long long value) {
return static_cast<long>(value);
});
return result;
}

rust::Vec<long long> to_rust_history(const std::vector<long>& history) {
rust::Vec<long long> rust_history;
rust_history.reserve(history.size());
std::transform(history.begin(), history.end(), std::back_inserter(rust_history), [](long value) {
return static_cast<long long>(value);
});
return rust_history;
}

void populate_toc_items(std::vector<std::unique_ptr<toc_item>>& toc_items, const rust::Vec<FfiTocItemWithParent>& ffi_toc_items) {
if (ffi_toc_items.empty()) return;
std::vector<toc_item*> item_ptrs;
Expand Down Expand Up @@ -92,12 +72,6 @@ void populate_toc_items(std::vector<std::unique_ptr<toc_item>>& toc_items, const
}
}

void ensure_toc_loaded(session_document& session_doc) {
if (session_doc.toc_loaded) return;
session_doc.toc_loaded = true;
const DocumentHandle& handle = session_doc.get_handle();
populate_toc_items(session_doc.toc_items, document_toc_items_with_parents(handle));
}
} // namespace

void session_document::ensure_toc_loaded() {
Expand Down Expand Up @@ -184,9 +158,9 @@ bool document_manager::create_document_tab(const wxString& path, bool set_focus,
size_t history_index = 0;
config.get_navigation_history(path, history, history_index);
if (!history.empty()) {
rust::Vec<long long> rust_history;
rust::Vec<std::int64_t> rust_history;
rust_history.reserve(history.size());
for (long pos : history) rust_history.push_back(static_cast<long long>(pos));
for (long pos : history) rust_history.push_back(static_cast<std::int64_t>(pos));
rust::Slice<const std::int64_t> history_slice(rust_history.data(), rust_history.size());
session_set_history(*session, history_slice, history_index);
}
Expand Down
Loading