├── doc ├── .gitignore ├── Doxyfile ├── meson.build └── man │ └── conf.py ├── tests ├── .gitignore ├── zathurarc ├── test_utils.c ├── weston_session.sh ├── test_document.c ├── test_types.c ├── test_session.c ├── tests.h ├── test_sandbox.c └── meson.build ├── po ├── .gitignore ├── meson.build ├── LINGUAS └── POTFILES ├── subprojects └── girara.wrap ├── .github ├── dependabot.yml └── workflows │ └── build.yaml ├── .tx └── config ├── zathura ├── print.h ├── landlock.h ├── version.h.in ├── seccomp-filters.h ├── config.h ├── macros.h ├── internal.h ├── content-type.h ├── synctex.h ├── file-monitor-noop.c ├── file-monitor-glib.h ├── file-monitor-noop.h ├── file-monitor-signal.h ├── completion.h ├── database-null.h ├── database-sqlite.h ├── marks.h ├── bookmarks.h ├── dbus-interface.h ├── links.h ├── file-monitor-signal.c ├── file-monitor.h ├── file-monitor-glib.c ├── adjustment.h ├── database.c ├── page-widget.h ├── database-null.c ├── jumplist.h ├── plugin.h ├── types.c ├── document-widget.h ├── bookmarks.c ├── landlock.c ├── file-monitor.c ├── content-type.c ├── adjustment.c ├── commands.h ├── utils.h ├── jumplist.c ├── database.h ├── print.c ├── page.h ├── render.h ├── main-sandbox.c ├── marks.c ├── plugin-api.h └── links.c ├── data ├── zathura.gresource.xml ├── icon-16 │ └── meson.build ├── icon-32 │ └── meson.build ├── icon-64 │ └── meson.build ├── icon-128 │ └── meson.build ├── icon-256 │ └── meson.build ├── zathura.css_t ├── org.pwmt.zathura.desktop.in ├── bash-completion.in ├── tex_zathurasynctex.vim ├── org.pwmt.zathura.svg ├── zsh-completion.in ├── fish-completion.in ├── org.pwmt.zathura.xml └── meson.build ├── .clang-format ├── .gitignore ├── meson_options.txt ├── LICENSE ├── AUTHORS ├── README.md └── meson.build /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | tests 2 | tests-gcov 3 | tests-debug 4 | -------------------------------------------------------------------------------- /po/.gitignore: -------------------------------------------------------------------------------- 1 | POTFILES.in* 2 | *.mo 3 | org.pwmt.zathura.pot 4 | -------------------------------------------------------------------------------- /tests/zathurarc: -------------------------------------------------------------------------------- 1 | # do not use database for tests 2 | set database null 3 | 4 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n = import('i18n') 2 | i18n.gettext('org.pwmt.zathura', 3 | preset: 'glib' 4 | ) 5 | -------------------------------------------------------------------------------- /subprojects/girara.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=girara 3 | url=https://github.com/pwmt/girara.git 4 | revision=develop 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | ar 2 | ca 3 | cs 4 | de 5 | el 6 | eo 7 | es 8 | es_CL 9 | et 10 | fr 11 | he 12 | id_ID 13 | it 14 | lt 15 | nl 16 | no 17 | pl 18 | pt_BR 19 | ru 20 | sv 21 | ta_IN 22 | tr 23 | uk_UA 24 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [o:pwmt:p:zathura:r:zathura] 5 | file_filter = po/.po 6 | source_file = po/org.pwmt.zathura.pot 7 | source_lang = en 8 | replace_edited_strings = false 9 | keep_translations = false 10 | 11 | -------------------------------------------------------------------------------- /zathura/print.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef PRINT_H 4 | #define PRINT_H 5 | 6 | #include "zathura.h" 7 | 8 | /** 9 | * Opens a print dialog to print the current file 10 | * 11 | * @param zathura 12 | */ 13 | void print(zathura_t* zathura); 14 | 15 | #endif // PRINT_H 16 | -------------------------------------------------------------------------------- /zathura/landlock.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_LANDLOCK_H 4 | #define ZATHURA_LANDLOCK_H 5 | 6 | /* 7 | ** Remove write and execute permissions 8 | */ 9 | void landlock_drop_write(void); 10 | 11 | /* 12 | ** Restrict write permissions to XDG_DATA 13 | */ 14 | void landlock_restrict_write(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /data/zathura.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | zathura.css_t 5 | 6 | 7 | org.pwmt.zathura.xml 8 | 9 | 10 | -------------------------------------------------------------------------------- /data/icon-16/meson.build: -------------------------------------------------------------------------------- 1 | custom_target('org.pwmt.zathura_16.png', 2 | input: '../org.pwmt.zathura.svg', 3 | output: 'org.pwmt.zathura.png', 4 | command: [ 5 | rsvg_convert, 6 | '-w', '16', '-h', '16', 7 | '-o', '@OUTPUT@', 8 | '@INPUT@' 9 | ], 10 | install: true, 11 | install_dir: join_paths(datadir, 'icons', 'hicolor', '16x16', 'apps') 12 | ) 13 | -------------------------------------------------------------------------------- /data/icon-32/meson.build: -------------------------------------------------------------------------------- 1 | custom_target('org.pwmt.zathura_32.png', 2 | input: '../org.pwmt.zathura.svg', 3 | output: 'org.pwmt.zathura.png', 4 | command: [ 5 | rsvg_convert, 6 | '-w', '32', '-h', '32', 7 | '-o', '@OUTPUT@', 8 | '@INPUT@' 9 | ], 10 | install: true, 11 | install_dir: join_paths(datadir, 'icons', 'hicolor', '32x32', 'apps') 12 | ) 13 | -------------------------------------------------------------------------------- /data/icon-64/meson.build: -------------------------------------------------------------------------------- 1 | custom_target('org.pwmt.zathura_64.png', 2 | input: '../org.pwmt.zathura.svg', 3 | output: 'org.pwmt.zathura.png', 4 | command: [ 5 | rsvg_convert, 6 | '-w', '64', '-h', '64', 7 | '-o', '@OUTPUT@', 8 | '@INPUT@' 9 | ], 10 | install: true, 11 | install_dir: join_paths(datadir, 'icons', 'hicolor', '64x64', 'apps') 12 | ) 13 | -------------------------------------------------------------------------------- /data/icon-128/meson.build: -------------------------------------------------------------------------------- 1 | custom_target('org.pwmt.zathura_128.png', 2 | input: '../org.pwmt.zathura.svg', 3 | output: 'org.pwmt.zathura.png', 4 | command: [ 5 | rsvg_convert, 6 | '-w', '128', '-h', '128', 7 | '-o', '@OUTPUT@', 8 | '@INPUT@' 9 | ], 10 | install: true, 11 | install_dir: join_paths(datadir, 'icons', 'hicolor', '128x128', 'apps') 12 | ) 13 | -------------------------------------------------------------------------------- /data/icon-256/meson.build: -------------------------------------------------------------------------------- 1 | custom_target('org.pwmt.zathura_256.png', 2 | input: '../org.pwmt.zathura.svg', 3 | output: 'org.pwmt.zathura.png', 4 | command: [ 5 | rsvg_convert, 6 | '-w', '256', '-h', '256', 7 | '-o', '@OUTPUT@', 8 | '@INPUT@' 9 | ], 10 | install: true, 11 | install_dir: join_paths(datadir, 'icons', 'hicolor', '256x256', 'apps') 12 | ) 13 | -------------------------------------------------------------------------------- /zathura/version.h.in: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_VERSION_H 4 | #define ZATHURA_VERSION_H 5 | 6 | #define ZATHURA_VERSION_MAJOR @ZVMAJOR@ 7 | #define ZATHURA_VERSION_MINOR @ZVMINOR@ 8 | #define ZATHURA_VERSION_REV @ZVREV@ 9 | #define ZATHURA_VERSION "@version@" 10 | #define ZATHURA_API_VERSION @ZVAPI@ 11 | #define ZATHURA_ABI_VERSION @ZVABI@ 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /zathura/seccomp-filters.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_SECCOMP_FILTERS_H 4 | #define ZATHURA_SECCOMP_FILTERS_H 5 | 6 | #include "zathura.h" 7 | 8 | /* strict filter before document parsing */ 9 | /* this filter is to be enabled after most of the initialisation of zathura has finished */ 10 | int seccomp_enable_strict_filter(zathura_t* zathura); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AlignConsecutiveAssignments: true 5 | AllowShortFunctionsOnASingleLine: Empty 6 | AllowShortIfStatementsOnASingleLine: false 7 | AlwaysBreakTemplateDeclarations: true 8 | BreakBeforeBraces: Attach 9 | ConstructorInitializerIndentWidth: 2 10 | NamespaceIndentation: All 11 | PointerAlignment: Left 12 | TabWidth: 2 13 | ColumnLimit: 120 14 | SortIncludes: false 15 | ... 16 | -------------------------------------------------------------------------------- /data/zathura.css_t: -------------------------------------------------------------------------------- 1 | /* Index mode colors */ 2 | 3 | #@session@ .indexmode { 4 | color: @index-fg@; 5 | background-color: @index-bg@; 6 | } 7 | 8 | #@session@ .indexmode:selected { 9 | color: @index-active-fg@; 10 | background-color: @index-active-bg@; 11 | } 12 | 13 | /* Scrollbar */ 14 | #@session@ scrolledwindow scrollbar { 15 | background-color: @scrollbar-bg@; 16 | } 17 | 18 | #@session@ scrolledwindow scrollbar > slider { 19 | background-color: @scrollbar-fg@; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /data/org.pwmt.zathura.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Name=Zathura 5 | Comment=Minimalistic document viewer 6 | Exec=zathura %U 7 | # Translators: Icon of the desktop entry. 8 | Icon=org.pwmt.zathura 9 | Terminal=false 10 | Categories=Office;Viewer; 11 | # Translators: Search terms to find this application. Do not translate or 12 | # localize the semicolons. The list must also end with a semicolon. 13 | Keywords=PDF;PS;PostScript;DjVU;document;presentation;viewer; 14 | -------------------------------------------------------------------------------- /tests/test_utils.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "utils.h" 4 | 5 | static void test_file_valid_extension(void) { 6 | g_assert_false(file_valid_extension(NULL, NULL)); 7 | g_assert_false(file_valid_extension((void*)0xDEAD, NULL)); 8 | g_assert_false(file_valid_extension(NULL, "pdf")); 9 | } 10 | 11 | int main(int argc, char* argv[]) { 12 | g_test_init(&argc, &argv, NULL); 13 | g_test_add_func("/utils/file_valid_extension", test_file_valid_extension); 14 | return g_test_run(); 15 | } 16 | -------------------------------------------------------------------------------- /zathura/config.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef CONFIG_H 4 | #define CONFIG_H 5 | 6 | #include "zathura.h" 7 | 8 | /** 9 | * This function loads the default values of the configuration 10 | * 11 | * @param zathura The zathura session 12 | */ 13 | void config_load_default(zathura_t* zathura); 14 | 15 | /** 16 | * Loads and evaluates a configuration file 17 | * 18 | * @param zathura The zathura session 19 | */ 20 | void config_load_files(zathura_t* zathura); 21 | 22 | #endif // CONFIG_H 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build files 2 | *.o 3 | *.do 4 | *.gcda 5 | *.gcno 6 | *.info 7 | *.pc 8 | *.sw[a-z] 9 | *.pyc 10 | tags 11 | 12 | # generated files 13 | resources.* 14 | zathura.1 15 | zathurarc.5 16 | 17 | # dist files 18 | zathura-*.tar.gz 19 | 20 | # patch files 21 | *.diff 22 | *.patch 23 | 24 | # build dirs 25 | build/ 26 | gcov/ 27 | subprojects/girara 28 | 29 | # development files 30 | .clang_complete 31 | .lvimrc 32 | .ropeproject 33 | .frama-c 34 | compile_commands.json 35 | *.log 36 | .ycm_extra_conf.py 37 | _*/ 38 | -------------------------------------------------------------------------------- /tests/weston_session.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export XDG_RUNTIME_DIR=$(mktemp -d) 4 | mkdir -p "$XDG_RUNTIME_DIR" 5 | 6 | weston --backend=headless-backend.so --socket=zathura-test-weston --idle-time=0 & 7 | WESTON_PID=$! 8 | 9 | # Wait for the socket to exist 10 | for i in $(seq 10); do 11 | [ -e "$XDG_RUNTIME_DIR/zathura-test-weston" ] && break 12 | sleep 0.5 13 | done 14 | 15 | # run tests 16 | $@ 17 | RET=$? 18 | 19 | # Clean up Weston 20 | kill $WESTON_PID 21 | wait $WESTON_PID 22 | 23 | rm -rf "$XDG_RUNTIME_DIR" 24 | exit $RET 25 | -------------------------------------------------------------------------------- /zathura/macros.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_MACROS_H 4 | #define ZATHURA_MACROS_H 5 | 6 | #include 7 | 8 | #define UNUSED(x) GIRARA_UNUSED(x) 9 | #define DEPRECATED(x) GIRARA_DEPRECATED(x) 10 | #define ZATHURA_PLUGIN_API GIRARA_VISIBLE 11 | 12 | #ifndef MIN 13 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 14 | #endif 15 | 16 | #ifndef MAX 17 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 18 | #endif 19 | 20 | #define checked_umul(lhs, rhs, res) __builtin_umul_overflow((lhs), (rhs), (res)) 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /tests/test_document.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "document.h" 4 | 5 | static void test_open(void) { 6 | g_assert_null(zathura_document_open(NULL, NULL, NULL, NULL, NULL)); 7 | g_assert_null(zathura_document_open(NULL, "fl", NULL, NULL, NULL)); 8 | g_assert_null(zathura_document_open(NULL, "fl", "ur", NULL, NULL)); 9 | g_assert_null(zathura_document_open(NULL, "fl", NULL, "pw", NULL)); 10 | } 11 | 12 | int main(int argc, char* argv[]) { 13 | g_test_init(&argc, &argv, NULL); 14 | g_test_add_func("/document/open", test_open); 15 | return g_test_run(); 16 | } 17 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('synctex', 2 | type: 'feature', 3 | value: 'auto', 4 | description: 'SyncTeX integration' 5 | ) 6 | option('seccomp', 7 | type: 'feature', 8 | value: 'auto', 9 | description: 'seccomp-based sandbox' 10 | ) 11 | option('landlock', 12 | type: 'feature', 13 | value: 'auto', 14 | description: 'landlock-based sandbox' 15 | ) 16 | option('manpages', 17 | type: 'feature', 18 | value: 'auto', 19 | description: 'manual pages' 20 | ) 21 | option('tests', 22 | type: 'feature', 23 | value: 'auto', 24 | description: 'run tests' 25 | ) 26 | option('convert-icon', 27 | type: 'feature', 28 | value: 'auto', 29 | description: 'convert icon to PNG' 30 | ) 31 | -------------------------------------------------------------------------------- /tests/test_types.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | 5 | #include "types.h" 6 | 7 | static void test_image_buffer_fail(void) { 8 | g_assert_null(zathura_image_buffer_create(UINT_MAX, UINT_MAX)); 9 | } 10 | 11 | static void test_image_buffer(void) { 12 | zathura_image_buffer_t* buffer = zathura_image_buffer_create(1, 1); 13 | g_assert_nonnull(buffer); 14 | zathura_image_buffer_free(buffer); 15 | } 16 | 17 | int main(int argc, char* argv[]) { 18 | g_test_init(&argc, &argv, NULL); 19 | g_test_add_func("/types/image_buffer_fail", test_image_buffer_fail); 20 | g_test_add_func("/types/image_buffer", test_image_buffer); 21 | return g_test_run(); 22 | } 23 | -------------------------------------------------------------------------------- /data/bash-completion.in: -------------------------------------------------------------------------------- 1 | _zathura() { 2 | _init_completion 2>/dev/null || true 3 | 4 | local EXTS="" 5 | for PLUGIN in @PLUGINDIR@/lib*.so; do 6 | case ${PLUGIN##*/} in 7 | libpdf-poppler.so) 8 | EXTS="$EXTS|pdf|PDF" 9 | ;; 10 | libpdf-mupdf.so) 11 | EXTS="$EXTS|pdf|PDF|epub|oxps|xhtml" 12 | ;; 13 | libps.so) 14 | EXTS="$EXTS|ps|eps|epsi|epsf" 15 | ;; 16 | libdjvu.so) 17 | EXTS="$EXTS|djvu|djv" 18 | ;; 19 | libcb.so) 20 | EXTS="$EXTS|cb7|cbr|cbz|cbt|rar|zip|7z|tar" 21 | ;; 22 | esac 23 | done 24 | 25 | _filedir "${EXTS#|}" 2>/dev/null || COMPREPLY=($(shopt -s extglob; compgen -f -X "!*.@($EXTS)")) 26 | } 27 | complete -F _zathura zathura 28 | -------------------------------------------------------------------------------- /tests/test_session.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | 5 | #include "zathura.h" 6 | 7 | #include "tests.h" 8 | 9 | static void test_create(void) { 10 | setup_logger(); 11 | girara_set_log_level(GIRARA_ERROR); 12 | 13 | zathura_t* zathura = zathura_create(); 14 | g_assert_nonnull(zathura); 15 | g_assert_nonnull(g_getenv("G_TEST_SRCDIR")); 16 | zathura_set_config_dir(zathura, g_getenv("G_TEST_SRCDIR")); 17 | g_assert_true(zathura_init(zathura)); 18 | zathura_free(zathura); 19 | } 20 | 21 | int main(int argc, char* argv[]) { 22 | setup_logger(); 23 | 24 | gtk_init(NULL, NULL); 25 | g_test_init(&argc, &argv, NULL); 26 | g_test_add_func("/session/create", test_create); 27 | return g_test_run(); 28 | } 29 | -------------------------------------------------------------------------------- /doc/Doxyfile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Zlib 2 | 3 | # General information 4 | PROJECT_NAME = zathura 5 | OUTPUT_DIRECTORY = ./_build/doxygen/ 6 | OUTPUT_LANGUAGE = English 7 | TAB_SIZE = 2 8 | EXTRACT_ALL = YES 9 | OPTIMIZE_OUTPUT_FOR_C = YES 10 | DOXYFILE_ENCODING = UTF-8 11 | TYPEDEF_HIDES_STRUCT = YES 12 | 13 | # Warning and progress messages 14 | QUIET = YES 15 | WARNINGS = YES 16 | WARN_IF_UNDOCUMENTED = YES 17 | 18 | # Input files 19 | INPUT = ../ 20 | EXCLUDE = ./tests ./doc 21 | FILE_PATTERNS = *.h *.c 22 | RECURSIVE = YES 23 | 24 | # Output files 25 | GENERATE_HTML = NO 26 | GENERATE_LATEX = NO 27 | GENERATE_RTF = NO 28 | GENERATE_XML = YES 29 | 30 | SOURCE_BROWSER = YES 31 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | data/org.pwmt.zathura.metainfo.xml.in 2 | data/org.pwmt.zathura.desktop.in 3 | zathura/adjustment.c 4 | zathura/bookmarks.c 5 | zathura/callbacks.c 6 | zathura/commands.c 7 | zathura/completion.c 8 | zathura/config.c 9 | zathura/content-type.c 10 | zathura/database-sqlite.c 11 | zathura/database.c 12 | zathura/dbus-interface.c 13 | zathura/document-widget.c 14 | zathura/document.c 15 | zathura/file-monitor-glib.c 16 | zathura/file-monitor-signal.c 17 | zathura/file-monitor.c 18 | zathura/jumplist.c 19 | zathura/links.c 20 | zathura/main.c 21 | zathura/marks.c 22 | zathura/page-widget.c 23 | zathura/page.c 24 | zathura/plugin.c 25 | zathura/print.c 26 | zathura/render.c 27 | zathura/seccomp-filters.c 28 | zathura/shortcuts.c 29 | zathura/synctex.c 30 | zathura/types.c 31 | zathura/utils.c 32 | zathura/zathura.c 33 | -------------------------------------------------------------------------------- /tests/tests.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef GIRARA_TESTS_H 4 | #define GIRARA_TESTS_H 5 | 6 | static void glog_handler(const gchar* GIRARA_UNUSED(log_domain), GLogLevelFlags GIRARA_UNUSED(log_level), 7 | const gchar* GIRARA_UNUSED(message), gpointer GIRARA_UNUSED(user_data)) {} 8 | 9 | static gboolean ignore_all_log_errors(const gchar* GIRARA_UNUSED(log_domain), GLogLevelFlags GIRARA_UNUSED(log_level), 10 | const gchar* GIRARA_UNUSED(message), gpointer GIRARA_UNUSED(user_data)) { 11 | return FALSE; 12 | } 13 | 14 | static void setup_logger(void) { 15 | g_test_log_set_fatal_handler(ignore_all_log_errors, NULL); 16 | g_log_set_handler(NULL, G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL | G_LOG_LEVEL_ERROR, glog_handler, NULL); 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /data/tex_zathurasynctex.vim: -------------------------------------------------------------------------------- 1 | " SPDX-License-Identifier: Zlib 2 | 3 | " This is a sample plugin that can be used for synctex forward synchronization. 4 | " It currently uses latexsuite to obtain the file name of the document. If you 5 | " are not using latexsuite, it should be enough to adopt the calculation of 6 | " 'output' accordingly. 7 | 8 | " avoid re-execution 9 | if exists("b:did_zathura_synctex_plugin") || !exists("*Tex_GetMainFileName") 10 | finish 11 | endif 12 | let b:did_zathura_synctex_plugin = 1 13 | 14 | function! Zathura_SyncTexForward() 15 | let source = expand("%:p") 16 | let input = shellescape(line(".").":".col(".").":".source) 17 | let output = Tex_GetMainFileName(":p:r").".pdf" 18 | let execstr = "zathura --synctex-forward=".input." ".shellescape(output) 19 | silent call system(execstr) 20 | endfunction 21 | 22 | nmap f :call Zathura_SyncTexForward() 23 | -------------------------------------------------------------------------------- /zathura/internal.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef INTERNAL_H 4 | #define INTERNAL_H 5 | 6 | #include "zathura.h" 7 | #include "plugin.h" 8 | 9 | /** 10 | * Zathura password dialog 11 | */ 12 | typedef struct zathura_password_dialog_info_s { 13 | char* path; /**< Path to the file */ 14 | char* uri; /**< URI to the file */ 15 | zathura_t* zathura; /**< Zathura session */ 16 | } zathura_password_dialog_info_t; 17 | 18 | struct zathura_document_information_entry_s { 19 | zathura_document_information_type_t type; /**< Type of the information */ 20 | char* value; /**< Value */ 21 | }; 22 | 23 | /** 24 | * Returns the associated plugin 25 | * 26 | * @param document The document 27 | * @return The plugin or NULL 28 | */ 29 | const zathura_plugin_t* zathura_document_get_plugin(zathura_document_t* document); 30 | 31 | #endif // INTERNAL_H 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2020 pwmt.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | -------------------------------------------------------------------------------- /doc/meson.build: -------------------------------------------------------------------------------- 1 | sphinx = find_program('sphinx-build', required: get_option('manpages')) 2 | if sphinx.found() 3 | custom_target('man pages', 4 | command: [ 5 | sphinx, 6 | '-b', 'man', 7 | '-D', 'version=' + version, 8 | '-D', 'release=' + version, 9 | join_paths(meson.current_source_dir(), 'man'), 10 | meson.current_build_dir()], 11 | output: ['zathura.1', 'zathurarc.5'], 12 | input: [ 13 | 'man/conf.py', 14 | 'man/zathurarc.5.rst', 15 | 'man/zathura.1.rst' 16 | ], 17 | build_by_default: true, 18 | install: true, 19 | install_dir: [ 20 | join_paths(get_option('mandir'), 'man1'), 21 | join_paths(get_option('mandir'), 'man5') 22 | ] 23 | ) 24 | if seccomp.found() or landlock 25 | install_symlink('zathura-sandbox.1', 26 | install_dir: join_paths(get_option('mandir'), 'man1'), 27 | pointing_to: 'zathura.1' 28 | ) 29 | endif 30 | endif 31 | -------------------------------------------------------------------------------- /zathura/content-type.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_CONTENT_TYPE_H 4 | #define ZATHURA_CONTENT_TYPE_H 5 | 6 | #include 7 | 8 | typedef struct zathura_content_type_context_s zathura_content_type_context_t; 9 | 10 | /** 11 | * Create new context for content type detection. 12 | * 13 | * @return new context 14 | */ 15 | zathura_content_type_context_t* zathura_content_type_new(void); 16 | 17 | /** 18 | * Free content type detection context. 19 | * 20 | * @param context The context. 21 | */ 22 | void zathura_content_type_free(zathura_content_type_context_t* context); 23 | 24 | /** 25 | * "Guess" the content type of a file. Various methods are tried depending on 26 | * the available libraries. 27 | * 28 | * @param path file name 29 | * @return content type of path, needs to freeed with g_free. 30 | */ 31 | char* zathura_content_type_guess(zathura_content_type_context_t* context, const char* path, 32 | const girara_list_t* supported_content_types); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /doc/man/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # SPDX-License-Identifier: Zlib 4 | 5 | import os.path 6 | import glob 7 | import time 8 | 9 | dirname = os.path.dirname(__file__) 10 | files = glob.glob(os.path.join(dirname, '*.rst')) 11 | 12 | maxdate = 0 13 | for path in files: 14 | s = os.stat(path) 15 | maxdate = max(maxdate, s.st_mtime) 16 | 17 | # -- General configuration ------------------------------------------------ 18 | 19 | source_suffix = '.rst' 20 | master_doc = 'zathura.1' 21 | templates_path = ['_templates'] 22 | today = time.strftime('%Y-%m-%d', time.gmtime(maxdate)) 23 | 24 | # -- Project configuration ------------------------------------------------ 25 | 26 | project = 'zathura' 27 | copyright = '2009-2018, pwmt.org' 28 | version = '0.2.7' 29 | release = '0.2.7' 30 | 31 | # -- Options for manual page output --------------------------------------- 32 | 33 | man_pages = [ 34 | ('zathura.1', 'zathura', 'a document viewer', ['pwmt.org'], 1), 35 | ('zathurarc.5', 'zathurarc', 'zathura configuration file', ['pwmt.org'], 5) 36 | ] 37 | -------------------------------------------------------------------------------- /data/org.pwmt.zathura.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | zathura is written by: 2 | 3 | Moritz Lipp 4 | Sebastian Ramacher 5 | 6 | Other contributors are (in no particular order): 7 | 8 | Aepelzen 9 | Pavel Borzenkov 10 | Géraud Le Falher 11 | Glen Winters 12 | Ivan Sichmann Freitas 13 | Felix Herrmann 14 | int3 15 | karottenreibe 16 | Johannes Meng 17 | J. Commelin 18 | Julian Orth 19 | Roland Schatz 20 | Abdó Roig-Maranges 21 | Benoît Knecht 22 | Rob Cornish 23 | Marwan Tanager 24 | Diego Joss 25 | Ignas Anikevicius 26 | Kamil Smardzewski 27 | oblique 28 | Maxime Chéramy 29 | Alexander Shabalin 30 | Lingzhu Xiang 31 | Jeremie Knuesel 32 | -------------------------------------------------------------------------------- /zathura/synctex.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef SYNCTEX_H 4 | #define SYNCTEX_H 5 | 6 | #include "types.h" 7 | 8 | typedef struct synctex_page_rect_s { 9 | int page; 10 | zathura_rectangle_t rect; 11 | } synctex_page_rect_t; 12 | 13 | bool synctex_get_input_line_column(zathura_t* zathura, const char* filename, unsigned int page, int x, int y, char** input_file, 14 | unsigned int* line, unsigned int* column); 15 | 16 | void synctex_edit(zathura_t* zathura, const char* editor, zathura_page_t* page, int x, int y); 17 | 18 | bool synctex_parse_input(const char* synctex, char** input_file, int* line, int* column); 19 | 20 | girara_list_t* synctex_rectangles_from_position(zathura_t* zathura, const char* filename, const char* input_file, int line, int column, 21 | unsigned int* page, girara_list_t** secondary_rects); 22 | 23 | void synctex_highlight_rects(zathura_t* zathura, unsigned int page, girara_list_t** rectangles); 24 | 25 | bool synctex_view(zathura_t* zathura, const char* input_file, unsigned int line, unsigned int column); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /zathura/file-monitor-noop.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "file-monitor-noop.h" 4 | 5 | #include 6 | #ifdef G_OS_UNIX 7 | #include 8 | #endif 9 | 10 | struct zathura_noopfilemonitor_s { 11 | ZathuraFileMonitor parent; 12 | }; 13 | 14 | G_DEFINE_TYPE(ZathuraNoopFileMonitor, zathura_noopfilemonitor, ZATHURA_TYPE_FILEMONITOR) 15 | 16 | static void start(ZathuraFileMonitor* GIRARA_UNUSED(file_monitor)) {} 17 | 18 | static void stop(ZathuraFileMonitor* GIRARA_UNUSED(file_monitor)) {} 19 | 20 | static void zathura_noopfilemonitor_finalize(GObject* object) { 21 | stop(ZATHURA_FILEMONITOR(object)); 22 | 23 | G_OBJECT_CLASS(zathura_noopfilemonitor_parent_class)->finalize(object); 24 | } 25 | 26 | static void zathura_noopfilemonitor_class_init(ZathuraNoopFileMonitorClass* class) { 27 | ZathuraFileMonitorClass* filemonitor_class = ZATHURA_FILEMONITOR_CLASS(class); 28 | filemonitor_class->start = start; 29 | filemonitor_class->stop = stop; 30 | 31 | GObjectClass* object_class = G_OBJECT_CLASS(class); 32 | object_class->finalize = zathura_noopfilemonitor_finalize; 33 | } 34 | 35 | static void zathura_noopfilemonitor_init(ZathuraNoopFileMonitor* GIRARA_UNUSED(noopfilemonitor)) {} 36 | -------------------------------------------------------------------------------- /zathura/file-monitor-glib.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef FILEMONITOR_GLIB_H 4 | #define FILEMONITOR_GLIB_H 5 | 6 | #include "file-monitor.h" 7 | 8 | #define ZATHURA_TYPE_GLIBFILEMONITOR (zathura_glibfilemonitor_get_type()) 9 | #define ZATHURA_GLIBFILEMONITOR(obj) \ 10 | (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_GLIBFILEMONITOR, ZathuraGLibFileMonitor)) 11 | #define ZATHURA_GLIBFILEMONITOR_CLASS(obj) \ 12 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_GLIBFILEMONITOR, ZathuraGLibFileMonitorClass)) 13 | #define ZATHURA_IS_GLIBFILEMONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_GLIBFILEMONITOR)) 14 | #define ZATHURA_IS_GLIBFILEMONITOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_GLIBFILEMONITOR)) 15 | #define ZATHURA_GLIBFILEMONITOR_GET_CLASS(obj) \ 16 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_GLIBFILEMONITOR, ZathuraGLibFileMonitorClass)) 17 | 18 | typedef struct zathura_glibfilemonitor_s ZathuraGLibFileMonitor; 19 | typedef struct zathura_glibfilemonitor_class_s ZathuraGLibFileMonitorClass; 20 | 21 | struct zathura_glibfilemonitor_class_s { 22 | ZathuraFileMonitorClass parent_class; 23 | }; 24 | 25 | GType zathura_glibfilemonitor_get_type(void) G_GNUC_CONST; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /zathura/file-monitor-noop.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef FILEMONITOR_NOOP_H 4 | #define FILEMONITOR_NOOP_H 5 | 6 | #include "file-monitor.h" 7 | 8 | #define ZATHURA_TYPE_NOOPFILEMONITOR (zathura_noopfilemonitor_get_type()) 9 | #define ZATHURA_NOOPFILEMONITOR(obj) \ 10 | (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_NOOPFILEMONITOR, ZathuraNoopFileMonitor)) 11 | #define ZATHURA_NOOPFILEMONITOR_CLASS(obj) \ 12 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_NOOPFILEMONITOR, ZathuraNoopFileMonitorClass)) 13 | #define ZATHURA_IS_NOOPFILEMONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_NOOPFILEMONITOR)) 14 | #define ZATHURA_IS_NOOPFILEMONITOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_NOOPFILEMONITOR)) 15 | #define ZATHURA_NOOPFILEMONITOR_GET_CLASS(obj) \ 16 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_NOOPFILEMONITOR, ZathuraNoopFileMonitorClass)) 17 | 18 | typedef struct zathura_noopfilemonitor_s ZathuraNoopFileMonitor; 19 | typedef struct zathura_noopfilemonitor_class_s ZathuraNoopFileMonitorClass; 20 | 21 | struct zathura_noopfilemonitor_class_s { 22 | ZathuraFileMonitorClass parent_class; 23 | }; 24 | 25 | GType zathura_noopfilemonitor_get_type(void) G_GNUC_CONST; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /zathura/file-monitor-signal.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef FILEMONITOR_SIGNAL_H 4 | #define FILEMONITOR_SIGNAL_H 5 | 6 | #include "file-monitor.h" 7 | 8 | #define ZATHURA_TYPE_SIGNALFILEMONITOR (zathura_signalfilemonitor_get_type()) 9 | #define ZATHURA_SIGNALFILEMONITOR(obj) \ 10 | (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_SIGNALFILEMONITOR, ZathuraSignalFileMonitor)) 11 | #define ZATHURA_SIGNALFILEMONITOR_CLASS(obj) \ 12 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_SIGNALFILEMONITOR, ZathuraSignalFileMonitorClass)) 13 | #define ZATHURA_IS_SIGNALFILEMONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_SIGNALFILEMONITOR)) 14 | #define ZATHURA_IS_SIGNALFILEMONITOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_SIGNALFILEMONITOR)) 15 | #define ZATHURA_SIGNALFILEMONITOR_GET_CLASS(obj) \ 16 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_SIGNALFILEMONITOR, ZathuraSignalFileMonitorClass)) 17 | 18 | typedef struct zathura_signalfilemonitor_s ZathuraSignalFileMonitor; 19 | typedef struct zathura_signalfilemonitor_class_s ZathuraSignalFileMonitorClass; 20 | 21 | struct zathura_signalfilemonitor_class_s { 22 | ZathuraFileMonitorClass parent_class; 23 | }; 24 | 25 | GType zathura_signalfilemonitor_get_type(void) G_GNUC_CONST; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /zathura/completion.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef COMPLETION_H 4 | #define COMPLETION_H 5 | 6 | #include 7 | 8 | /** 9 | * Completion for the open command - Creates a list of accesible directories or 10 | * files 11 | * 12 | * @param session The used girara session 13 | * @param input The current input 14 | * @return The completion object or NULL if an error occurred 15 | */ 16 | girara_completion_t* cc_open(girara_session_t* session, const char* input); 17 | 18 | /** 19 | * Completion for the write command - Creates a list of accesible directories or 20 | * files 21 | * 22 | * @param session The used girara session 23 | * @param input The current input 24 | * @return The completion object or NULL if an error occurred 25 | */ 26 | girara_completion_t* cc_write(girara_session_t* session, const char* input); 27 | 28 | /** 29 | * Completion for the bmarks command - Creates a list of bookmarks 30 | * 31 | * @param session The used girara session 32 | * @param input The current input 33 | * @return The completion object or NULL if an error occurred 34 | */ 35 | girara_completion_t* cc_bookmarks(girara_session_t* session, const char* input); 36 | 37 | /** 38 | * Completion for the export command - Creates a list of attachments 39 | * 40 | * @param session the girara session 41 | * @param input the current input 42 | * @return completion object, NULL on error 43 | */ 44 | girara_completion_t* cc_export(girara_session_t* session, const char* input); 45 | 46 | #endif // COMPLETION_H 47 | -------------------------------------------------------------------------------- /tests/test_sandbox.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "zathura.h" 4 | #include "document.h" 5 | #ifdef WITH_SECCOMP 6 | #include "seccomp-filters.h" 7 | #endif 8 | #ifdef WITH_LANDLOCK 9 | #include "landlock.h" 10 | #endif 11 | 12 | #include "tests.h" 13 | 14 | static void test_create(void) { 15 | setup_logger(); 16 | 17 | #ifdef GDK_WINDOWING_X11 18 | GdkDisplay* display = gdk_display_get_default(); 19 | 20 | if (GDK_IS_X11_DISPLAY(display)) { 21 | g_test_skip("not running under X11"); 22 | return; 23 | } 24 | #endif 25 | 26 | zathura_t* zathura = zathura_create(); 27 | g_assert_nonnull(zathura); 28 | g_assert_nonnull(g_getenv("G_TEST_SRCDIR")); 29 | zathura_set_config_dir(zathura, g_getenv("G_TEST_SRCDIR")); 30 | g_assert_true(zathura_init(zathura)); 31 | 32 | #ifdef WITH_LANDLOCK 33 | landlock_drop_write(); 34 | #endif 35 | #ifdef WITH_SECCOMP 36 | g_assert_cmpint(seccomp_enable_strict_filter(zathura), ==, 0); 37 | #endif 38 | 39 | g_assert_null(zathura_document_open(zathura, NULL, NULL, NULL, NULL)); 40 | g_assert_null(zathura_document_open(zathura, "fl", NULL, NULL, NULL)); 41 | g_assert_null(zathura_document_open(zathura, "fl", "ur", NULL, NULL)); 42 | g_assert_null(zathura_document_open(zathura, "fl", NULL, "pw", NULL)); 43 | 44 | zathura_free(zathura); 45 | } 46 | 47 | int main(int argc, char* argv[]) { 48 | setup_logger(); 49 | 50 | gtk_init(NULL, NULL); 51 | g_test_init(&argc, &argv, NULL); 52 | g_test_add_func("/sandbox/session_create", test_create); 53 | return g_test_run(); 54 | } 55 | -------------------------------------------------------------------------------- /zathura/database-null.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_DATABASE_NULL_H 4 | #define ZATHURA_DATABASE_NULL_H 5 | 6 | #include "database.h" 7 | 8 | #define ZATHURA_TYPE_NULLDATABASE (zathura_nulldatabase_get_type()) 9 | #define ZATHURA_NULLDATABASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_NULLDATABASE, ZathuraNullDatabase)) 10 | #define ZATHURA_IS_NULLDATABASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_NULLDATABASE)) 11 | #define ZATHURA_NULLDATABASE_CLASS(klass) \ 12 | (G_TYPE_CHECK_CLASS_CAST((klass), ZATHURA_TYPE_NULLDATABASE, ZathuraNullDatabaseClass)) 13 | #define ZATHURA_IS_NULLDATABASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ZATHURA_TYPE_NULLDATABASE)) 14 | #define ZATHURA_NULLDATABASE_GET_CLASS(obj) \ 15 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_NULLDATABASE, ZathuraNullDatabaseClass)) 16 | 17 | typedef struct _ZathuraNullDatabase ZathuraNullDatabase; 18 | typedef struct _ZathuraNullDatabaseClass ZathuraNullDatabaseClass; 19 | 20 | struct _ZathuraNullDatabase { 21 | GObject parent_instance; 22 | }; 23 | 24 | struct _ZathuraNullDatabaseClass { 25 | GObjectClass parent_class; 26 | }; 27 | 28 | GType zathura_nulldatabase_get_type(void) G_GNUC_CONST; 29 | 30 | /** 31 | * Initialize database system. 32 | * 33 | * @return A valid zathura_database_t instance or NULL on failure 34 | */ 35 | zathura_database_t* zathura_nulldatabase_new(void); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /zathura/database-sqlite.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_DATABASE_SQLITE_H 4 | #define ZATHURA_DATABASE_SQLITE_H 5 | 6 | #include "database.h" 7 | 8 | #define ZATHURA_TYPE_SQLDATABASE (zathura_sqldatabase_get_type()) 9 | #define ZATHURA_SQLDATABASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_SQLDATABASE, ZathuraSQLDatabase)) 10 | #define ZATHURA_IS_SQLDATABASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_SQLDATABASE)) 11 | #define ZATHURA_SQLDATABASE_CLASS(klass) \ 12 | (G_TYPE_CHECK_CLASS_CAST((klass), ZATHURA_TYPE_SQLDATABASE, ZathuraSQLDatabaseClass)) 13 | #define ZATHURA_IS_SQLDATABASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ZATHURA_TYPE_SQLDATABASE)) 14 | #define ZATHURA_SQLDATABASE_GET_CLASS(obj) \ 15 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_SQLDATABASE, ZathuraSQLDatabaseClass)) 16 | 17 | typedef struct _ZathuraSQLDatabase ZathuraSQLDatabase; 18 | typedef struct _ZathuraSQLDatabaseClass ZathuraSQLDatabaseClass; 19 | 20 | struct _ZathuraSQLDatabase { 21 | GObject parent_instance; 22 | }; 23 | 24 | struct _ZathuraSQLDatabaseClass { 25 | GObjectClass parent_class; 26 | }; 27 | 28 | GType zathura_sqldatabase_get_type(void) G_GNUC_CONST; 29 | 30 | /** 31 | * Initialize database system. 32 | * 33 | * @param path Path to the sqlite database. 34 | * @return A valid zathura_database_t instance or NULL on failure 35 | */ 36 | zathura_database_t* zathura_sqldatabase_new(const char* path); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /zathura/marks.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef MARKS_H 4 | #define MARKS_H 5 | 6 | #include 7 | 8 | #include "zathura.h" 9 | 10 | /** 11 | * Saves a mark 12 | * 13 | * @param session The used girara session 14 | * @param argument The used argument 15 | * @param event Girara event 16 | * @param t Number of executions 17 | * @return true if no error occurred otherwise false 18 | */ 19 | bool sc_mark_add(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t); 20 | 21 | /** 22 | * Evaluates a mark 23 | * 24 | * @param session The used girara session 25 | * @param argument The used argument 26 | * @param event Girara event 27 | * @param t Number of executions 28 | * @return true if no error occurred otherwise false 29 | */ 30 | bool sc_mark_evaluate(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t); 31 | 32 | /** 33 | * Mark current location within the web page 34 | * 35 | * @param session The girara session 36 | * @param argument_list Argument list 37 | * @return true if no error occurred otherwise false 38 | */ 39 | bool cmd_marks_add(girara_session_t* session, girara_list_t* argument_list); 40 | 41 | /** 42 | * Delete the specified marks 43 | * 44 | * @param session The girara session 45 | * @param argument_list Argument list 46 | * @return true if no error occurred otherwise false 47 | */ 48 | bool cmd_marks_delete(girara_session_t* session, girara_list_t* argument_list); 49 | 50 | /** 51 | * Load and set quickmarks from database 52 | * 53 | * @param zathura The zathura instance 54 | * @param file Open file 55 | * @return true if no error occurred otherwise false 56 | */ 57 | bool zathura_quickmarks_load(zathura_t* zathura, const gchar* file); 58 | 59 | #endif // MARKS_H 60 | -------------------------------------------------------------------------------- /zathura/bookmarks.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef BOOKMARKS_H 4 | #define BOOKMARKS_H 5 | 6 | #include 7 | #include "zathura.h" 8 | 9 | struct zathura_bookmark_s { 10 | gchar* id; 11 | unsigned int page; 12 | double x; 13 | double y; 14 | }; 15 | 16 | typedef struct zathura_bookmark_s zathura_bookmark_t; 17 | 18 | /** 19 | * Create a bookmark and add it to the list of bookmarks. 20 | * @param zathura The zathura instance. 21 | * @param id The bookmark's id. 22 | * @param page The bookmark's page. 23 | * @return the bookmark instance or NULL on failure. 24 | */ 25 | zathura_bookmark_t* zathura_bookmark_add(zathura_t* zathura, const gchar* id, unsigned int page); 26 | 27 | /** 28 | * Remove a bookmark from the list of bookmarks. 29 | * @param zathura The zathura instance. 30 | * @param id The bookmark's id. 31 | * @return true on success, false otherwise 32 | */ 33 | bool zathura_bookmark_remove(zathura_t* zathura, const gchar* id); 34 | 35 | /** 36 | * Get bookmark from the list of bookmarks. 37 | * @param zathura The zathura instance. 38 | * @param id The bookmark's id. 39 | * @return The bookmark instance if it exists or NULL otherwise. 40 | */ 41 | zathura_bookmark_t* zathura_bookmark_get(zathura_t* zathura, const gchar* id); 42 | 43 | /** 44 | * Free a bookmark instance. 45 | * @param bookmark The bookmark instance. 46 | */ 47 | void zathura_bookmark_free(zathura_bookmark_t* bookmark); 48 | 49 | /** 50 | * Load bookmarks for a specific file. 51 | * @param zathura The zathura instance. 52 | * @param file The file. 53 | * @return true on success, false otherwise 54 | */ 55 | bool zathura_bookmarks_load(zathura_t* zathura, const gchar* file); 56 | 57 | /** 58 | * Compare two bookmarks. 59 | * @param lhs a bookmark 60 | * @param rhs a bookmark 61 | * @returns g_strcmp0(lhs->id, rhs->id) 62 | */ 63 | int zathura_bookmarks_compare(const zathura_bookmark_t* lhs, const zathura_bookmark_t* rhs); 64 | 65 | #endif // BOOKMARKS_H 66 | -------------------------------------------------------------------------------- /zathura/dbus-interface.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef DBUS_INTERFACE_H 4 | #define DBUS_INTERFACE_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "types.h" 11 | 12 | typedef struct zathura_dbus_class_s ZathuraDbusClass; 13 | 14 | struct zathura_dbus_s { 15 | GObject parent; 16 | }; 17 | 18 | struct zathura_dbus_class_s { 19 | GObjectClass parent_class; 20 | }; 21 | 22 | #define ZATHURA_TYPE_DBUS (zathura_dbus_get_type()) 23 | #define ZATHURA_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_DBUS, ZathuraDbus)) 24 | #define ZATHURA_DBUS_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_DBUS, ZathuraDbus)) 25 | #define ZATHURA_IS_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_DBUS)) 26 | #define ZATHURA_IS_DBUS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_DBUS)) 27 | #define ZATHURA_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_DBUS, ZathuraDbusClass)) 28 | 29 | GType zathura_dbus_get_type(void) G_GNUC_CONST; 30 | 31 | ZathuraDbus* zathura_dbus_new(zathura_t* zathura); 32 | const char* zathura_dbus_get_name(zathura_t* zathura); 33 | 34 | /** 35 | * Emit the 'Edit' signal on the D-Bus connection. 36 | * 37 | * @param zathura Zathura session 38 | * @param page page 39 | * @param x x coordinate 40 | * @param y y coordinate 41 | */ 42 | void zathura_dbus_edit(zathura_t* zathura, unsigned int page, unsigned int x, unsigned int y); 43 | 44 | /** 45 | * Highlight rectangles in a zathura instance that has filename open. 46 | * input_file, line and column determine the rectangles to display and are 47 | * passed to SyncTeX. 48 | * 49 | * @param filename path of the document 50 | * @param input_file path of the input file 51 | * @param line line index (starts at 0) 52 | * @param column column index (starts at 0) 53 | * @param hint zathura process ID that has filename open 54 | */ 55 | int zathura_dbus_synctex_position(const char* filename, const char* input_file, int line, int column, pid_t hint); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /data/zsh-completion.in: -------------------------------------------------------------------------------- 1 | #compdef zathura 2 | 3 | local -a all_opts 4 | all_opts=( 5 | '(-c --config-dir)'{-c,--config-dir=}'[specify path to config directory]:config directory:_files -/' 6 | '(-d --data-dir)'{-d,--data-dir=}'[specify path to data directory]:data directory:_files -/' 7 | '--cache-dir=[specify path to cache directory]:cache directory:_files -/' 8 | '(-p --plugins-dir)'{-p,--plugins-dir=}'[specify path to plugins directory]:plugins directory:_files -/' 9 | '(-e --reparent)'{-e,--reparent=}'[reparent to window specified by XID (X11)]: :_x_window' 10 | '(-w --password)'{-w,--password=}'[specify a password for the document]:password' 11 | '(-P --page)'{-p,--page=}'[open the document at the given page number]:page number' 12 | '(-l --log-level)'{-l,--log-level=}'[set log level]:level:(error warning info debug)' 13 | '(-x --synctex-editor-command)'{-x,--synctex-editor-command=}'[specify synctex editor (forwarded to the synctex command)]:command' 14 | '--synctex-forward=[move to the given position]:position' 15 | '--synctex-pid=[highlight position in given process]:pid:_pids' 16 | '--mode[start in a non-default mode]:mode:(presentation fullscreen)' 17 | '--fork[fork into the background]' 18 | '(- :)'{-h,--help}'[show help message]' 19 | '(- :)'{-v,--version}'[print version information]' 20 | '*:file:->files' 21 | ) 22 | 23 | local curcontext="$curcontext" state state_descr line ret=1 24 | typeset -A opt_args 25 | _arguments -C -s -S "$all_opts[@]" && ret=0 26 | 27 | local PLUGIN 28 | local -a exts 29 | for PLUGIN in @PLUGINDIR@/lib*.so; do 30 | case ${PLUGIN##*/} in 31 | libpdf-poppler.so) 32 | exts+=( pdf PDF ) 33 | ;; 34 | libpdf-mupdf.so) 35 | exts+=( pdf PDF epub oxps ) 36 | ;; 37 | libps.so) 38 | exts+=( ps eps epsi epsf ) 39 | ;; 40 | libdjvu.so) 41 | exts+=( djvu djv ) 42 | ;; 43 | libcb.so) 44 | exts+=( cb7 cbr cbz cbt rar zip 7z tar ) 45 | ;; 46 | esac 47 | done 48 | 49 | case $state in 50 | (files) 51 | _wanted files expl file _files -g "*.(${(j:|:)exts:-pdf})(-.)" && ret=0 52 | ;; 53 | esac 54 | 55 | return ret 56 | -------------------------------------------------------------------------------- /zathura/links.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef LINK_H 4 | #define LINK_H 5 | 6 | #include "types.h" 7 | 8 | #include 9 | 10 | /** 11 | * Creates a new zathura link 12 | * 13 | * @param type Type of the link 14 | * @param position Position of the link 15 | * @param target Target 16 | * @return New zathura link 17 | */ 18 | ZATHURA_PLUGIN_API zathura_link_t* zathura_link_new(zathura_link_type_t type, zathura_rectangle_t position, 19 | zathura_link_target_t target); 20 | 21 | /** 22 | * Free link 23 | * 24 | * @param link The link 25 | */ 26 | ZATHURA_PLUGIN_API void zathura_link_free(zathura_link_t* link); 27 | 28 | /** 29 | * Returns the type of the link 30 | * 31 | * @param link The link 32 | * @return The target type of the link 33 | */ 34 | ZATHURA_PLUGIN_API zathura_link_type_t zathura_link_get_type(zathura_link_t* link); 35 | 36 | /** 37 | * Returns the position of the link 38 | * 39 | * @param link The link 40 | * @return The position of the link 41 | */ 42 | ZATHURA_PLUGIN_API zathura_rectangle_t zathura_link_get_position(zathura_link_t* link); 43 | 44 | /** 45 | * The target value of the link 46 | * 47 | * @param link The link 48 | * @return Returns the target of the link (depends on the link type) 49 | */ 50 | ZATHURA_PLUGIN_API zathura_link_target_t zathura_link_get_target(zathura_link_t* link); 51 | 52 | /** 53 | * Evaluate link 54 | * 55 | * @param zathura Zathura instance 56 | * @param link The link 57 | */ 58 | void zathura_link_evaluate(zathura_t* zathura, zathura_link_t* link); 59 | 60 | /** 61 | * Display a link using girara_notify 62 | * 63 | * @param zathura Zathura instance 64 | * @param link The link 65 | */ 66 | void zathura_link_display(zathura_t* zathura, zathura_link_t* link); 67 | 68 | /** 69 | * Copy a link into the clipboard using and display it using girara_notify 70 | * 71 | * @param zathura Zathura instance 72 | * @param link The link 73 | * @param selection target clipboard 74 | */ 75 | void zathura_link_copy(zathura_t* zathura, zathura_link_t* link, GdkAtom* selection); 76 | 77 | #endif // LINK_H 78 | -------------------------------------------------------------------------------- /zathura/file-monitor-signal.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "file-monitor-signal.h" 4 | 5 | #include 6 | #ifdef G_OS_UNIX 7 | #include 8 | #endif 9 | 10 | struct zathura_signalfilemonitor_s { 11 | ZathuraFileMonitor parent; 12 | gint handle; 13 | }; 14 | 15 | G_DEFINE_TYPE(ZathuraSignalFileMonitor, zathura_signalfilemonitor, ZATHURA_TYPE_FILEMONITOR) 16 | 17 | static gboolean signal_handler(gpointer data) { 18 | if (data == NULL) { 19 | return TRUE; 20 | } 21 | 22 | ZathuraSignalFileMonitor* signalfilemonitor = data; 23 | 24 | girara_debug("SIGHUP received"); 25 | g_signal_emit_by_name(signalfilemonitor, "reload-file"); 26 | 27 | return TRUE; 28 | } 29 | 30 | static void start(ZathuraFileMonitor* file_monitor) { 31 | #ifdef G_OS_UNIX 32 | ZathuraSignalFileMonitor* signal_file_monitor = ZATHURA_SIGNALFILEMONITOR(file_monitor); 33 | 34 | signal_file_monitor->handle = g_unix_signal_add(SIGHUP, signal_handler, signal_file_monitor); 35 | #endif 36 | } 37 | 38 | static void stop(ZathuraFileMonitor* file_monitor) { 39 | #ifdef G_OS_UNIX 40 | ZathuraSignalFileMonitor* signal_file_monitor = ZATHURA_SIGNALFILEMONITOR(file_monitor); 41 | 42 | if (signal_file_monitor->handle > 0) { 43 | g_source_remove(signal_file_monitor->handle); 44 | signal_file_monitor->handle = 0; 45 | } 46 | #endif 47 | } 48 | 49 | static void zathura_signalfilemonitor_finalize(GObject* object) { 50 | stop(ZATHURA_FILEMONITOR(object)); 51 | 52 | G_OBJECT_CLASS(zathura_signalfilemonitor_parent_class)->finalize(object); 53 | } 54 | 55 | static void zathura_signalfilemonitor_class_init(ZathuraSignalFileMonitorClass* class) { 56 | ZathuraFileMonitorClass* filemonitor_class = ZATHURA_FILEMONITOR_CLASS(class); 57 | filemonitor_class->start = start; 58 | filemonitor_class->stop = stop; 59 | 60 | GObjectClass* object_class = G_OBJECT_CLASS(class); 61 | object_class->finalize = zathura_signalfilemonitor_finalize; 62 | } 63 | 64 | static void zathura_signalfilemonitor_init(ZathuraSignalFileMonitor* signalfilemonitor) { 65 | signalfilemonitor->handle = 0; 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | zathura - a document viewer 2 | =========================== 3 | 4 | zathura is a highly customizable and functional document viewer based on the 5 | girara user interface library and several document libraries. 6 | 7 | Requirements 8 | ------------ 9 | 10 | The following dependencies are required: 11 | 12 | * `gtk3` (>= 3.24) 13 | * `glib` (>= 2.76) 14 | * `girara` (>= 0.4.3) 15 | * `libmagic` from file(1): for mime-type detection 16 | * `json-glib` 17 | * `sqlite3` (>= 3.6.23): sqlite3 database backend 18 | 19 | The following dependencies are optional: 20 | * `libsynctex` from TeXLive (>= 2): SyncTeX support 21 | * `libseccomp`: sandbox support 22 | 23 | For building zathura, the following dependencies are also required: 24 | 25 | * `meson` (>= 1.5) 26 | * `gettext` 27 | * `pkgconf` 28 | 29 | The following dependencies are optional build-time only dependencies: 30 | 31 | * `librvsg-bin`: PNG icons 32 | * `Sphinx`: manpages and HTML documentation 33 | * `doxygen`: HTML documentation 34 | * `breathe`: for HTML documentation 35 | * `sphinx_rtd_theme`: for HTML documentation 36 | 37 | Note that `Sphinx` is needed to build the manpages. If it is not installed, the 38 | man pages won't be built. For building the HTML documentation, `doxygen`, 39 | `breathe` and `sphinx_rtd_theme` are needed in addition to `Sphinx`. 40 | 41 | The use of `libseccomp` and/or `landlock` to create a sandboxed environment is 42 | optional and can be disabled by configure the build system with 43 | `-Dseccomp=disabled` and `-Dlandlock=disabled`. The sandboxed version of zathura 44 | will be built into a separate binary named `zathura-sandbox`. Strict sandbox 45 | mode will reduce the available functionality of zathura and provide a read only 46 | document viewer. 47 | 48 | Installation 49 | ------------ 50 | 51 | To build and install zathura using meson's ninja backend: 52 | 53 | meson build 54 | cd build 55 | ninja 56 | ninja install 57 | 58 | > **Note:** The default backend for meson might vary based on the platform. Please 59 | refer to the meson documentation for platform specific dependencies. 60 | 61 | Bugs 62 | ---- 63 | 64 | Please report bugs at https://github.com/pwmt/zathura. 65 | -------------------------------------------------------------------------------- /zathura/file-monitor.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef FILEMONITOR_H 4 | #define FILEMONITOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define ZATHURA_TYPE_FILEMONITOR (zathura_filemonitor_get_type()) 11 | #define ZATHURA_FILEMONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_FILEMONITOR, ZathuraFileMonitor)) 12 | #define ZATHURA_FILEMONITOR_CLASS(obj) \ 13 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_FILEMONITOR, ZathuraFileMonitorClass)) 14 | #define ZATHURA_IS_FILEMONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_FILEMONITOR)) 15 | #define ZATHURA_IS_FILEMONITOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_FILEMONITOR)) 16 | #define ZATHURA_FILEMONITOR_GET_CLASS(obj) \ 17 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_FILEMONITOR, ZathuraFileMonitorClass)) 18 | 19 | typedef struct zathura_filemonitor_s ZathuraFileMonitor; 20 | typedef struct zathura_filemonitor_class_s ZathuraFileMonitorClass; 21 | 22 | /** 23 | * Base class for all file monitors. 24 | * 25 | * The signal 'reload-file' is emitted if the monitored file changed. 26 | */ 27 | struct zathura_filemonitor_s { 28 | GObject parent; 29 | }; 30 | 31 | struct zathura_filemonitor_class_s { 32 | GObjectClass parent_class; 33 | 34 | void (*start)(ZathuraFileMonitor*); 35 | void (*stop)(ZathuraFileMonitor*); 36 | }; 37 | 38 | /** 39 | * Get the type of the filemonitor. 40 | * 41 | * @return the type 42 | */ 43 | GType zathura_filemonitor_get_type(void) G_GNUC_CONST; 44 | 45 | /** 46 | * Type of file monitor. 47 | */ 48 | typedef enum zathura_filemonitor_type_e { 49 | ZATHURA_FILEMONITOR_GLIB, /**< Use filemonitor from GLib */ 50 | ZATHURA_FILEMONITOR_SIGNAL, /**< Reload when receiving SIGHUP */ 51 | ZATHURA_FILEMONITOR_NOOP /**< Monitor that does nothing */ 52 | } zathura_filemonitor_type_t; 53 | 54 | /** 55 | * Create a new file monitor. 56 | * 57 | * @param file_path file to monitor 58 | * @param filemonitor_type type of file monitor 59 | * @return new file monitor instance 60 | */ 61 | ZathuraFileMonitor* zathura_filemonitor_new(const char* file_path, zathura_filemonitor_type_t filemonitor_type); 62 | 63 | /** 64 | * Get path of the monitored file. 65 | * 66 | * @return path of monitored file 67 | */ 68 | const char* zathura_filemonitor_get_filepath(ZathuraFileMonitor* file_monitor); 69 | 70 | /** 71 | * Start file monitor. 72 | */ 73 | void zathura_filemonitor_start(ZathuraFileMonitor* file_monitor); 74 | 75 | /** 76 | * Stop file monitor. 77 | */ 78 | void zathura_filemonitor_stop(ZathuraFileMonitor* file_monitor); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /data/fish-completion.in: -------------------------------------------------------------------------------- 1 | # Complete custom suffix based on libraries installed 2 | function __fish_complete_zathura 3 | for plugin in @PLUGINDIR@/*.so 4 | switch (basename $plugin) 5 | case libpdf-poppler.so 6 | __fish_complete_suffix .pdf 7 | case libpdf-mupdf.so 8 | __fish_complete_suffix .pdf 9 | __fish_complete_suffix .epub 10 | __fish_complete_suffix .oxps 11 | case libps.so 12 | __fish_complete_suffix .ps 13 | __fish_complete_suffix .eps 14 | __fish_complete_suffix .epsi 15 | __fish_complete_suffix .epsf 16 | case libdjvu.so 17 | __fish_complete_suffix .djvu 18 | __fish_complete_suffix .djv 19 | case libcb.so 20 | __fish_complete_suffix .cb7 21 | __fish_complete_suffix .cbr 22 | __fish_complete_suffix .cbz 23 | __fish_complete_suffix .cbt 24 | __fish_complete_suffix .rar 25 | __fish_complete_suffix .zip 26 | __fish_complete_suffix .7z 27 | __fish_complete_suffix .tar 28 | end 29 | end 30 | end 31 | 32 | complete -c zathura -kxa '(__fish_complete_zathura)' 33 | 34 | complete -c zathura -s e -l reparent -d 'Reparents to window specified by xid' 35 | complete -c zathura -s c -l config-dir -d 'Path to config directory' \ 36 | -x -a '(__fish_complete_directories (commandline -ct) "Config directory")' 37 | complete -c zathura -s d -l data-dir -d 'Path to data directory' \ 38 | -x -a '(__fish_complete_directories (commandline -ct) "Data directory")' 39 | complete -c zathura -l cache-dir -d 'Path to cache directory' \ 40 | -x -a '(__fish_complete_directories (commandline -ct) "Cache directory")' 41 | complete -c zathura -s p -l plugins-dir -d 'Path to plugins directory' \ 42 | -x -a '(__fish_complete_directories (commandline -ct) "Plugins directory")' 43 | complete -c zathura -s w -l password -d 'Document password' 44 | complete -c zathura -s P -l page -d 'Page number to go to' 45 | complete -c zathura -s l -l log-level -d 'Log level' -x -a 'debug info warning error' 46 | complete -c zathura -s x -l synctex-editor-command -d 'Synctex editor (forwarded to the synctex command)' -x 47 | complete -c zathura -l synctex-forward -d 'Move to given synctex position' -x 48 | complete -c zathura -l synctex-pid -d 'Highlight position in given process' -x -a '(__fish_complete_pids)' 49 | complete -c zathura -l mode -d 'Start in a non-default mode' -x -a 'presentation fullscreen' 50 | complete -c zathura -l fork -d 'Fork into the background' 51 | complete -c zathura -s h -l help -d 'Show help options' 52 | complete -c zathura -s v -l version -d 'Print version information' 53 | -------------------------------------------------------------------------------- /data/org.pwmt.zathura.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | gnome = import('gnome') 2 | zathura_resources = gnome.compile_resources( 3 | 'resources', 4 | 'zathura.gresource.xml', 5 | c_name: 'zathura_resources', 6 | dependencies: files('zathura.css_t', 'org.pwmt.zathura.xml') 7 | ) 8 | 9 | install_data('org.pwmt.zathura.xml', install_dir: dbusinterfacesdir) 10 | install_data('org.pwmt.zathura.svg', install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps')) 11 | 12 | rsvg_convert = find_program('rsvg-convert', required: get_option('convert-icon'), native: true) 13 | if rsvg_convert.found() 14 | foreach width : [16, 32, 64, 128, 256] 15 | subdir('icon-@0@'.format(width)) 16 | endforeach 17 | endif 18 | 19 | i18n = import('i18n') 20 | podir = join_paths(meson.project_source_root(), 'po') 21 | 22 | desktop = i18n.merge_file( 23 | input: 'org.pwmt.zathura.desktop.in', 24 | output: 'org.pwmt.zathura.desktop', 25 | install: true, 26 | install_dir: desktopdir, 27 | po_dir: podir, 28 | type: 'desktop' 29 | ) 30 | 31 | appdata = i18n.merge_file( 32 | input: 'org.pwmt.zathura.metainfo.xml.in', 33 | output: 'org.pwmt.zathura.metainfo.xml', 34 | install: true, 35 | install_dir: metainfodir, 36 | po_dir: podir, 37 | ) 38 | 39 | desktop_file_validate = find_program('desktop-file-validate', required: get_option('tests'), native: true) 40 | if desktop_file_validate.found() 41 | test('validate-desktop', 42 | desktop_file_validate, 43 | args: [desktop.full_path()] 44 | ) 45 | endif 46 | 47 | appstreamcli = find_program('appstreamcli', required: get_option('tests'), native: true) 48 | if appstreamcli.found() 49 | test( 50 | 'validate-appdata', 51 | appstreamcli, 52 | args: ['validate', '--no-net', appdata.full_path()], 53 | ) 54 | endif 55 | 56 | conf_data = configuration_data() 57 | conf_data.set('PLUGINDIR', join_paths(prefix, plugindir)) 58 | bash_completion = configure_file( 59 | input: 'bash-completion.in', 60 | output: 'zathura', 61 | configuration: conf_data 62 | ) 63 | zsh_completion = configure_file( 64 | input: 'zsh-completion.in', 65 | output: '_zathura', 66 | configuration: conf_data 67 | ) 68 | fish_completion = configure_file( 69 | input: 'fish-completion.in', 70 | output: 'zathura.fish', 71 | configuration: conf_data 72 | ) 73 | 74 | bash_comp = dependency('bash-completion', required: false) 75 | if bash_comp.found() 76 | bash_compdir = bash_comp.get_variable(pkgconfig: 'completionsdir') 77 | else 78 | bash_compdir = join_paths(datadir, 'bash-completion', 'completions') 79 | endif 80 | 81 | fish_comp = dependency('fish', required: false) 82 | if fish_comp.found() 83 | fish_compdir = fish_comp.get_variable(pkgconfig: 'completionsdir') 84 | else 85 | fish_compdir = join_paths(datadir, 'fish', 'vendor_completions.d') 86 | endif 87 | 88 | install_data(bash_completion, install_dir: bash_compdir) 89 | install_data(zsh_completion, install_dir: join_paths(datadir, 'zsh', 'site-functions')) 90 | install_data(fish_completion, install_dir: fish_compdir) 91 | -------------------------------------------------------------------------------- /zathura/file-monitor-glib.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "file-monitor-glib.h" 4 | #include "macros.h" 5 | 6 | #include 7 | #include 8 | 9 | struct zathura_glibfilemonitor_s { 10 | ZathuraFileMonitor parent; 11 | GFileMonitor* monitor; /**< File monitor */ 12 | GFile* file; /**< File for file monitor */ 13 | }; 14 | 15 | G_DEFINE_TYPE(ZathuraGLibFileMonitor, zathura_glibfilemonitor, ZATHURA_TYPE_FILEMONITOR) 16 | 17 | static void file_changed(GFileMonitor* UNUSED(monitor), GFile* file, GFile* UNUSED(other_file), GFileMonitorEvent event, 18 | gpointer user_data) { 19 | ZathuraGLibFileMonitor* file_monitor = user_data; 20 | 21 | switch (event) { 22 | case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: 23 | case G_FILE_MONITOR_EVENT_CREATED: { 24 | g_autofree char* uri = g_file_get_uri(file); 25 | girara_debug("received file-monitor event for %s", uri); 26 | 27 | g_signal_emit_by_name(file_monitor, "reload-file"); 28 | break; 29 | } 30 | default: 31 | return; 32 | } 33 | } 34 | 35 | static void start(ZathuraFileMonitor* file_monitor) { 36 | ZathuraGLibFileMonitor* glib_file_monitor = ZATHURA_GLIBFILEMONITOR(file_monitor); 37 | 38 | const char* file_path = zathura_filemonitor_get_filepath(file_monitor); 39 | 40 | /* install file monitor */ 41 | glib_file_monitor->file = g_file_new_for_path(file_path); 42 | if (glib_file_monitor->file == NULL) { 43 | return; 44 | } 45 | 46 | glib_file_monitor->monitor = 47 | g_file_monitor_file(glib_file_monitor->file, G_FILE_MONITOR_WATCH_HARD_LINKS, NULL, NULL); 48 | if (glib_file_monitor->monitor != NULL) { 49 | g_signal_connect_object(G_OBJECT(glib_file_monitor->monitor), "changed", G_CALLBACK(file_changed), 50 | glib_file_monitor, 0); 51 | } 52 | } 53 | 54 | static void stop(ZathuraFileMonitor* file_monitor) { 55 | ZathuraGLibFileMonitor* glib_file_monitor = ZATHURA_GLIBFILEMONITOR(file_monitor); 56 | 57 | if (glib_file_monitor->monitor != NULL) { 58 | g_file_monitor_cancel(glib_file_monitor->monitor); 59 | } 60 | 61 | g_clear_object(&glib_file_monitor->monitor); 62 | g_clear_object(&glib_file_monitor->file); 63 | } 64 | 65 | static void dispose(GObject* object) { 66 | stop(ZATHURA_FILEMONITOR(object)); 67 | 68 | G_OBJECT_CLASS(zathura_glibfilemonitor_parent_class)->dispose(object); 69 | } 70 | 71 | static void finalize(GObject* object) { 72 | G_OBJECT_CLASS(zathura_glibfilemonitor_parent_class)->finalize(object); 73 | } 74 | 75 | static void zathura_glibfilemonitor_class_init(ZathuraGLibFileMonitorClass* class) { 76 | ZathuraFileMonitorClass* filemonitor_class = ZATHURA_FILEMONITOR_CLASS(class); 77 | filemonitor_class->start = start; 78 | filemonitor_class->stop = stop; 79 | 80 | GObjectClass* object_class = G_OBJECT_CLASS(class); 81 | object_class->dispose = dispose; 82 | object_class->finalize = finalize; 83 | } 84 | 85 | static void zathura_glibfilemonitor_init(ZathuraGLibFileMonitor* glibfilemonitor) { 86 | glibfilemonitor->monitor = NULL; 87 | glibfilemonitor->file = NULL; 88 | } 89 | -------------------------------------------------------------------------------- /zathura/adjustment.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_ADJUSTMENT_H 4 | #define ZATHURA_ADJUSTMENT_H 5 | 6 | #include 7 | #include 8 | #include "document.h" 9 | 10 | /** 11 | * Calculate the page size according to the current scaling and rotation if 12 | * desired. 13 | * 14 | * @param document the document 15 | * @param height the original height 16 | * @param width the original width 17 | * @param page_height the scaled and rotated height 18 | * @param page_width the scaled and rotated width 19 | * @param rotate honor page's rotation 20 | * @return real scale after rounding 21 | */ 22 | double page_calc_height_width(zathura_document_t* document, double height, double width, unsigned int* page_height, 23 | unsigned int* page_width, bool rotate); 24 | 25 | /** 26 | * Calculate a page relative position after a rotation. The positions x y are 27 | * relative to a page, i.e. 0=top of page, 1=bottom of page. They are NOT 28 | * relative to the entire document. 29 | * 30 | * @param document the document 31 | * @param x the x coordinates on the unrotated page 32 | * @param y the y coordinates on the unrotated page 33 | * @param xn the x coordinates after rotation 34 | * @param yn the y coordinates after rotation 35 | */ 36 | void page_calc_position(zathura_document_t* document, double x, double y, double* xn, double* yn); 37 | 38 | /** 39 | * Converts a relative position within the document to a page number. 40 | * 41 | * @param document The document 42 | * @param pos_x the x position relative to the document 43 | * @param pos_y the y position relative to the document 44 | * @return page sitting in that position 45 | */ 46 | unsigned int position_to_page_number(zathura_document_t* document, double pos_x, double pos_y); 47 | 48 | /** 49 | * Converts a page number to a position in units relative to the document 50 | * 51 | * We can specify where to aliwn the viewport and the page. For instance, xalign 52 | * = 0 means align them on the left margin, xalign = 0.5 means centered, and 53 | * xalign = 1.0 align them on the right margin. 54 | * 55 | * The return value is the position in in units relative to the document (0=top 56 | * 1=bottom) of the point thet will lie at the center of the viewport. 57 | * 58 | * @param document The document 59 | * @param page_number the given page number 60 | * @param xalign where to align the viewport and the page 61 | * @param yalign where to align the viewport and the page 62 | * @param pos_x position that will lie at the center of the viewport. 63 | * @param pos_y position that will lie at the center of the viewport. 64 | */ 65 | void page_number_to_position(zathura_document_t* document, unsigned int page_number, double xalign, double yalign, 66 | double* pos_x, double* pos_y); 67 | 68 | /** 69 | * Checks whether a given page falls within the viewport 70 | * 71 | * @param document The document 72 | * @param page_number the page number 73 | * @return true if the page intersects the viewport 74 | */ 75 | bool page_is_visible(zathura_document_t* document, unsigned int page_number); 76 | 77 | #endif /* ZATHURA_ADJUSTMENT_H */ 78 | -------------------------------------------------------------------------------- /zathura/database.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "database.h" 4 | 5 | G_DEFINE_INTERFACE(ZathuraDatabase, zathura_database, G_TYPE_OBJECT) 6 | 7 | static void zathura_database_default_init(ZathuraDatabaseInterface* GIRARA_UNUSED(iface)) {} 8 | 9 | bool zathura_db_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark) { 10 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL && bookmark != NULL, false); 11 | 12 | return ZATHURA_DATABASE_GET_INTERFACE(db)->add_bookmark(db, file, bookmark); 13 | } 14 | 15 | bool zathura_db_remove_bookmark(zathura_database_t* db, const char* file, const char* id) { 16 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL && id != NULL, false); 17 | 18 | return ZATHURA_DATABASE_GET_INTERFACE(db)->remove_bookmark(db, file, id); 19 | } 20 | 21 | girara_list_t* zathura_db_load_bookmarks(zathura_database_t* db, const char* file) { 22 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL, NULL); 23 | 24 | return ZATHURA_DATABASE_GET_INTERFACE(db)->load_bookmarks(db, file); 25 | } 26 | 27 | girara_list_t* zathura_db_load_jumplist(zathura_database_t* db, const char* file) { 28 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL, NULL); 29 | 30 | return ZATHURA_DATABASE_GET_INTERFACE(db)->load_jumplist(db, file); 31 | } 32 | 33 | bool zathura_db_save_jumplist(zathura_database_t* db, const char* file, girara_list_t* jumplist) { 34 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL && jumplist != NULL, NULL); 35 | 36 | return ZATHURA_DATABASE_GET_INTERFACE(db)->save_jumplist(db, file, jumplist); 37 | } 38 | 39 | bool zathura_db_set_fileinfo(zathura_database_t* db, const char* file, const uint8_t* hash_sha256, 40 | zathura_fileinfo_t* file_info) { 41 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL && hash_sha256 != NULL && file_info != NULL, false); 42 | 43 | return ZATHURA_DATABASE_GET_INTERFACE(db)->set_fileinfo(db, file, hash_sha256, file_info); 44 | } 45 | 46 | bool zathura_db_get_fileinfo(zathura_database_t* db, const char* file, const uint8_t* hash_sha256, 47 | zathura_fileinfo_t* file_info) { 48 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file != NULL && hash_sha256 != NULL && file_info != NULL, false); 49 | 50 | return ZATHURA_DATABASE_GET_INTERFACE(db)->get_fileinfo(db, file, hash_sha256, file_info); 51 | } 52 | 53 | girara_list_t* zathura_db_get_recent_files(zathura_database_t* db, int max, const char* basepath) { 54 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db), NULL); 55 | 56 | return ZATHURA_DATABASE_GET_INTERFACE(db)->get_recent_files(db, max, basepath); 57 | } 58 | 59 | girara_list_t* zathura_db_load_quickmarks(ZathuraDatabase* db, const char* file) { 60 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file, NULL); 61 | 62 | return ZATHURA_DATABASE_GET_INTERFACE(db)->load_quickmarks(db, file); 63 | } 64 | 65 | bool zathura_db_save_quickmarks(ZathuraDatabase* db, const char* file, girara_list_t* quickmarks) { 66 | g_return_val_if_fail(ZATHURA_IS_DATABASE(db) && file && quickmarks, false); 67 | 68 | return ZATHURA_DATABASE_GET_INTERFACE(db)->save_quickmarks(db, file, quickmarks); 69 | } 70 | -------------------------------------------------------------------------------- /zathura/page-widget.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef PAGE_WIDGET_H 4 | #define PAGE_WIDGET_H 5 | 6 | #include 7 | #include "types.h" 8 | #include "document.h" 9 | 10 | /** 11 | * The page view widget. The widget handles all the rendering on its own. It 12 | * only has to be resized. The widget also manages and handles all the 13 | * rectangles for highlighting. 14 | * 15 | * Before the properties contain the correct values, 'draw-links' has to be set 16 | * to TRUE at least one time. 17 | * */ 18 | struct zathura_page_widget_s { 19 | GtkDrawingArea parent; 20 | }; 21 | 22 | struct zathura_page_widget_class_s { 23 | GtkDrawingAreaClass parent_class; 24 | }; 25 | 26 | #define ZATHURA_TYPE_PAGE (zathura_page_widget_get_type()) 27 | #define ZATHURA_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_PAGE, ZathuraPage)) 28 | #define ZATHURA_PAGE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_PAGE, ZathuraPageClass)) 29 | #define ZATHURA_IS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_PAGE)) 30 | #define ZATHURA_IS_PAGE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_PAGE)) 31 | #define ZATHURA_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_PAGE, ZathuraPageClass)) 32 | 33 | /** 34 | * Returns the type of the page view widget. 35 | * @return the type 36 | */ 37 | GType zathura_page_widget_get_type(void) G_GNUC_CONST; 38 | /** 39 | * Create a page view widget. 40 | * @param zathura the zathura instance 41 | * @param page the page to be displayed 42 | * @return a page view widget 43 | */ 44 | GtkWidget* zathura_page_widget_new(zathura_t* zathura, zathura_page_t* page); 45 | /** 46 | * Update the widget's surface. This should only be called from the render 47 | * thread. 48 | * @param widget the widget 49 | * @param surface the new surface 50 | * @param keep_thumbnail don't destroy when surface is NULL 51 | */ 52 | void zathura_page_widget_update_surface(ZathuraPage* widget, cairo_surface_t* surface, bool keep_thumbnail); 53 | /** 54 | * Clear highlight of the selection/highlighter. 55 | * @param widget the widget 56 | */ 57 | void zathura_page_widget_clear_selection(ZathuraPage* widget); 58 | /** 59 | * Draw a rectangle to mark links or search results 60 | * @param widget the widget 61 | * @param rectangle the rectangle 62 | * @param linkid the link id if it's a link, -1 otherwise 63 | */ 64 | zathura_link_t* zathura_page_widget_link_get(ZathuraPage* widget, unsigned int index); 65 | /** 66 | * Update the last view time of the page. 67 | * 68 | * @param widget the widget 69 | */ 70 | void zathura_page_widget_update_view_time(ZathuraPage* widget); 71 | /** 72 | * Check if we have a surface. 73 | * 74 | * @param widget the widget 75 | * @returns true if the widget has a surface, false otherwise 76 | */ 77 | bool zathura_page_widget_have_surface(ZathuraPage* widget); 78 | /** 79 | * Abort outstanding render requests 80 | * 81 | * @param widget the widget 82 | */ 83 | void zathura_page_widget_abort_render_request(ZathuraPage* widget); 84 | /** 85 | * Get underlying page 86 | * 87 | * @param widget the widget 88 | * @return underlying zathura_page_t instance 89 | */ 90 | zathura_page_t* zathura_page_widget_get_page(ZathuraPage* widget); 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /zathura/database-null.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include 5 | 6 | #include "database-null.h" 7 | #include "utils.h" 8 | 9 | static bool add_bookmark(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file), 10 | zathura_bookmark_t* GIRARA_UNUSED(bookmark)) { 11 | return true; 12 | } 13 | 14 | static bool remove_bookmark(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file), 15 | const char* GIRARA_UNUSED(id)) { 16 | return true; 17 | } 18 | 19 | static girara_list_t* load_list(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file)) { 20 | return girara_list_new(); 21 | } 22 | 23 | static bool save_list(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file), 24 | girara_list_t* GIRARA_UNUSED(jumplist)) { 25 | return true; 26 | } 27 | 28 | static bool set_fileinfo(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file), 29 | const uint8_t* GIRARA_UNUSED(hash_sha256), zathura_fileinfo_t* GIRARA_UNUSED(file_info)) { 30 | return true; 31 | } 32 | 33 | static bool get_fileinfo(zathura_database_t* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(file), 34 | const uint8_t* GIRARA_UNUSED(hash_sha256), zathura_fileinfo_t* GIRARA_UNUSED(file_info)) { 35 | return false; 36 | } 37 | 38 | static void io_append(GiraraInputHistoryIO* GIRARA_UNUSED(db), const char* GIRARA_UNUSED(input)) {} 39 | 40 | static girara_list_t* io_read(GiraraInputHistoryIO* GIRARA_UNUSED(db)) { 41 | return girara_list_new(); 42 | } 43 | 44 | static girara_list_t* get_recent_files(zathura_database_t* GIRARA_UNUSED(db), int GIRARA_UNUSED(max), 45 | const char* GIRARA_UNUSED(basepath)) { 46 | return girara_list_new(); 47 | } 48 | 49 | static void db_interface_init(ZathuraDatabaseInterface* iface) { 50 | /* initialize interface */ 51 | iface->add_bookmark = add_bookmark; 52 | iface->remove_bookmark = remove_bookmark; 53 | iface->load_bookmarks = load_list; 54 | iface->load_jumplist = load_list; 55 | iface->save_jumplist = save_list; 56 | iface->set_fileinfo = set_fileinfo; 57 | iface->get_fileinfo = get_fileinfo; 58 | iface->get_recent_files = get_recent_files; 59 | iface->load_quickmarks = load_list; 60 | iface->save_quickmarks = save_list; 61 | } 62 | 63 | static void io_interface_init(GiraraInputHistoryIOInterface* iface) { 64 | /* initialize interface */ 65 | iface->append = io_append; 66 | iface->read = io_read; 67 | } 68 | 69 | static void zathura_nulldatabase_class_init(ZathuraNullDatabaseClass* GIRARA_UNUSED(class)) {} 70 | 71 | static void zathura_nulldatabase_init(ZathuraNullDatabase* GIRARA_UNUSED(db)) {} 72 | 73 | G_DEFINE_TYPE_WITH_CODE(ZathuraNullDatabase, zathura_nulldatabase, G_TYPE_OBJECT, 74 | G_IMPLEMENT_INTERFACE(ZATHURA_TYPE_DATABASE, db_interface_init) 75 | G_IMPLEMENT_INTERFACE(GIRARA_TYPE_INPUT_HISTORY_IO, io_interface_init)) 76 | 77 | zathura_database_t* zathura_nulldatabase_new(void) { 78 | zathura_database_t* db = g_object_new(ZATHURA_TYPE_NULLDATABASE, NULL); 79 | return db; 80 | } 81 | -------------------------------------------------------------------------------- /zathura/jumplist.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef ZATHURA_JUMPLIST_H 4 | #define ZATHURA_JUMPLIST_H 5 | 6 | #include 7 | #include "types.h" 8 | 9 | typedef struct zathura_jumplist_s { 10 | girara_list_t* list; 11 | girara_list_iterator_t* cur; 12 | unsigned int size; 13 | unsigned int max_size; 14 | } zathura_jumplist_t; 15 | 16 | /** 17 | * Checks whether current jump has a previous jump 18 | * 19 | * @param zathura The zathura session 20 | * @return true if current jump has a previous jump 21 | */ 22 | bool zathura_jumplist_has_previous(zathura_t* zathura); 23 | 24 | /** 25 | * Checks whether current jump has a next jump 26 | * 27 | * @param zathura The zathura session 28 | * @return true if current jump has a next jump 29 | */ 30 | bool zathura_jumplist_has_next(zathura_t* zathura); 31 | 32 | /** 33 | * Return current jump in the jumplist 34 | * 35 | * @param zathura The zathura session 36 | * @return current jump 37 | */ 38 | zathura_jump_t* zathura_jumplist_current(zathura_t* zathura); 39 | 40 | /** 41 | * Move forward in the jumplist 42 | * 43 | * @param zathura The zathura session 44 | */ 45 | void zathura_jumplist_forward(zathura_t* zathura); 46 | 47 | /** 48 | * Move backward in the jumplist 49 | * 50 | * @param zathura The zathura session 51 | */ 52 | void zathura_jumplist_backward(zathura_t* zathura); 53 | 54 | /** 55 | * Add current page as a new item to the jumplist after current position 56 | * 57 | * @param zathura The zathura session 58 | */ 59 | void zathura_jumplist_add(zathura_t* zathura); 60 | 61 | /** 62 | * Trim entries from the beginning of the jumplist to maintain it's maximum size constraint. 63 | * 64 | * @param zathura The zathura session 65 | */ 66 | void zathura_jumplist_trim(zathura_t* zathura); 67 | 68 | /** 69 | * Set maximum jump list size (and trim if necessary) 70 | * 71 | * @param zathura The zathura session 72 | * @param max_size New maximum size 73 | */ 74 | void zathura_jumplist_set_max_size(zathura_t* zathura, size_t max_size); 75 | 76 | /** 77 | * Load the jumplist of the specified file 78 | * 79 | * @param zathura The zathura session 80 | * @param file The file whose jumplist is to be loaded 81 | * 82 | * return A linked list of zathura_jump_t structures constituting the jumplist of the specified file, or NULL. 83 | */ 84 | bool zathura_jumplist_load(zathura_t* zathura, const char* file); 85 | 86 | /** 87 | * Init jumplist with a maximum size 88 | * 89 | * @param zathura The zathura session 90 | * @param max_size maximum jumplist size (or 0 for unbounded lists) 91 | */ 92 | void zathura_jumplist_init(zathura_t* zathura, size_t max_size); 93 | 94 | /** 95 | * Check if the jumplist is initalized 96 | * 97 | * @param zathura The zathura session 98 | */ 99 | bool zathura_jumplist_is_initalized(zathura_t* zathura); 100 | 101 | /** 102 | * Clear jumplist 103 | * 104 | * After this operation, the jumplist is empty but initialized. 105 | * 106 | * @param zathura The zathura session 107 | */ 108 | void zathura_jumplist_clear(zathura_t* zathura); 109 | 110 | /** 111 | * Free jumplist 112 | * 113 | * @param zathura The zathura session 114 | */ 115 | void zathura_jumplist_free(zathura_t* zathura); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /zathura/plugin.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef PLUGIN_H 4 | #define PLUGIN_H 5 | 6 | #include 7 | #include 8 | 9 | #include "types.h" 10 | #include "plugin-api.h" 11 | #include "zathura-version.h" 12 | #include "zathura.h" 13 | 14 | /** 15 | * Creates a new instance of the plugin manager 16 | * 17 | * @return A plugin manager object or NULL if an error occurred 18 | */ 19 | zathura_plugin_manager_t* zathura_plugin_manager_new(void); 20 | 21 | /** 22 | * Frees the plugin manager 23 | * 24 | * @param plugin_manager 25 | */ 26 | void zathura_plugin_manager_free(zathura_plugin_manager_t* plugin_manager); 27 | 28 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(zathura_plugin_manager_t, zathura_plugin_manager_free) 29 | 30 | /** 31 | * Add colon-seperated list of directories to the plugin manager's plugin search path 32 | * 33 | * @param plugin_manager The plain manager 34 | * @param dir Colon-seperated list of directories 35 | */ 36 | void zathura_plugin_manager_set_dir(zathura_plugin_manager_t* plugin_manager, const char* dir); 37 | 38 | /** 39 | * Loads all plugins available in the previously given directories 40 | * 41 | * @param plugin_manager The plugin manager 42 | * @return Success if some plugins have been loaded, false otherwise 43 | */ 44 | bool zathura_plugin_manager_load(zathura_plugin_manager_t* plugin_manager); 45 | 46 | /** 47 | * Returns the (if available) associated plugin 48 | * 49 | * @param plugin_manager The plugin manager 50 | * @param type The document type 51 | * @return The plugin or NULL if no matching plugin is available 52 | */ 53 | const zathura_plugin_t* zathura_plugin_manager_get_plugin(const zathura_plugin_manager_t* plugin_manager, 54 | const char* type); 55 | 56 | /** 57 | * Returns a list with the plugin objects 58 | * 59 | * @param plugin_manager The plugin manager 60 | * @return List of plugins or NULL 61 | */ 62 | girara_list_t* zathura_plugin_manager_get_plugins(const zathura_plugin_manager_t* plugin_manager); 63 | 64 | /** 65 | * Return a list of supported content types 66 | * 67 | * @param plugin_manager The plugin manager 68 | * @return List of plugins or NULL 69 | */ 70 | girara_list_t* zathura_plugin_manager_get_content_types(const zathura_plugin_manager_t* plugin_manager); 71 | 72 | /** 73 | * Returns the plugin functions 74 | * 75 | * @param plugin The plugin 76 | * @return The plugin functions 77 | */ 78 | const zathura_plugin_functions_t* zathura_plugin_get_functions(const zathura_plugin_t* plugin); 79 | 80 | /** 81 | * Returns the name of the plugin 82 | * 83 | * @param plugin The plugin 84 | * @return The name of the plugin or NULL 85 | */ 86 | const char* zathura_plugin_get_name(const zathura_plugin_t* plugin); 87 | 88 | /** 89 | * Returns the path to the plugin 90 | * 91 | * @param plugin The plugin 92 | * @return The path of the plugin or NULL 93 | */ 94 | const char* zathura_plugin_get_path(const zathura_plugin_t* plugin); 95 | 96 | /** 97 | * Returns the version information of the plugin 98 | * 99 | * @param plugin The plugin 100 | * @return The version information of the plugin 101 | */ 102 | zathura_plugin_version_t zathura_plugin_get_version(const zathura_plugin_t* plugin); 103 | 104 | #endif // PLUGIN_H 105 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | 2 | env = [ 3 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 4 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 5 | ] 6 | 7 | test_dependencies = [ 8 | libzathura_dep, 9 | ] 10 | 11 | include_directories += [ include_directories('../zathura') ] 12 | 13 | document = executable('test_document', files('test_document.c'), 14 | dependencies: build_dependencies + test_dependencies, 15 | include_directories: include_directories, 16 | c_args: defines + flags 17 | ) 18 | test('document', document, 19 | timeout: 60*60, 20 | protocol: 'tap', 21 | env: env 22 | ) 23 | 24 | types = executable('test_types', files('test_types.c'), 25 | dependencies: build_dependencies + test_dependencies, 26 | include_directories: include_directories, 27 | c_args: defines + flags 28 | ) 29 | test('types', types, 30 | timeout: 60*60, 31 | protocol: 'tap', 32 | env: env 33 | ) 34 | 35 | utils = executable('test_utils', files('test_utils.c'), 36 | dependencies: build_dependencies + test_dependencies, 37 | include_directories: include_directories, 38 | c_args: defines + flags 39 | ) 40 | test('utils', utils, 41 | timeout: 60*60, 42 | protocol: 'tap', 43 | env: env 44 | ) 45 | 46 | xvfb = find_program('xvfb-run', required: get_option('tests')) 47 | weston = find_program('weston', required: get_option('tests')) 48 | if xvfb.found() or weston.found() 49 | session = executable('test_session', files('test_session.c'), 50 | dependencies: build_dependencies + test_dependencies, 51 | include_directories: include_directories, 52 | c_args: defines + flags 53 | ) 54 | endif 55 | 56 | if xvfb.found() 57 | xvfb_args = ['-s', '-screen 0 1400x900x24 -ac +extension GLX +render -noreset'] 58 | xvfb_h_output = run_command(xvfb, '-h', capture: true, check: false) 59 | if xvfb_h_output.stdout().contains('--auto-display') 60 | # because Arch and Fedora 61 | xvfb_args += ['-d'] 62 | else 63 | xvfb_args += ['-a'] 64 | endif 65 | 66 | test('xvfb_session', xvfb, 67 | args: xvfb_args + [session], 68 | timeout: 60*60, 69 | protocol: 'tap', 70 | env: env + [ 71 | 'NO_AT_BRIDGE=1', 72 | 'MESA_LOG=null', 73 | 'LIBGL_DEBUG=quiet' 74 | ] 75 | ) 76 | endif 77 | 78 | # Weston-headless test for Wayland environments 79 | if weston.found() 80 | weston_session = find_program(meson.current_source_dir() / 'weston_session.sh') 81 | 82 | test('weston_session', weston_session, 83 | args: [session], 84 | timeout: 60*60, 85 | protocol: 'tap', 86 | env: env + [ 87 | 'NO_AT_BRIDGE=1', 88 | 'WAYLAND_DISPLAY=zathura-test-weston' 89 | ] 90 | ) 91 | 92 | if seccomp.found() or landlock 93 | sandbox = executable('test_sandbox', files('test_sandbox.c'), 94 | dependencies: build_dependencies + [libzathura_sandbox_dep] + sandbox_dependencies, 95 | include_directories: include_directories, 96 | c_args: defines + sandbox_defines + flags 97 | ) 98 | 99 | test('weston_sandbox', weston_session, 100 | args: [sandbox], 101 | timeout: 60*60, 102 | protocol: 'tap', 103 | env: [ 104 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 105 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 106 | 'NO_AT_BRIDGE=1', 107 | 'WAYLAND_DISPLAY=zathura-test-weston' 108 | ] 109 | ) 110 | endif 111 | endif 112 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | VERBOSE: 1 9 | 10 | jobs: 11 | build-test-debian-trixie: 12 | name: Test on Debian trixie 13 | runs-on: ubuntu-latest 14 | container: 15 | image: ghcr.io/pwmt/github-actions-debian:trixie 16 | credentials: 17 | username: ${{ github.actor }} 18 | password: ${{ secrets.GITHUB_TOKEN }} 19 | steps: 20 | - uses: actions/checkout@v6 21 | - name: Build and test 22 | run: | 23 | mkdir build 24 | cd build 25 | meson .. 26 | ninja --verbose 27 | ninja test --verbose 28 | - name: Build and test (features disabled) 29 | run: | 30 | mkdir build-nofeatures 31 | cd build-nofeatures 32 | meson -Dsynctex=disabled -Dseccomp=disabled -Dlandlock=disabled .. 33 | ninja --verbose 34 | ninja test --verbose 35 | 36 | build-test-debian-ofrky: 37 | name: Test on Debian forky 38 | runs-on: ubuntu-latest 39 | container: 40 | image: ghcr.io/pwmt/github-actions-debian:forky 41 | credentials: 42 | username: ${{ github.actor }} 43 | password: ${{ secrets.GITHUB_TOKEN }} 44 | steps: 45 | - uses: actions/checkout@v6 46 | - name: Build and test 47 | run: | 48 | mkdir build 49 | cd build 50 | meson .. 51 | ninja --verbose 52 | ninja test --verbose 53 | - name: Build and test (features disabled) 54 | run: | 55 | mkdir build-nofeatures 56 | cd build-nofeatures 57 | meson -Dsynctex=disabled -Dseccomp=disabled -Dlandlock=disabled .. 58 | ninja --verbose 59 | ninja test --verbose 60 | 61 | build-test-ubuntu-noble: 62 | name: Test on Ubuntu noble 63 | runs-on: ubuntu-latest 64 | container: 65 | image: ghcr.io/pwmt/github-actions-ubuntu:noble 66 | credentials: 67 | username: ${{ github.actor }} 68 | password: ${{ secrets.GITHUB_TOKEN }} 69 | steps: 70 | - uses: actions/checkout@v6 71 | - name: Build and test 72 | run: | 73 | mkdir build 74 | cd build 75 | meson .. 76 | ninja --verbose 77 | ninja test --verbose 78 | - name: Build and test (features disabled) 79 | run: | 80 | mkdir build-nofeatures 81 | cd build-nofeatures 82 | meson -Dsynctex=disabled -Dseccomp=disabled -Dlandlock=disabled .. 83 | ninja --verbose 84 | ninja test --verbose 85 | 86 | build-test-archlinux: 87 | name: Test on Archlinux 88 | runs-on: ubuntu-latest 89 | container: 90 | image: ghcr.io/pwmt/github-actions-archlinux:latest 91 | credentials: 92 | username: ${{ github.actor }} 93 | password: ${{ secrets.GITHUB_TOKEN }} 94 | steps: 95 | - uses: actions/checkout@v6 96 | - name: Build and test 97 | run: | 98 | mkdir build 99 | cd build 100 | meson .. 101 | ninja --verbose 102 | ninja test --verbose 103 | - name: Build and test (features disabled) 104 | run: | 105 | mkdir build-nofeatures 106 | cd build-nofeatures 107 | meson -Dsynctex=disabled -Dseccomp=disabled -Dlandlock=disabled .. 108 | ninja --verbose 109 | ninja test --verbose 110 | -------------------------------------------------------------------------------- /zathura/types.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types.h" 8 | #include "links.h" 9 | #include "internal.h" 10 | 11 | zathura_index_element_t* zathura_index_element_new(const char* title) { 12 | if (title == NULL) { 13 | return NULL; 14 | } 15 | 16 | zathura_index_element_t* res = g_try_malloc0(sizeof(zathura_index_element_t)); 17 | if (res == NULL) { 18 | return NULL; 19 | } 20 | 21 | res->title = g_strdup(title); 22 | 23 | return res; 24 | } 25 | 26 | void zathura_index_element_free(zathura_index_element_t* index) { 27 | if (index == NULL) { 28 | return; 29 | } 30 | 31 | g_free(index->title); 32 | zathura_link_free(index->link); 33 | g_free(index); 34 | } 35 | 36 | zathura_image_buffer_t* zathura_image_buffer_create(unsigned int width, unsigned int height) { 37 | g_return_val_if_fail(width != 0, NULL); 38 | g_return_val_if_fail(height != 0, NULL); 39 | 40 | unsigned int size = 0; 41 | if (checked_umul(width, height, &size) == true || checked_umul(size, 3, &size) == true) { 42 | return NULL; 43 | } 44 | 45 | zathura_image_buffer_t* image_buffer = g_try_malloc(sizeof(zathura_image_buffer_t)); 46 | if (image_buffer == NULL) { 47 | return NULL; 48 | } 49 | 50 | image_buffer->data = g_try_malloc0_n(size, sizeof(unsigned char)); 51 | 52 | if (image_buffer->data == NULL) { 53 | g_free(image_buffer); 54 | return NULL; 55 | } 56 | 57 | image_buffer->width = width; 58 | image_buffer->height = height; 59 | image_buffer->rowstride = width * 3; 60 | 61 | return image_buffer; 62 | } 63 | 64 | void zathura_image_buffer_free(zathura_image_buffer_t* image_buffer) { 65 | if (image_buffer == NULL) { 66 | return; 67 | } 68 | 69 | g_free(image_buffer->data); 70 | g_free(image_buffer); 71 | } 72 | 73 | static void document_information_entry_free(void* data) { 74 | zathura_document_information_entry_t* entry = data; 75 | zathura_document_information_entry_free(entry); 76 | } 77 | 78 | girara_list_t* zathura_document_information_entry_list_new(void) { 79 | return girara_list_new_with_free(document_information_entry_free); 80 | } 81 | 82 | zathura_document_information_entry_t* zathura_document_information_entry_new(zathura_document_information_type_t type, 83 | const char* value) { 84 | if (value == NULL) { 85 | return NULL; 86 | } 87 | 88 | zathura_document_information_entry_t* entry = g_try_malloc0(sizeof(zathura_document_information_entry_t)); 89 | if (entry == NULL) { 90 | return NULL; 91 | } 92 | 93 | entry->type = type; 94 | entry->value = g_strdup(value); 95 | 96 | return entry; 97 | } 98 | 99 | void zathura_document_information_entry_free(zathura_document_information_entry_t* entry) { 100 | if (entry == NULL) { 101 | return; 102 | } 103 | 104 | g_free(entry->value); 105 | g_free(entry); 106 | } 107 | 108 | zathura_signature_info_t* zathura_signature_info_new(void) { 109 | return g_try_malloc0(sizeof(zathura_signature_info_t)); 110 | } 111 | 112 | void zathura_signature_info_free(zathura_signature_info_t* signature) { 113 | if (signature == NULL) { 114 | return; 115 | } 116 | 117 | g_free(signature->signer); 118 | if (signature->time) { 119 | g_date_time_unref(signature->time); 120 | } 121 | g_free(signature); 122 | } 123 | -------------------------------------------------------------------------------- /zathura/document-widget.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef DOCUMENT_WIDGET_H 4 | #define DOCUMENT_WIDGET_H 5 | 6 | #include 7 | #include "types.h" 8 | 9 | /** 10 | * The document view widget. Places a subset of the pages of 11 | * the document into a grid. The widget handles updating the 12 | * grid to contain the pages in view, and as many pages around 13 | * the view as the cairo surface will allow. 14 | * 15 | * zathura_document_widget_[get_ratio|set_value|set_value_from_ratio] 16 | * functions replace the equivalent ones previously contained in 17 | * adjustment.c. They wrap these functions and perform the necessary 18 | * transformations to move between a ratio of [0,1] in the whole document, 19 | * to a ratio in the subset of the pages contained in the document widgets' 20 | * grid. 21 | * 22 | * */ 23 | struct zathura_document_widget_s { 24 | GtkGrid parent; 25 | }; 26 | 27 | struct zathura_document_widget_class_s { 28 | GtkGridClass parent_class; 29 | }; 30 | 31 | #define ZATHURA_TYPE_DOCUMENT_WIDGET (zathura_document_widget_get_type()) 32 | #define ZATHURA_DOCUMENT_WIDGET(obj) \ 33 | (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_DOCUMENT_WIDGET, ZathuraDocumentWidget)) 34 | #define ZATHURA_DOCUMENT_WIDGET_CLASS(obj) \ 35 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_DOCUMENT_WIDGET, ZathuraDocumentWidgetClass)) 36 | #define ZATHURA_IS_DOCUMENT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_DOCUMENT_WIDGET)) 37 | #define ZATHURA_IS_DOCUMENT_WIDGET_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_DOCUMENT_WIDGET)) 38 | #define ZATHURA_DOCUMENT_WIDGET_GET_CLASS(obj) \ 39 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_DOCUMENT_WIDGET, ZathuraDocumentWidgetClass)) 40 | 41 | /** 42 | * Returns the type of the document view widget. 43 | * 44 | * @return the type 45 | */ 46 | GType zathura_document_widget_get_type(void) G_GNUC_CONST; 47 | 48 | /** 49 | * Create a document view widget. 50 | * 51 | * @param zathura the zathura instance 52 | * @return a document view widget 53 | */ 54 | GtkWidget* zathura_document_widget_new(void); 55 | 56 | /** 57 | * Builds the box structure to show the rendered pages 58 | * 59 | * @param zathura The zathura session 60 | * @param page_right_to_left Render pages right to left 61 | */ 62 | void zathura_document_widget_set_mode(zathura_t* zathura, bool page_right_to_left); 63 | 64 | /** 65 | * Update the pages in the document view 66 | * 67 | * @param widget the document view widget 68 | */ 69 | void zathura_document_widget_render(zathura_t* zathura); 70 | 71 | /** 72 | * Clear pages from the document view 73 | * 74 | * @param widget the document view widget 75 | */ 76 | void zathura_document_widget_clear_pages(GtkWidget* widget); 77 | 78 | /** 79 | * Compute the adjustment ratio 80 | * 81 | * That is, the ratio between the length from the lower bound to the middle of 82 | * the slider, and the total length of the scrollbar. 83 | * 84 | * @param adjustment Scrollbar adjustment 85 | * @param width Is the adjustment for width? 86 | * @return Adjustment ratio 87 | */ 88 | gdouble zathura_document_widget_get_ratio(zathura_t* zathura, GtkAdjustment* adjustment, bool width); 89 | 90 | /** 91 | * Set the adjustment value from ratio 92 | * 93 | * The ratio is usually obtained from a previous call to 94 | * zathura_document_widget_get_ratio(). 95 | * 96 | * @param adjustment Adjustment instance 97 | * @param ratio Ratio from which the adjustment value will be set 98 | * @param width Is the adjustment for width? 99 | */ 100 | void zathura_document_widget_set_value_from_ratio(zathura_t* zathura, GtkAdjustment* adjustment, double ratio, 101 | bool width); 102 | #endif // DOCUMENT_WIDGET_H 103 | -------------------------------------------------------------------------------- /zathura/bookmarks.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include "bookmarks.h" 5 | #include "database.h" 6 | #include "document.h" 7 | #include "adjustment.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | static int bookmark_compare_find(const void* item, const void* data) { 14 | const zathura_bookmark_t* bookmark = item; 15 | const char* id = data; 16 | 17 | return g_strcmp0(bookmark->id, id); 18 | } 19 | 20 | zathura_bookmark_t* zathura_bookmark_add(zathura_t* zathura, const gchar* id, unsigned int page) { 21 | g_return_val_if_fail(zathura_has_document(zathura) == true && zathura->bookmarks.bookmarks, NULL); 22 | g_return_val_if_fail(id, NULL); 23 | 24 | zathura_document_t* document = zathura_get_document(zathura); 25 | double position_x = zathura_document_get_position_x(document); 26 | double position_y = zathura_document_get_position_y(document); 27 | zathura_bookmark_t* old = zathura_bookmark_get(zathura, id); 28 | 29 | if (old != NULL) { 30 | old->page = page; 31 | old->x = position_x; 32 | old->y = position_y; 33 | 34 | const char* path = zathura_document_get_path(document); 35 | if (zathura_db_remove_bookmark(zathura->database, path, old->id) == false) { 36 | girara_warning("Failed to remove old bookmark from database."); 37 | } 38 | 39 | if (zathura_db_add_bookmark(zathura->database, path, old) == false) { 40 | girara_warning("Failed to add new bookmark to database."); 41 | } 42 | 43 | return old; 44 | } 45 | 46 | zathura_bookmark_t* bookmark = g_try_malloc0(sizeof(zathura_bookmark_t)); 47 | if (bookmark == NULL) { 48 | return NULL; 49 | } 50 | 51 | bookmark->id = g_strdup(id); 52 | bookmark->page = page; 53 | bookmark->x = position_x; 54 | bookmark->y = position_y; 55 | girara_list_append(zathura->bookmarks.bookmarks, bookmark); 56 | 57 | const char* path = zathura_document_get_path(document); 58 | if (zathura_db_add_bookmark(zathura->database, path, bookmark) == false) { 59 | girara_warning("Failed to add bookmark to database."); 60 | } 61 | 62 | return bookmark; 63 | } 64 | 65 | bool zathura_bookmark_remove(zathura_t* zathura, const gchar* id) { 66 | g_return_val_if_fail(zathura_has_document(zathura) == true && zathura->bookmarks.bookmarks, false); 67 | g_return_val_if_fail(id, false); 68 | 69 | zathura_bookmark_t* bookmark = zathura_bookmark_get(zathura, id); 70 | if (bookmark == NULL) { 71 | return false; 72 | } 73 | 74 | const char* path = zathura_document_get_path(zathura_get_document(zathura)); 75 | if (zathura_db_remove_bookmark(zathura->database, path, bookmark->id) == false) { 76 | girara_warning("Failed to remove bookmark from database."); 77 | } 78 | 79 | girara_list_remove(zathura->bookmarks.bookmarks, bookmark); 80 | 81 | return true; 82 | } 83 | 84 | zathura_bookmark_t* zathura_bookmark_get(zathura_t* zathura, const gchar* id) { 85 | g_return_val_if_fail(zathura && zathura->bookmarks.bookmarks, NULL); 86 | g_return_val_if_fail(id, NULL); 87 | 88 | return girara_list_find(zathura->bookmarks.bookmarks, bookmark_compare_find, id); 89 | } 90 | 91 | void zathura_bookmark_free(zathura_bookmark_t* bookmark) { 92 | if (bookmark != NULL) { 93 | g_free(bookmark->id); 94 | g_free(bookmark); 95 | } 96 | } 97 | 98 | bool zathura_bookmarks_load(zathura_t* zathura, const gchar* file) { 99 | g_return_val_if_fail(zathura, false); 100 | g_return_val_if_fail(file, false); 101 | 102 | if (zathura->database == NULL) { 103 | return false; 104 | } 105 | 106 | girara_list_t* bookmarks = zathura_db_load_bookmarks(zathura->database, file); 107 | if (bookmarks == NULL) { 108 | return false; 109 | } 110 | 111 | girara_list_free(zathura->bookmarks.bookmarks); 112 | zathura->bookmarks.bookmarks = bookmarks; 113 | 114 | return true; 115 | } 116 | 117 | int zathura_bookmarks_compare(const zathura_bookmark_t* lhs, const zathura_bookmark_t* rhs) { 118 | if (lhs == NULL && rhs == NULL) { 119 | return 0; 120 | } 121 | 122 | if (lhs == NULL) { 123 | return -1; 124 | } 125 | 126 | if (rhs == NULL) { 127 | return 1; 128 | } 129 | 130 | return g_strcmp0(lhs->id, rhs->id); 131 | } 132 | -------------------------------------------------------------------------------- /zathura/landlock.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #define _GNU_SOURCE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "landlock.h" 13 | 14 | #ifndef landlock_create_ruleset 15 | static inline int landlock_create_ruleset(const struct landlock_ruleset_attr* const attr, const size_t size, 16 | const __u32 flags) { 17 | return syscall(__NR_landlock_create_ruleset, attr, size, flags); 18 | } 19 | #endif 20 | 21 | #ifndef landlock_add_rule 22 | static inline int landlock_add_rule(const int ruleset_fd, const enum landlock_rule_type rule_type, 23 | const void* const rule_attr, const __u32 flags) { 24 | return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags); 25 | } 26 | #endif 27 | 28 | #ifndef landlock_restrict_self 29 | static inline int landlock_restrict_self(const int ruleset_fd, const __u32 flags) { 30 | return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 31 | } 32 | #endif 33 | 34 | static void landlock_drop(__u64 fs_access) { 35 | const struct landlock_ruleset_attr ruleset_attr = { 36 | .handled_access_fs = fs_access, 37 | }; 38 | 39 | int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 40 | if (ruleset_fd < 0) { 41 | girara_error("Failed to create a landlock ruleset"); 42 | return; 43 | } 44 | prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 45 | if (landlock_restrict_self(ruleset_fd, 0)) { 46 | girara_error("landlock_restrict_self"); 47 | } 48 | close(ruleset_fd); 49 | } 50 | 51 | #define _LANDLOCK_ACCESS_FS_WRITE \ 52 | (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 53 | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | \ 54 | LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 55 | LANDLOCK_ACCESS_FS_MAKE_SYM) 56 | 57 | #define _LANDLOCK_ACCESS_FS_READ (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR) 58 | 59 | static void landlock_check_kernel(void) { 60 | int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); 61 | if (abi == -1) { 62 | /* 63 | * Kernel too old, not compiled with Landlock, 64 | * or Landlock was not enabled at boot time. 65 | */ 66 | girara_warning("Unable to use Landlock: Kernel too old, not compiled with Landlock,\ 67 | or Landlock was not enabled at boot time. Sandbox partly disabled."); 68 | return; /* Graceful fallback: Do nothing. */ 69 | } 70 | } 71 | 72 | void landlock_drop_write(void) { 73 | landlock_check_kernel(); 74 | landlock_drop(_LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE); 75 | } 76 | 77 | #if 0 78 | static void landlock_drop_all(void) { 79 | landlock_drop(_LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE); 80 | } 81 | #endif 82 | 83 | static void landlock_write_fd(const int dir_fd) { 84 | const struct landlock_ruleset_attr ruleset_attr = { 85 | .handled_access_fs = _LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE, 86 | }; 87 | struct landlock_path_beneath_attr path_beneath = { 88 | .allowed_access = _LANDLOCK_ACCESS_FS_WRITE, 89 | }; 90 | 91 | int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 92 | if (ruleset_fd < 0) { 93 | return; 94 | } 95 | if (dir_fd == -1) { 96 | path_beneath.parent_fd = open(".", O_PATH | O_CLOEXEC | O_DIRECTORY); 97 | } else { 98 | path_beneath.parent_fd = dir_fd; 99 | } 100 | 101 | if (!landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) { 102 | prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 103 | if (landlock_restrict_self(ruleset_fd, 0)) { 104 | girara_error("landlock_restrict_self"); 105 | } 106 | } else { 107 | girara_error("landlock_add_rule"); 108 | } 109 | 110 | if (dir_fd == -1) { 111 | close(path_beneath.parent_fd); 112 | } 113 | close(ruleset_fd); 114 | } 115 | 116 | void landlock_restrict_write(void) { 117 | landlock_check_kernel(); 118 | g_autofree char* data_path = girara_get_xdg_path(XDG_DATA); 119 | int fd = open(data_path, O_PATH | O_CLOEXEC | O_DIRECTORY); 120 | landlock_write_fd(fd); 121 | close(fd); 122 | } 123 | -------------------------------------------------------------------------------- /zathura/file-monitor.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "file-monitor.h" 4 | #include "file-monitor-glib.h" 5 | #ifdef G_OS_UNIX 6 | #include "file-monitor-signal.h" 7 | #endif 8 | #include "file-monitor-noop.h" 9 | #include "macros.h" 10 | 11 | #include 12 | 13 | typedef struct { 14 | char* file_path; 15 | } ZathuraFileMonitorPrivate; 16 | 17 | G_DEFINE_TYPE_WITH_CODE(ZathuraFileMonitor, zathura_filemonitor, G_TYPE_OBJECT, G_ADD_PRIVATE(ZathuraFileMonitor)) 18 | 19 | enum { 20 | PROP_0, 21 | PROP_FILE_PATH, 22 | }; 23 | 24 | static void finalize(GObject* object) { 25 | ZathuraFileMonitor* file_monitor = ZATHURA_FILEMONITOR(object); 26 | ZathuraFileMonitorPrivate* priv = zathura_filemonitor_get_instance_private(file_monitor); 27 | 28 | if (priv->file_path != NULL) { 29 | g_free(priv->file_path); 30 | } 31 | 32 | G_OBJECT_CLASS(zathura_filemonitor_parent_class)->finalize(object); 33 | } 34 | 35 | static void set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { 36 | ZathuraFileMonitor* file_monitor = ZATHURA_FILEMONITOR(object); 37 | ZathuraFileMonitorPrivate* priv = zathura_filemonitor_get_instance_private(file_monitor); 38 | 39 | switch (prop_id) { 40 | case PROP_FILE_PATH: 41 | if (priv->file_path != NULL) { 42 | g_free(priv->file_path); 43 | } 44 | priv->file_path = g_value_dup_string(value); 45 | break; 46 | default: 47 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 48 | } 49 | } 50 | 51 | static void get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { 52 | ZathuraFileMonitor* file_monitor = ZATHURA_FILEMONITOR(object); 53 | ZathuraFileMonitorPrivate* priv = zathura_filemonitor_get_instance_private(file_monitor); 54 | 55 | switch (prop_id) { 56 | case PROP_FILE_PATH: 57 | g_value_set_string(value, priv->file_path); 58 | break; 59 | default: 60 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 61 | } 62 | } 63 | 64 | static void zathura_filemonitor_class_init(ZathuraFileMonitorClass* class) { 65 | /* set up methods */ 66 | class->start = NULL; 67 | class->stop = NULL; 68 | 69 | GObjectClass* object_class = G_OBJECT_CLASS(class); 70 | object_class->finalize = finalize; 71 | object_class->set_property = set_property; 72 | object_class->get_property = get_property; 73 | 74 | /* add properties */ 75 | g_object_class_install_property( 76 | object_class, PROP_FILE_PATH, 77 | g_param_spec_string("file-path", "file-path", "file path to monitor", NULL, 78 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); 79 | 80 | /* add signals */ 81 | g_signal_new("reload-file", ZATHURA_TYPE_FILEMONITOR, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, 82 | G_TYPE_NONE, 0); 83 | } 84 | 85 | static void zathura_filemonitor_init(ZathuraFileMonitor* file_monitor) { 86 | ZathuraFileMonitorPrivate* priv = zathura_filemonitor_get_instance_private(file_monitor); 87 | priv->file_path = NULL; 88 | } 89 | 90 | const char* zathura_filemonitor_get_filepath(ZathuraFileMonitor* file_monitor) { 91 | ZathuraFileMonitorPrivate* priv = zathura_filemonitor_get_instance_private(file_monitor); 92 | return priv->file_path; 93 | } 94 | 95 | void zathura_filemonitor_start(ZathuraFileMonitor* file_monitor) { 96 | ZATHURA_FILEMONITOR_GET_CLASS(file_monitor)->start(file_monitor); 97 | } 98 | 99 | void zathura_filemonitor_stop(ZathuraFileMonitor* file_monitor) { 100 | ZATHURA_FILEMONITOR_GET_CLASS(file_monitor)->stop(file_monitor); 101 | } 102 | 103 | ZathuraFileMonitor* zathura_filemonitor_new(const char* file_path, zathura_filemonitor_type_t filemonitor_type) { 104 | g_return_val_if_fail(file_path != NULL, NULL); 105 | 106 | GObject* ret = NULL; 107 | switch (filemonitor_type) { 108 | case ZATHURA_FILEMONITOR_GLIB: 109 | girara_debug("using glib file monitor"); 110 | ret = g_object_new(ZATHURA_TYPE_GLIBFILEMONITOR, "file-path", file_path, NULL); 111 | break; 112 | #ifdef G_OS_UNIX 113 | case ZATHURA_FILEMONITOR_SIGNAL: 114 | girara_debug("using SIGHUP file monitor"); 115 | ret = g_object_new(ZATHURA_TYPE_SIGNALFILEMONITOR, "file-path", file_path, NULL); 116 | break; 117 | #endif 118 | case ZATHURA_FILEMONITOR_NOOP: 119 | girara_debug("using noop file monitor"); 120 | ret = g_object_new(ZATHURA_TYPE_NOOPFILEMONITOR, "file-path", file_path, NULL); 121 | break; 122 | default: 123 | girara_debug("invalid filemonitor type: %d", filemonitor_type); 124 | g_return_val_if_fail(false, NULL); 125 | } 126 | 127 | if (ret == NULL) { 128 | return NULL; 129 | } 130 | 131 | return ZATHURA_FILEMONITOR(ret); 132 | } 133 | -------------------------------------------------------------------------------- /zathura/content-type.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "content-type.h" 4 | #include "macros.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct zathura_content_type_context_s { 13 | magic_t magic; 14 | }; 15 | 16 | zathura_content_type_context_t* zathura_content_type_new(void) { 17 | zathura_content_type_context_t* context = g_try_malloc0(sizeof(zathura_content_type_context_t)); 18 | if (context == NULL) { 19 | return NULL; 20 | } 21 | 22 | /* creat magic cookie */ 23 | static const int flags = MAGIC_ERROR | MAGIC_MIME_TYPE | MAGIC_SYMLINK | MAGIC_NO_CHECK_APPTYPE | MAGIC_NO_CHECK_CDF | 24 | MAGIC_NO_CHECK_ELF | MAGIC_NO_CHECK_ENCODING; 25 | magic_t magic = magic_open(flags); 26 | if (magic == NULL) { 27 | girara_debug("failed creating the magic cookie"); 28 | return context; 29 | } 30 | 31 | /* ... and load mime database */ 32 | if (magic_load(magic, NULL) < 0) { 33 | girara_debug("failed loading the magic database: %s", magic_error(magic)); 34 | magic_close(magic); 35 | return context; 36 | } 37 | 38 | context->magic = magic; 39 | return context; 40 | } 41 | 42 | void zathura_content_type_free(zathura_content_type_context_t* context) { 43 | if (context != NULL && context->magic != NULL) { 44 | magic_close(context->magic); 45 | } 46 | 47 | g_free(context); 48 | } 49 | 50 | /** Read a most GT_MAX_READ bytes before falling back to file. */ 51 | static const size_t GT_MAX_READ = 1 << 16; 52 | 53 | static char* guess_type_magic(zathura_content_type_context_t* context, const char* path) { 54 | if (context == NULL || context->magic == NULL) { 55 | return NULL; 56 | } 57 | 58 | const char* mime_type = NULL; 59 | 60 | /* get the mime type */ 61 | mime_type = magic_file(context->magic, path); 62 | if (mime_type == NULL || magic_errno(context->magic) != 0) { 63 | girara_debug("failed guessing filetype: %s", magic_error(context->magic)); 64 | return NULL; 65 | } 66 | girara_debug("magic detected filetype: %s", mime_type); 67 | 68 | char* content_type = g_content_type_from_mime_type(mime_type); 69 | if (content_type == NULL) { 70 | girara_warning("failed to convert mime type to content type: %s", mime_type); 71 | /* dup so we own the memory */ 72 | return g_strdup(mime_type); 73 | } 74 | 75 | return content_type; 76 | } 77 | 78 | static char* guess_type_glib(const char* path) { 79 | gboolean uncertain = FALSE; 80 | char* content_type = g_content_type_guess(path, NULL, 0, &uncertain); 81 | if (content_type == NULL) { 82 | girara_debug("g_content_type failed\n"); 83 | } else { 84 | if (uncertain == FALSE) { 85 | girara_debug("g_content_type detected filetype: %s", content_type); 86 | return content_type; 87 | } 88 | girara_debug("g_content_type is uncertain, guess: %s", content_type); 89 | } 90 | 91 | g_free(content_type); 92 | content_type = NULL; 93 | 94 | GMappedFile* f = g_mapped_file_new(path, FALSE, NULL); 95 | if (f == NULL) { 96 | return NULL; 97 | } 98 | 99 | content_type = g_content_type_guess(NULL, (const guchar*)g_mapped_file_get_contents(f), 100 | MIN(g_mapped_file_get_length(f), GT_MAX_READ), &uncertain); 101 | girara_debug("new guess: %s uncertain: %d", content_type, uncertain); 102 | g_mapped_file_unref(f); 103 | if (uncertain == FALSE) { 104 | return content_type; 105 | } 106 | 107 | g_free(content_type); 108 | return NULL; 109 | } 110 | 111 | static int compare_content_types(const void* lhs, const void* rhs) { 112 | return g_strcmp0(lhs, rhs); 113 | } 114 | 115 | char* zathura_content_type_guess(zathura_content_type_context_t* context, const char* path, 116 | const girara_list_t* supported_content_types) { 117 | /* try libmagic first */ 118 | char* content_type = guess_type_magic(context, path); 119 | if (content_type != NULL) { 120 | if (supported_content_types == NULL || 121 | girara_list_find(supported_content_types, compare_content_types, content_type) != NULL) { 122 | return content_type; 123 | } 124 | girara_debug("content type '%s' not supported, trying again", content_type); 125 | g_free(content_type); 126 | } 127 | /* else fallback to g_content_type_guess method */ 128 | content_type = guess_type_glib(path); 129 | if (content_type != NULL) { 130 | if (supported_content_types == NULL || 131 | girara_list_find(supported_content_types, compare_content_types, content_type) != NULL) { 132 | return content_type; 133 | } 134 | girara_debug("content type '%s' not supported", content_type); 135 | g_free(content_type); 136 | } 137 | return NULL; 138 | } 139 | -------------------------------------------------------------------------------- /zathura/adjustment.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "adjustment.h" 4 | #include "utils.h" 5 | 6 | #include 7 | 8 | double page_calc_height_width(zathura_document_t* document, double height, double width, unsigned int* page_height, 9 | unsigned int* page_width, bool rotate) { 10 | g_return_val_if_fail(document != NULL && page_height != NULL && page_width != NULL, 0.0); 11 | 12 | double scale = zathura_document_get_scale(document); 13 | 14 | // TODO this just set all pages to the maximum. 15 | // needs to adjust cell size based on the page size itself. 16 | if (rotate == true && zathura_document_get_rotation(document) % 180 != 0) { 17 | *page_width = round(height * scale); 18 | *page_height = round(width * scale); 19 | scale = MAX(*page_width / height, *page_height / width); 20 | } else { 21 | *page_width = round(width * scale); 22 | *page_height = round(height * scale); 23 | scale = MAX(*page_width / width, *page_height / height); 24 | } 25 | 26 | return scale; 27 | } 28 | 29 | void page_calc_position(zathura_document_t* document, double x, double y, double* xn, double* yn) { 30 | g_return_if_fail(document != NULL && xn != NULL && yn != NULL); 31 | 32 | const unsigned int rot = zathura_document_get_rotation(document); 33 | if (rot == 90) { 34 | *xn = 1 - y; 35 | *yn = x; 36 | } else if (rot == 180) { 37 | *xn = 1 - x; 38 | *yn = 1 - y; 39 | } else if (rot == 270) { 40 | *xn = y; 41 | *yn = 1 - x; 42 | } else { 43 | *xn = x; 44 | *yn = y; 45 | } 46 | } 47 | 48 | unsigned int position_to_page_number(zathura_document_t* document, double pos_x, double pos_y) { 49 | g_return_val_if_fail(document != NULL, 0); 50 | 51 | unsigned int doc_width, doc_height; 52 | zathura_document_get_document_size(document, &doc_height, &doc_width); 53 | 54 | unsigned int cell_width, cell_height; 55 | zathura_document_get_cell_size(document, &cell_height, &cell_width); 56 | 57 | unsigned int c0 = zathura_document_get_first_page_column(document); 58 | unsigned int npag = zathura_document_get_number_of_pages(document); 59 | unsigned int ncol = zathura_document_get_pages_per_row(document); 60 | unsigned int nrow = 0; 61 | unsigned int v_padding = zathura_document_get_page_v_padding(document); 62 | unsigned int h_padding = zathura_document_get_page_h_padding(document); 63 | 64 | if (c0 == 1) { 65 | /* There is no offset, so this is easy. */ 66 | nrow = (npag + ncol - 1) / ncol; 67 | } else { 68 | /* If there is a offset, we handle the first row extra. */ 69 | nrow = 1 + (npag - (ncol - c0 - 1) + (ncol - 1)) / ncol; 70 | } 71 | 72 | unsigned int col = floor(pos_x * (double)doc_width / (double)(cell_width + h_padding)); 73 | unsigned int row = floor(pos_y * (double)doc_height / (double)(cell_height + v_padding)); 74 | 75 | unsigned int page = ncol * (row % nrow) + (col % ncol); 76 | if (page < c0 - 1) { 77 | return 0; 78 | } else { 79 | return MIN(page - (c0 - 1), npag - 1); 80 | } 81 | } 82 | 83 | void page_number_to_position(zathura_document_t* document, unsigned int page_number, double xalign, double yalign, 84 | double* pos_x, double* pos_y) { 85 | g_return_if_fail(document != NULL); 86 | 87 | unsigned int c0 = zathura_document_get_first_page_column(document); 88 | unsigned int ncol = zathura_document_get_pages_per_row(document); 89 | 90 | /* row and column for page_number indexed from 0 */ 91 | unsigned int row = (page_number + c0 - 1) / ncol; 92 | unsigned int col = (page_number + c0 - 1) % ncol; 93 | 94 | /* sizes of page cell, viewport and document */ 95 | unsigned int cell_height = 0, cell_width = 0; 96 | zathura_document_get_cell_size(document, &cell_height, &cell_width); 97 | 98 | unsigned int view_height = 0, view_width = 0; 99 | zathura_document_get_viewport_size(document, &view_height, &view_width); 100 | 101 | unsigned int doc_height = 0, doc_width = 0; 102 | zathura_document_get_document_size(document, &doc_height, &doc_width); 103 | 104 | /* compute the shift to align to the viewport. If the page fits to viewport, just center it. */ 105 | double shift_x = 0.5, shift_y = 0.5; 106 | if (cell_width > view_width) { 107 | shift_x = 0.5 + (xalign - 0.5) * ((double)cell_width - (double)view_width) / (double)cell_width; 108 | } 109 | 110 | if (cell_height > view_height) { 111 | shift_y = 0.5 + (yalign - 0.5) * ((double)cell_height - (double)view_height) / (double)cell_height; 112 | } 113 | 114 | const unsigned int v_padding = zathura_document_get_page_v_padding(document); 115 | const unsigned int h_padding = zathura_document_get_page_h_padding(document); 116 | 117 | /* compute the position */ 118 | *pos_x = ((double)col * (cell_width + h_padding) + shift_x * cell_width) / (double) doc_width; 119 | *pos_y = ((double)row * (cell_height + v_padding) + shift_y * cell_height) / (double) doc_height; 120 | } 121 | 122 | bool page_is_visible(zathura_document_t* document, unsigned int page_number) { 123 | g_return_val_if_fail(document != NULL, false); 124 | 125 | /* position at the center of the viewport */ 126 | double pos_x = zathura_document_get_position_x(document); 127 | double pos_y = zathura_document_get_position_y(document); 128 | 129 | /* get the center of page page_number */ 130 | double page_x, page_y; 131 | page_number_to_position(document, page_number, 0.5, 0.5, &page_x, &page_y); 132 | 133 | unsigned int cell_width, cell_height; 134 | zathura_document_get_cell_size(document, &cell_height, &cell_width); 135 | 136 | unsigned int doc_width, doc_height; 137 | zathura_document_get_document_size(document, &doc_height, &doc_width); 138 | 139 | unsigned int view_width, view_height; 140 | zathura_document_get_viewport_size(document, &view_height, &view_width); 141 | 142 | return (fabs(pos_x - page_x) < 0.5 * (double)(view_width + cell_width) / (double)doc_width && 143 | fabs(pos_y - page_y) < 0.5 * (double)(view_height + cell_height) / (double)doc_height); 144 | } 145 | -------------------------------------------------------------------------------- /zathura/commands.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef COMMANDS_H 4 | #define COMMANDS_H 5 | 6 | #include 7 | #include 8 | 9 | /** 10 | * Create a bookmark 11 | * 12 | * @param session The used girara session 13 | * @param argument_list List of passed arguments 14 | * @return true if no error occurred 15 | */ 16 | bool cmd_bookmark_create(girara_session_t* session, girara_list_t* argument_list); 17 | 18 | /** 19 | * Delete a bookmark 20 | * 21 | * @param session The used girara session 22 | * @param argument_list List of passed arguments 23 | * @return true if no error occurred 24 | */ 25 | bool cmd_bookmark_delete(girara_session_t* session, girara_list_t* argument_list); 26 | 27 | /** 28 | * List bookmarks 29 | * 30 | * @param session The used girara session 31 | * @param argument_list List of passed arguments 32 | * @return true if no error occurred 33 | */ 34 | bool cmd_bookmark_list(girara_session_t* session, girara_list_t* argument_list); 35 | 36 | /** 37 | * Open a bookmark 38 | * 39 | * @param session The used girara session 40 | * @param argument_list List of passed arguments 41 | * @return true if no error occurred 42 | */ 43 | bool cmd_bookmark_open(girara_session_t* session, girara_list_t* argument_list); 44 | 45 | /** 46 | * Show recent jumps in jumplist 47 | * 48 | * @param session The used girara session 49 | * @param argument_list List of passed arguments 50 | * @return true if no error occurred 51 | */ 52 | bool cmd_jumplist_list(girara_session_t* session, girara_list_t* argument_list); 53 | 54 | /** 55 | * Close zathura 56 | * 57 | * @param session The used girara session 58 | * @param argument_list List of passed arguments 59 | * @return true if no error occurred 60 | */ 61 | bool cmd_close(girara_session_t* session, girara_list_t* argument_list); 62 | 63 | /** 64 | * Display document information 65 | * 66 | * @param session The used girara session 67 | * @param argument_list List of passed arguments 68 | * @return true if no error occurred 69 | */ 70 | bool cmd_info(girara_session_t* session, girara_list_t* argument_list); 71 | 72 | /** 73 | * Display help 74 | * 75 | * @param session The used girara session 76 | * @param argument_list List of passed arguments 77 | * @return true if no error occurred 78 | */ 79 | bool cmd_help(girara_session_t* session, girara_list_t* argument_list); 80 | 81 | /** 82 | * Shows current search results 83 | * 84 | * @param session The used girara session 85 | * @param argument_list List of passed arguments 86 | * @return true if no error occurred 87 | */ 88 | bool cmd_hlsearch(girara_session_t* session, girara_list_t* argument_list); 89 | 90 | /** 91 | * Opens a document file 92 | * 93 | * @param session The used girara session 94 | * @param argument_list List of passed arguments 95 | * @return true if no error occurred 96 | */ 97 | bool cmd_open(girara_session_t* session, girara_list_t* argument_list); 98 | 99 | /** 100 | * Print the current file 101 | * 102 | * @param session The used girara session 103 | * @param argument_list List of passed arguments 104 | * @return true if no error occurred 105 | */ 106 | bool cmd_print(girara_session_t* session, girara_list_t* argument_list); 107 | 108 | /** 109 | * Hides current search results 110 | * 111 | * @param session The used girara session 112 | * @param argument_list List of passed arguments 113 | * @return true if no error occurred 114 | */ 115 | bool cmd_nohlsearch(girara_session_t* session, girara_list_t* argument_list); 116 | 117 | /** 118 | * Close zathura 119 | * 120 | * @param session The used girara session 121 | * @param argument_list List of passed arguments 122 | * @return true if no error occurred 123 | */ 124 | bool cmd_quit(girara_session_t* session, girara_list_t* argument_list); 125 | 126 | /** 127 | * Save the current file 128 | * 129 | * @param session The used girara session 130 | * @param argument_list List of passed arguments 131 | * @return true if no error occurred 132 | */ 133 | bool cmd_save(girara_session_t* session, girara_list_t* argument_list); 134 | 135 | /** 136 | * Save the current file and overwrite existing files 137 | * 138 | * @param session The used girara session 139 | * @param argument_list List of passed arguments 140 | * @return true if no error occurred 141 | */ 142 | bool cmd_savef(girara_session_t* session, girara_list_t* argument_list); 143 | 144 | /** 145 | * Search the current file 146 | * 147 | * @param session The used girara session 148 | * @param input The current input 149 | * @param argument Passed argument 150 | * @return true if no error occurred 151 | */ 152 | bool cmd_search(girara_session_t* session, const char* input, girara_argument_t* argument); 153 | 154 | /** 155 | * Save attachment to a file 156 | * 157 | * @param session The used girara session 158 | * @param argument_list List of passed arguments 159 | * @return true if no error occurred 160 | */ 161 | bool cmd_export(girara_session_t* session, girara_list_t* argument_list); 162 | 163 | /** 164 | * Execute command 165 | * 166 | * @param session The used girara session 167 | * @param argument_list List of passed arguments 168 | * @return true if no error occurred 169 | */ 170 | bool cmd_exec(girara_session_t* session, girara_list_t* argument_list); 171 | 172 | /** 173 | * Set page offset 174 | * 175 | * @param session The used girara session 176 | * @param argument_list List of passed arguments 177 | * @return true if no error occurred 178 | */ 179 | bool cmd_offset(girara_session_t* session, girara_list_t* argument_list); 180 | 181 | /** 182 | * Shows version information 183 | * 184 | * @param session The used girara session 185 | * @param argument_list List of passed arguments 186 | * @return true if no error occurred 187 | */ 188 | bool cmd_version(girara_session_t* session, girara_list_t* argument_list); 189 | 190 | /** 191 | * Source config file 192 | * 193 | * @param session The used girara session 194 | * @param argument_list List of passed arguments 195 | * @return true if no error occurred 196 | */ 197 | bool cmd_source(girara_session_t* session, girara_list_t* argument_list); 198 | 199 | #endif // COMMANDS_H 200 | -------------------------------------------------------------------------------- /zathura/utils.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef UTILS_H 4 | #define UTILS_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "document.h" 11 | 12 | #define LENGTH(x) (sizeof(x) / sizeof((x)[0])) 13 | 14 | typedef struct page_offset_s { 15 | int x; 16 | int y; 17 | } page_offset_t; 18 | 19 | /** 20 | * This function checks if the file has a valid extension. A extension is 21 | * evaluated as valid if it matches a supported filetype. 22 | * 23 | * @param zathura Zathura object 24 | * @param path The path to the file 25 | * @return true if the extension is valid, otherwise false 26 | */ 27 | bool file_valid_extension(zathura_t* zathura, const char* path); 28 | 29 | /** 30 | * Generates the document index based upon the list retrieved from the document 31 | * object. 32 | * 33 | * @param session The session 34 | * @param model The tree model 35 | * @param parent The tree iterator parent 36 | * @param tree The Tree iterator 37 | */ 38 | void document_index_build(girara_session_t* session, GtkTreeModel* model, GtkTreeIter* parent, 39 | girara_tree_node_t* tree); 40 | 41 | /** 42 | * A custom search equal function for the index tree view, so that 43 | * when interactively searching, the string will be recursively compared 44 | * to all the children of visible entries 45 | * 46 | * @param model The tree model 47 | * @param column The column of the entry 48 | * @param key The keyword to be compared 49 | * @param iter The tree iterator 50 | * @param search_data User data pointer 51 | */ 52 | gboolean search_equal_func_index(GtkTreeModel* model, gint column, const gchar* key, GtkTreeIter* iter, 53 | gpointer search_data); 54 | /** 55 | * Scrolls the document index to the current page 56 | * 57 | * @param zathura The zathura instance 58 | */ 59 | void index_scroll_to_current_page(zathura_t* zathura); 60 | 61 | /** 62 | * Calculates the new coordinates based on the rotation and scale level of the 63 | * document for the given rectangle 64 | * 65 | * @param page Page where the rectangle should be 66 | * @param rectangle The rectangle 67 | * @return New rectangle 68 | */ 69 | zathura_rectangle_t recalc_rectangle(zathura_page_t* page, zathura_rectangle_t rectangle); 70 | 71 | /** 72 | * Returns the page widget of the page 73 | * 74 | * @param zathura The zathura instance 75 | * @param page The page object 76 | * @return The page widget of the page 77 | * @return NULL if an error occurred 78 | */ 79 | GtkWidget* zathura_page_get_widget(zathura_t* zathura, zathura_page_t* page); 80 | 81 | /** 82 | * Set if the search results should be drawn or not 83 | * 84 | * @param zathura Zathura instance 85 | * @param value true if they should be drawn, otherwise false 86 | */ 87 | void document_draw_search_results(zathura_t* zathura, bool value); 88 | 89 | /** 90 | * Create zathura version string 91 | * 92 | * @param plugin_manager The plugin manager 93 | * @param markup Enable markup 94 | * @return Version string 95 | */ 96 | char* zathura_get_version_string(const zathura_plugin_manager_t* plugin_manager, bool markup); 97 | 98 | /** 99 | * Get a pointer to the GdkAtom of the current clipboard. 100 | * 101 | * @param zathura The zathura instance 102 | * 103 | * @return A pointer to a GdkAtom object correspoinding to the current 104 | * clipboard, or NULL. 105 | */ 106 | GdkAtom* get_selection(zathura_t* zathura); 107 | 108 | /** 109 | * Returns the valid zoom value which needs to lie in the interval of zoom_min 110 | * and zoom_max specified in the girara session 111 | * 112 | * @param[in] session The session 113 | * @param[in] zoom The proposed zoom value 114 | * 115 | * @return The corrected zoom value 116 | */ 117 | double zathura_correct_zoom_value(girara_session_t* session, const double zoom); 118 | 119 | /** 120 | * Write a list of 'pages per row to first column' values as a colon separated string. 121 | * 122 | * For valid settings list, this is the inverse of parse_first_page_column_list. 123 | * 124 | * @param[in] first_page_columns The settings vector 125 | * @param[in] size The size of the settings vector 126 | * 127 | * @return The new settings string 128 | */ 129 | char* write_first_page_column(unsigned int* first_page_columns, unsigned int size); 130 | 131 | /** 132 | * Parse a 'pages per row to first column' settings list. 133 | * 134 | * For valid settings list, this is the inverse of write_first_page_column_list. 135 | * 136 | * @param[in] first_page_column_list The settings list 137 | * @param[in] size A cell to return the size of the result, mandatory 138 | * 139 | * @return The values from the settings list as a new vector 140 | */ 141 | unsigned int* parse_first_page_column(const char* first_page_column_list, unsigned int* size); 142 | 143 | /** 144 | * Extracts the column the first page should be rendered in from the specified 145 | * list of settings corresponding to the specified pages per row 146 | * 147 | * @param[in] first_page_column_list The settings list 148 | * @param[in] pages_per_row The current pages per row 149 | * 150 | * @return The column the first page should be rendered in 151 | */ 152 | unsigned int find_first_page_column(const char* first_page_column_list, const unsigned int pages_per_row); 153 | 154 | /** 155 | * Cycle the column the first page should be rendered in. 156 | * 157 | * @param[in] first_page_column_list The settings list 158 | * @param[in] pages_per_row The current pages per row 159 | * @param[in] incr The value added to the current first page column setting 160 | * 161 | * @return The new modified settings list 162 | */ 163 | char* increment_first_page_column(const char* first_page_column_list, const unsigned int pages_per_row, int incr); 164 | 165 | /** 166 | * Parse color string and print warning if color cannot be parsed. 167 | * 168 | * @param[out] color The color 169 | * @param[in] str Color string 170 | * 171 | * @return True if color string can be parsed, false otherwise. 172 | */ 173 | bool parse_color(GdkRGBA* color, const char* str); 174 | 175 | /** 176 | * Flatten list of overlapping rectangles. 177 | * 178 | * @param[in] rectangles A list of rectangles 179 | * 180 | * @return List of rectangles 181 | */ 182 | girara_list_t* flatten_rectangles(girara_list_t* rectangles); 183 | 184 | #endif // UTILS_H 185 | -------------------------------------------------------------------------------- /zathura/jumplist.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "jumplist.h" 4 | 5 | #include "zathura.h" 6 | #include "document.h" 7 | #include "database.h" 8 | 9 | #include 10 | #include 11 | 12 | static void zathura_jumplist_reset_current(zathura_t* zathura) { 13 | g_return_if_fail(zathura != NULL && zathura->jumplist.cur != NULL); 14 | 15 | while (girara_list_iterator_has_next(zathura->jumplist.cur) == true) { 16 | girara_list_iterator_next(zathura->jumplist.cur); 17 | } 18 | } 19 | 20 | static void zathura_jumplist_append_jump(zathura_t* zathura) { 21 | g_return_if_fail(zathura != NULL && zathura->jumplist.list != NULL); 22 | 23 | zathura_jump_t* jump = g_try_malloc0(sizeof(zathura_jump_t)); 24 | if (jump == NULL) { 25 | return; 26 | } 27 | 28 | girara_list_append(zathura->jumplist.list, jump); 29 | 30 | if (zathura->jumplist.size == 0) { 31 | zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list); 32 | } 33 | 34 | ++zathura->jumplist.size; 35 | zathura_jumplist_trim(zathura); 36 | } 37 | 38 | static void zathura_jumplist_save(zathura_t* zathura) { 39 | g_return_if_fail(zathura_has_document(zathura) == true); 40 | 41 | zathura_jump_t* cur = zathura_jumplist_current(zathura); 42 | if (cur != NULL) { 43 | zathura_document_t* document = zathura_get_document(zathura); 44 | cur->x = zathura_document_get_position_x(document); 45 | cur->y = zathura_document_get_position_y(document); 46 | cur->page = zathura_document_get_current_page_number(document); 47 | } 48 | } 49 | 50 | bool zathura_jumplist_has_previous(zathura_t* zathura) { 51 | return girara_list_iterator_has_previous(zathura->jumplist.cur); 52 | } 53 | 54 | bool zathura_jumplist_has_next(zathura_t* zathura) { 55 | return girara_list_iterator_has_next(zathura->jumplist.cur); 56 | } 57 | 58 | zathura_jump_t* zathura_jumplist_current(zathura_t* zathura) { 59 | if (zathura->jumplist.cur != NULL) { 60 | return girara_list_iterator_data(zathura->jumplist.cur); 61 | } else { 62 | return NULL; 63 | } 64 | } 65 | 66 | void zathura_jumplist_forward(zathura_t* zathura) { 67 | if (girara_list_iterator_has_next(zathura->jumplist.cur)) { 68 | girara_list_iterator_next(zathura->jumplist.cur); 69 | } 70 | } 71 | 72 | void zathura_jumplist_backward(zathura_t* zathura) { 73 | if (girara_list_iterator_has_previous(zathura->jumplist.cur)) { 74 | girara_list_iterator_previous(zathura->jumplist.cur); 75 | } 76 | } 77 | 78 | void zathura_jumplist_trim(zathura_t* zathura) { 79 | g_return_if_fail(zathura != NULL && zathura->jumplist.list != NULL && zathura->jumplist.size != 0); 80 | 81 | girara_list_iterator_t* cur = girara_list_iterator(zathura->jumplist.list); 82 | 83 | while (zathura->jumplist.size > zathura->jumplist.max_size) { 84 | if (girara_list_iterator_data(cur) == girara_list_iterator_data(zathura->jumplist.cur)) { 85 | girara_list_iterator_free(zathura->jumplist.cur); 86 | zathura->jumplist.cur = NULL; 87 | } 88 | 89 | girara_list_iterator_remove(cur); 90 | --zathura->jumplist.size; 91 | } 92 | 93 | if (zathura->jumplist.size == 0 || zathura->jumplist.cur != NULL) { 94 | girara_list_iterator_free(cur); 95 | } else { 96 | zathura->jumplist.cur = cur; 97 | } 98 | } 99 | 100 | void zathura_jumplist_add(zathura_t* zathura) { 101 | g_return_if_fail(zathura_has_document(zathura) == true && zathura->jumplist.list != NULL); 102 | 103 | zathura_document_t* document = zathura_get_document(zathura); 104 | double x = zathura_document_get_position_x(document); 105 | double y = zathura_document_get_position_y(document); 106 | 107 | if (zathura->jumplist.size != 0) { 108 | zathura_jumplist_reset_current(zathura); 109 | 110 | zathura_jump_t* cur = zathura_jumplist_current(zathura); 111 | if (cur != NULL) { 112 | if (fabs(cur->x - x) <= DBL_EPSILON && fabs(cur->y - y) <= DBL_EPSILON) { 113 | return; 114 | } 115 | } 116 | } 117 | 118 | zathura_jumplist_append_jump(zathura); 119 | zathura_jumplist_reset_current(zathura); 120 | zathura_jumplist_save(zathura); 121 | } 122 | 123 | void zathura_jumplist_set_max_size(zathura_t* zathura, size_t max_size) { 124 | zathura->jumplist.max_size = max_size; 125 | if (zathura->jumplist.list != NULL && zathura->jumplist.size != 0) { 126 | zathura_jumplist_trim(zathura); 127 | } 128 | } 129 | 130 | bool zathura_jumplist_load(zathura_t* zathura, const char* file) { 131 | g_return_val_if_fail(zathura != NULL && file != NULL, false); 132 | 133 | if (zathura->database == NULL) { 134 | return false; 135 | } 136 | 137 | girara_list_t* list = zathura_db_load_jumplist(zathura->database, file); 138 | if (list == NULL) { 139 | girara_error("Failed to load the jumplist from the database"); 140 | return false; 141 | } 142 | 143 | girara_list_free(zathura->jumplist.list); 144 | zathura->jumplist.list = list; 145 | zathura->jumplist.size = girara_list_size(zathura->jumplist.list); 146 | 147 | if (zathura->jumplist.size != 0) { 148 | zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list); 149 | zathura_jumplist_reset_current(zathura); 150 | zathura_jumplist_trim(zathura); 151 | girara_debug("Loaded the jumplist from the database"); 152 | } else { 153 | girara_debug("No jumplist for this file in the database yet"); 154 | } 155 | 156 | return true; 157 | } 158 | 159 | void zathura_jumplist_init(zathura_t* zathura, size_t max_size) { 160 | zathura->jumplist.max_size = max_size; 161 | zathura->jumplist.list = girara_list_new_with_free(g_free); 162 | zathura->jumplist.size = 0; 163 | zathura->jumplist.cur = NULL; 164 | } 165 | 166 | bool zathura_jumplist_is_initalized(zathura_t* zathura) { 167 | return zathura->jumplist.list != NULL; 168 | } 169 | 170 | void zathura_jumplist_clear(zathura_t* zathura) { 171 | if (zathura == NULL) { 172 | return; 173 | } 174 | 175 | /* remove jump list */ 176 | girara_list_iterator_free(zathura->jumplist.cur); 177 | zathura->jumplist.cur = NULL; 178 | girara_list_clear(zathura->jumplist.list); 179 | zathura->jumplist.size = 0; 180 | } 181 | 182 | void zathura_jumplist_free(zathura_t* zathura) { 183 | if (zathura == NULL) { 184 | return; 185 | } 186 | 187 | /* remove jump list */ 188 | zathura_jumplist_clear(zathura); 189 | zathura->jumplist.list = NULL; 190 | } 191 | -------------------------------------------------------------------------------- /zathura/database.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef DATABASE_H 4 | #define DATABASE_H 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "bookmarks.h" 13 | 14 | typedef struct zathura_fileinfo_s { 15 | unsigned int current_page; 16 | unsigned int page_offset; 17 | unsigned int rotation; 18 | unsigned int pages_per_row; 19 | char* first_page_column_list; 20 | double zoom; 21 | double position_x; 22 | double position_y; 23 | bool page_right_to_left; 24 | } zathura_fileinfo_t; 25 | 26 | #define ZATHURA_TYPE_DATABASE (zathura_database_get_type()) 27 | #define ZATHURA_DATABASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_DATABASE, ZathuraDatabase)) 28 | #define ZATHURA_IS_DATABASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_DATABASE)) 29 | #define ZATHURA_DATABASE_GET_INTERFACE(obj) \ 30 | (G_TYPE_INSTANCE_GET_INTERFACE((obj), ZATHURA_TYPE_DATABASE, ZathuraDatabaseInterface)) 31 | 32 | typedef struct _ZathuraDatabase ZathuraDatabase; 33 | typedef struct _ZathuraDatabaseInterface ZathuraDatabaseInterface; 34 | 35 | struct _ZathuraDatabaseInterface { 36 | GTypeInterface parent_iface; 37 | 38 | /* interface methods */ 39 | bool (*add_bookmark)(ZathuraDatabase* db, const char* file, zathura_bookmark_t* bookmark); 40 | 41 | bool (*remove_bookmark)(ZathuraDatabase* db, const char* file, const char* id); 42 | 43 | girara_list_t* (*load_bookmarks)(ZathuraDatabase* db, const char* file); 44 | 45 | girara_list_t* (*load_jumplist)(ZathuraDatabase* db, const char* file); 46 | 47 | bool (*save_jumplist)(ZathuraDatabase* db, const char* file, girara_list_t* jumplist); 48 | 49 | bool (*set_fileinfo)(ZathuraDatabase* db, const char* file, const uint8_t* hash_sha256, 50 | zathura_fileinfo_t* file_info); 51 | 52 | bool (*get_fileinfo)(ZathuraDatabase* db, const char* file, const uint8_t* hash_sha256, 53 | zathura_fileinfo_t* file_info); 54 | 55 | girara_list_t* (*get_recent_files)(ZathuraDatabase* db, int max, const char* basepath); 56 | 57 | girara_list_t* (*load_quickmarks)(ZathuraDatabase* db, const char* file); 58 | 59 | bool (*save_quickmarks)(ZathuraDatabase* db, const char* file, girara_list_t* jumplist); 60 | }; 61 | 62 | GType zathura_database_get_type(void) G_GNUC_CONST; 63 | 64 | /** 65 | * Add or update bookmark in the database. 66 | * 67 | * @param db The database instance 68 | * @param file The file to which the bookmark belongs. 69 | * @param bookmark The bookmark instance. 70 | * @return true on success, false otherwise 71 | */ 72 | bool zathura_db_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark); 73 | 74 | /** 75 | * Remove a bookmark from the database. 76 | * 77 | * @param db The database instance 78 | * @param file The file to which the bookmark belongs. 79 | * @param id The id of the bookmark 80 | * @return true on success, false otherwise 81 | */ 82 | bool zathura_db_remove_bookmark(zathura_database_t* db, const char* file, const char* id); 83 | 84 | /** 85 | * Loads all bookmarks from the database belonging to a specific file. 86 | * 87 | * @param db The database instance. 88 | * @param file The file for which the bookmarks should be loaded. 89 | * @return List of zathura_bookmark_t* or NULL on failure. 90 | */ 91 | girara_list_t* zathura_db_load_bookmarks(zathura_database_t* db, const char* file); 92 | 93 | /** 94 | * Load the jumplist belonging to the specified file from the database. 95 | * 96 | * @param db The database instance. 97 | * @param file The file to which the jumplist belongs. 98 | * 99 | * return A linked list constituting the jumplist of the specified file. 100 | */ 101 | girara_list_t* zathura_db_load_jumplist(ZathuraDatabase* db, const char* file); 102 | 103 | /** 104 | * Save the jumplist belonging to the specified file to the database. 105 | * 106 | * @param db The database instance. 107 | * @param file The file to which the jumplist belongs. 108 | * @param jumplist The jumplist to be saved 109 | * @return true on success, false otherwise. 110 | */ 111 | bool zathura_db_save_jumplist(ZathuraDatabase* db, const char* file, girara_list_t* jumplist); 112 | 113 | /** 114 | * Load the quickmarks belonging to the specified file from the database. 115 | * 116 | * @param db The database instance. 117 | * @param file The file to which the quick marks belongs. 118 | * @return A list constituting the quickmarks of the specified file. 119 | */ 120 | girara_list_t* zathura_db_load_quickmarks(ZathuraDatabase* db, const char* file); 121 | 122 | /** 123 | * Save the quickmarks belonging to the specified file to the database. 124 | * 125 | * @param db The database instance. 126 | * @param file The file to which the quickmarks belongs. 127 | * @param quickmarks The quickmarks to be saved 128 | * @return return true on success, false otherwise. 129 | */ 130 | bool zathura_db_save_quickmarks(ZathuraDatabase* db, const char* file, girara_list_t* quickmarks); 131 | 132 | /** 133 | * Set file info (last site, ...) in the database. 134 | * 135 | * @param db The database instance 136 | * @param file The file to which the file info belongs. 137 | * @param hash_sha256 The file's hash 138 | * @param file_info The file info 139 | * @return true on success, false otherwise. 140 | */ 141 | bool zathura_db_set_fileinfo(zathura_database_t* db, const char* file, const uint8_t* hash_sha256, 142 | zathura_fileinfo_t* file_info); 143 | 144 | /* Get file info (last site, ...) from the database. The info is first looked up by file and then by 145 | * its hash. 146 | * 147 | * @param db The database instance 148 | * @param file The file to which the file info belongs. 149 | * @param hash_sha256 The file's hash 150 | * @param file_info The file info 151 | * @return true on success, false otherwise. 152 | */ 153 | bool zathura_db_get_fileinfo(zathura_database_t* db, const char* file, const uint8_t* hash_sha256, 154 | zathura_fileinfo_t* file_info); 155 | 156 | /* Get a list of recent files from the database. The most recent file is listed 157 | * first. 158 | * 159 | * @param db The database instance 160 | * @param max The maximum number of recent files. If max is less than zero, now 161 | * limit is applied. 162 | * @return list of files 163 | */ 164 | girara_list_t* zathura_db_get_recent_files(zathura_database_t* db, int max, const char* basepath); 165 | 166 | #endif // DATABASE_H 167 | -------------------------------------------------------------------------------- /zathura/print.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include "print.h" 4 | #include "document.h" 5 | #include "render.h" 6 | #include "page.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static void cb_print_end(GtkPrintOperation* UNUSED(print_operation), GtkPrintContext* UNUSED(context), 14 | zathura_t* zathura) { 15 | if (zathura_has_document(zathura) == false || zathura->ui.session == NULL) { 16 | return; 17 | } 18 | 19 | g_autofree char* file_path = get_formatted_filename(zathura, true); 20 | girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, file_path); 21 | } 22 | 23 | static bool draw_page_cairo(cairo_t* cairo, zathura_t* zathura, zathura_page_t* page) { 24 | /* Try to render the page without a temporary surface. This only works with 25 | * plugins that support rendering to any surface. */ 26 | zathura_renderer_lock(zathura->sync.render_thread); 27 | const int err = zathura_page_render(page, cairo, true); 28 | zathura_renderer_unlock(zathura->sync.render_thread); 29 | 30 | return err == ZATHURA_ERROR_OK; 31 | } 32 | 33 | static bool draw_page_image(cairo_t* cairo, GtkPrintContext* context, zathura_t* zathura, zathura_page_t* page) { 34 | /* Try to render the page on a temporary image surface. */ 35 | const double width = gtk_print_context_get_width(context); 36 | const double height = gtk_print_context_get_height(context); 37 | 38 | const double scale_height = 5; 39 | const double scale_width = 5; 40 | 41 | /* Render to a surface that is 5 times larger to workaround quality issues. */ 42 | const double page_height = zathura_page_get_height(page) * scale_height; 43 | const double page_width = zathura_page_get_width(page) * scale_width; 44 | cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, page_width, page_height); 45 | if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { 46 | return false; 47 | } 48 | 49 | cairo_t* temp_cairo = cairo_create(surface); 50 | if (cairo_status(temp_cairo) != CAIRO_STATUS_SUCCESS) { 51 | cairo_surface_destroy(surface); 52 | return false; 53 | } 54 | 55 | /* Draw a white background. */ 56 | cairo_save(temp_cairo); 57 | cairo_set_source_rgb(temp_cairo, 1, 1, 1); 58 | cairo_rectangle(temp_cairo, 0, 0, page_width, page_height); 59 | cairo_fill(temp_cairo); 60 | cairo_restore(temp_cairo); 61 | 62 | /* Render the page to the temporary surface */ 63 | zathura_renderer_lock(zathura->sync.render_thread); 64 | const int err = zathura_page_render(page, temp_cairo, true); 65 | zathura_renderer_unlock(zathura->sync.render_thread); 66 | if (err != ZATHURA_ERROR_OK) { 67 | cairo_destroy(temp_cairo); 68 | cairo_surface_destroy(surface); 69 | return false; 70 | } 71 | 72 | /* Rescale the page and keep the aspect ratio */ 73 | const gdouble scale = MIN(width / page_width, height / page_height); 74 | cairo_scale(cairo, scale, scale); 75 | 76 | /* Blit temporary surface to original cairo object. */ 77 | cairo_set_source_surface(cairo, surface, 0.0, 0.0); 78 | cairo_paint(cairo); 79 | cairo_destroy(temp_cairo); 80 | cairo_surface_destroy(surface); 81 | 82 | return true; 83 | } 84 | 85 | static void cb_print_draw_page(GtkPrintOperation* print_operation, GtkPrintContext* context, gint page_number, 86 | zathura_t* zathura) { 87 | if (context == NULL || zathura_has_document(zathura) == false || zathura->ui.session == NULL || 88 | zathura->ui.statusbar.file == NULL) { 89 | gtk_print_operation_cancel(print_operation); 90 | return; 91 | } 92 | 93 | /* Update statusbar. */ 94 | g_autofree char* tmp = g_strdup_printf(_("Printing page %d ..."), page_number); 95 | girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, tmp); 96 | 97 | /* Get the page and cairo handle. */ 98 | zathura_page_t* page = zathura_document_get_page(zathura_get_document(zathura), page_number); 99 | cairo_t* cairo = gtk_print_context_get_cairo_context(context); 100 | if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS || page == NULL) { 101 | gtk_print_operation_cancel(print_operation); 102 | return; 103 | } 104 | 105 | girara_debug("printing page %d ...", page_number); 106 | if (draw_page_cairo(cairo, zathura, page) == true) { 107 | return; 108 | } 109 | 110 | girara_debug("printing page %d (fallback) ...", page_number); 111 | if (draw_page_image(cairo, context, zathura, page) == false) { 112 | gtk_print_operation_cancel(print_operation); 113 | } 114 | } 115 | 116 | static void cb_print_request_page_setup(GtkPrintOperation* UNUSED(print_operation), GtkPrintContext* UNUSED(context), 117 | gint page_number, GtkPageSetup* setup, zathura_t* zathura) { 118 | if (zathura_has_document(zathura) == false) { 119 | return; 120 | } 121 | 122 | zathura_page_t* page = zathura_document_get_page(zathura_get_document(zathura), page_number); 123 | double width = zathura_page_get_width(page); 124 | double height = zathura_page_get_height(page); 125 | 126 | if (width > height) { 127 | gtk_page_setup_set_orientation(setup, GTK_PAGE_ORIENTATION_LANDSCAPE); 128 | } else { 129 | gtk_page_setup_set_orientation(setup, GTK_PAGE_ORIENTATION_PORTRAIT); 130 | } 131 | } 132 | 133 | void print(zathura_t* zathura) { 134 | g_return_if_fail(zathura_has_document(zathura) == true); 135 | 136 | zathura_document_t* document = zathura_get_document(zathura); 137 | g_autoptr(GtkPrintOperation) print_operation = gtk_print_operation_new(); 138 | 139 | /* print operation settings */ 140 | gtk_print_operation_set_job_name(print_operation, zathura_document_get_path(document)); 141 | gtk_print_operation_set_allow_async(print_operation, TRUE); 142 | gtk_print_operation_set_n_pages(print_operation, zathura_document_get_number_of_pages(document)); 143 | gtk_print_operation_set_current_page(print_operation, zathura_document_get_current_page_number(document)); 144 | gtk_print_operation_set_use_full_page(print_operation, TRUE); 145 | 146 | if (zathura->print.settings != NULL) { 147 | gtk_print_operation_set_print_settings(print_operation, zathura->print.settings); 148 | } 149 | 150 | if (zathura->print.page_setup != NULL) { 151 | gtk_print_operation_set_default_page_setup(print_operation, zathura->print.page_setup); 152 | } 153 | gtk_print_operation_set_embed_page_setup(print_operation, TRUE); 154 | 155 | /* print operation signals */ 156 | g_signal_connect(print_operation, "draw-page", G_CALLBACK(cb_print_draw_page), zathura); 157 | g_signal_connect(print_operation, "end-print", G_CALLBACK(cb_print_end), zathura); 158 | g_signal_connect(print_operation, "request-page-setup", G_CALLBACK(cb_print_request_page_setup), zathura); 159 | 160 | /* print */ 161 | g_autoptr(GError) error = NULL; 162 | GtkPrintOperationResult result = gtk_print_operation_run(print_operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, 163 | GTK_WINDOW(zathura->ui.session->gtk.window), &error); 164 | 165 | if (result == GTK_PRINT_OPERATION_RESULT_ERROR) { 166 | girara_notify(zathura->ui.session, GIRARA_ERROR, _("Printing failed: %s"), error->message); 167 | } else if (result == GTK_PRINT_OPERATION_RESULT_APPLY) { 168 | g_clear_object(&zathura->print.settings); 169 | g_clear_object(&zathura->print.page_setup); 170 | 171 | /* save previous settings */ 172 | zathura->print.settings = g_object_ref(gtk_print_operation_get_print_settings(print_operation)); 173 | zathura->print.page_setup = g_object_ref(gtk_print_operation_get_default_page_setup(print_operation)); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('zathura', 'c', 2 | version: '0.5.14', 3 | meson_version: '>=1.5', 4 | default_options: ['c_std=c17', 'warning_level=3'], 5 | ) 6 | 7 | version = meson.project_version() 8 | version_array = version.split('.') 9 | 10 | # Rules for plugin API and ABI (non-exhaustive): 11 | # * zathura_plugin_function_t: If functions are addedd or removed or their 12 | # signature changes, bump both ABI and API. 13 | # * zathura_plugin_definition_t: If the struct changes in an ABI-incompatible 14 | # way, bump the ABI. 15 | plugin_api_version = '6' 16 | plugin_abi_version = '7' 17 | 18 | conf_data = configuration_data() 19 | conf_data.set('ZVMAJOR', version_array[0]) 20 | conf_data.set('ZVMINOR', version_array[1]) 21 | conf_data.set('ZVREV', version_array[2]) 22 | conf_data.set('ZVAPI', plugin_api_version) 23 | conf_data.set('ZVABI', plugin_abi_version) 24 | conf_data.set('version', version) 25 | 26 | cc = meson.get_compiler('c') 27 | 28 | prefix = get_option('prefix') 29 | localedir = get_option('localedir') 30 | datadir = get_option('datadir') 31 | metainfodir = join_paths(datadir, 'metainfo') 32 | desktopdir = join_paths(datadir, 'applications') 33 | dbusinterfacesdir = join_paths(datadir, 'dbus-1', 'interfaces') 34 | plugindir = join_paths(get_option('libdir'), 'zathura') 35 | sysconfdir = get_option('sysconfdir') 36 | if not sysconfdir.startswith('/') 37 | sysconfdir = join_paths(prefix, sysconfdir) 38 | endif 39 | 40 | # required attributes 41 | cc.compiles('''#if !__has_attribute(cleanup) 42 | #error attribute cleanup not supported 43 | #endif''', name: '__attribute__(cleanup)', required: true) 44 | 45 | # required dependencies 46 | libm = cc.find_library('m', required: false) 47 | girara = dependency('girara-gtk3', version: '>=0.4.5', fallback: ['girara', 'girara_dependency']) 48 | glib = dependency('glib-2.0', version: '>=2.76') 49 | gio = dependency('gio-unix-2.0', required: host_machine.system() != 'windows') 50 | gthread = dependency('gthread-2.0', version: '>=2.72') 51 | gmodule = dependency('gmodule-no-export-2.0', version: '>=2.72') 52 | gtk3 = dependency('gtk+-3.0', version: '>=3.24') 53 | json_glib = dependency('json-glib-1.0') 54 | cairo = dependency('cairo') 55 | magic = dependency('libmagic') 56 | sqlite = dependency('sqlite3', version: '>=3.6.23') 57 | 58 | build_dependencies = [libm, girara, glib, gio, gthread, gmodule, gtk3, cairo, magic, json_glib] 59 | 60 | if host_machine.system() == 'darwin' 61 | gtk_mac_integration = dependency('gtk-mac-integration-gtk3') 62 | build_dependencies += gtk_mac_integration 63 | endif 64 | 65 | # required functions 66 | cc.has_function('__builtin_umul_overflow', required: true) 67 | 68 | # defines 69 | defines = [ 70 | '-DGETTEXT_PACKAGE="org.pwmt.zathura"', 71 | '-DLOCALEDIR="@0@"'.format(join_paths(prefix, localedir)), 72 | '-DZATHURA_PLUGINDIR="@0@"'.format(join_paths(prefix, plugindir)), 73 | '-DSYSCONFDIR="@0@"'.format(sysconfdir), 74 | '-D_DEFAULT_SOURCE', 75 | ] 76 | 77 | if host_machine.system() == 'darwin' 78 | defines += '-DGTKOSXAPPLICATION' 79 | endif 80 | 81 | # compile flags 82 | flags = [ 83 | '-Werror=implicit-function-declaration', 84 | '-Werror=vla', 85 | '-Werror=int-conversion', 86 | '-Werror=maybe-uninitialized' 87 | ] 88 | flags = cc.get_supported_arguments(flags) 89 | 90 | # optional dependencies 91 | synctex = dependency('synctex', version: '>=2', required: get_option('synctex')) 92 | seccomp = dependency('libseccomp', required: get_option('seccomp')) 93 | landlock = cc.check_header('linux/landlock.h', required: get_option('landlock')) 94 | 95 | if synctex.found() 96 | build_dependencies += synctex 97 | defines += '-DWITH_SYNCTEX' 98 | endif 99 | 100 | # generate version header file 101 | version_header = configure_file( 102 | input: 'zathura/version.h.in', 103 | output: 'zathura-version.h', 104 | configuration: conf_data 105 | ) 106 | 107 | include_directories = [ 108 | include_directories('.') 109 | ] 110 | 111 | subdir('data') 112 | subdir('po') 113 | 114 | # source files 115 | sources = files( 116 | 'zathura/adjustment.c', 117 | 'zathura/bookmarks.c', 118 | 'zathura/callbacks.c', 119 | 'zathura/commands.c', 120 | 'zathura/completion.c', 121 | 'zathura/config.c', 122 | 'zathura/content-type.c', 123 | 'zathura/database.c', 124 | 'zathura/database-null.c', 125 | 'zathura/dbus-interface.c', 126 | 'zathura/document.c', 127 | 'zathura/document-widget.c', 128 | 'zathura/file-monitor.c', 129 | 'zathura/file-monitor-glib.c', 130 | 'zathura/file-monitor-noop.c', 131 | 'zathura/file-monitor-signal.c', 132 | 'zathura/jumplist.c', 133 | 'zathura/links.c', 134 | 'zathura/marks.c', 135 | 'zathura/page.c', 136 | 'zathura/page-widget.c', 137 | 'zathura/plugin.c', 138 | 'zathura/print.c', 139 | 'zathura/render.c', 140 | 'zathura/shortcuts.c', 141 | 'zathura/synctex.c', 142 | 'zathura/types.c', 143 | 'zathura/utils.c', 144 | 'zathura/zathura.c', 145 | ) 146 | sources += zathura_resources 147 | 148 | # header files to install 149 | headers = files( 150 | 'zathura/document.h', 151 | 'zathura/links.h', 152 | 'zathura/macros.h', 153 | 'zathura/page.h', 154 | 'zathura/plugin-api.h', 155 | 'zathura/types.h', 156 | ) 157 | headers += version_header 158 | 159 | # zathura helper library 160 | libzathura = static_library('zathura', 161 | sources + ['zathura/database-sqlite.c'], 162 | dependencies: build_dependencies + [sqlite], 163 | include_directories: include_directories, 164 | c_args: defines + flags, 165 | gnu_symbol_visibility: 'hidden', 166 | ) 167 | libzathura_dep = declare_dependency(link_with: libzathura) 168 | 169 | # zathura-sandbox executable 170 | if seccomp.found() or landlock or target_machine.system() == 'openbsd' 171 | sandbox_sources = files() 172 | sandbox_defines = ['-DWITH_SANDBOX'] 173 | sandbox_dependencies = [] 174 | if seccomp.found() 175 | sandbox_defines += '-DWITH_SECCOMP' 176 | sandbox_dependencies += seccomp 177 | sandbox_sources += files('zathura/seccomp-filters.c') 178 | endif 179 | if landlock 180 | sandbox_defines += '-DWITH_LANDLOCK' 181 | sandbox_sources += files('zathura/landlock.c') 182 | endif 183 | 184 | # zathura helper library 185 | libzathura_sandbox = static_library('zathura-sandbox', 186 | sources + sandbox_sources, 187 | dependencies: build_dependencies + sandbox_dependencies, 188 | include_directories: include_directories, 189 | c_args: defines + flags + sandbox_defines, 190 | gnu_symbol_visibility: 'hidden', 191 | ) 192 | libzathura_sandbox_dep = declare_dependency(link_with: libzathura_sandbox) 193 | 194 | zathura_sandbox = executable( 195 | 'zathura-sandbox', 196 | files('zathura/main-sandbox.c') + sandbox_sources, 197 | dependencies: build_dependencies + [libzathura_sandbox_dep] + sandbox_dependencies, 198 | install: true, 199 | include_directories: include_directories, 200 | c_args: defines + sandbox_defines + flags, 201 | gnu_symbol_visibility: 'hidden', 202 | export_dynamic: true, 203 | win_subsystem: 'windows' 204 | ) 205 | endif 206 | 207 | # zathura executable 208 | zathura = executable( 209 | 'zathura', 210 | files('zathura/main.c'), 211 | dependencies: build_dependencies + [libzathura_dep], 212 | install: true, 213 | include_directories: include_directories, 214 | c_args: defines + flags, 215 | gnu_symbol_visibility: 'hidden', 216 | export_dynamic: true, 217 | win_subsystem: 'windows' 218 | ) 219 | install_headers(headers, subdir: 'zathura') 220 | 221 | # pkg-config file 222 | pkg = import('pkgconfig') 223 | pkg.generate( 224 | name: 'zathura', 225 | description: 'document viewer - plugin API', 226 | url: 'https://pwmt.org/projects/zathura', 227 | version: version, 228 | requires: ['girara-gtk3', 'cairo'], 229 | variables: [ 230 | 'apiversion=@0@'.format(plugin_api_version), 231 | 'abiversion=@0@'.format(plugin_abi_version), 232 | 'plugindir=${libdir}/zathura' 233 | ] 234 | ) 235 | 236 | zathura_dependency = declare_dependency(link_with: zathura, include_directories: include_directories) 237 | 238 | subdir('doc') 239 | if get_option('tests').allowed() 240 | subdir('tests') 241 | endif 242 | -------------------------------------------------------------------------------- /zathura/page.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef PAGE_H 4 | #define PAGE_H 5 | 6 | #include 7 | #include 8 | 9 | #include "types.h" 10 | 11 | /** 12 | * Get the page object 13 | * 14 | * @param document The document 15 | * @param index Page number 16 | * @param error Optional error 17 | * @return Page object or NULL if an error occurred 18 | */ 19 | ZATHURA_PLUGIN_API zathura_page_t* zathura_page_new(zathura_document_t* document, unsigned int index, 20 | zathura_error_t* error); 21 | 22 | /** 23 | * Frees the page object 24 | * 25 | * @param page The page object 26 | * @return ZATHURA_ERROR_OK when no error occurred, otherwise see 27 | * zathura_error_t 28 | */ 29 | ZATHURA_PLUGIN_API zathura_error_t zathura_page_free(zathura_page_t* page); 30 | 31 | /** 32 | * Returns the associated document 33 | * 34 | * @param page The page object 35 | * @return The associated document 36 | * @return NULL if an error occurred 37 | */ 38 | ZATHURA_PLUGIN_API zathura_document_t* zathura_page_get_document(zathura_page_t* page); 39 | 40 | /** 41 | * Returns the set id of the page 42 | * 43 | * @param page The page object 44 | * @return The id of the page 45 | */ 46 | ZATHURA_PLUGIN_API unsigned int zathura_page_get_index(zathura_page_t* page); 47 | 48 | /** 49 | * Returns the width of the page 50 | * 51 | * @param page The page object 52 | * @return Width of the page 53 | * @return -1 If an error occurred 54 | */ 55 | ZATHURA_PLUGIN_API double zathura_page_get_width(zathura_page_t* page); 56 | 57 | /** 58 | * Sets the new width of the page 59 | * 60 | * @param page The page object 61 | * @param width The new width of the page 62 | */ 63 | ZATHURA_PLUGIN_API void zathura_page_set_width(zathura_page_t* page, double width); 64 | 65 | /** 66 | * Returns the height of the page 67 | * 68 | * @param page The page object 69 | * @return Height of the page 70 | * @return -1 If an error occurred 71 | */ 72 | ZATHURA_PLUGIN_API double zathura_page_get_height(zathura_page_t* page); 73 | 74 | /** 75 | * Sets the new height of the page 76 | * 77 | * @param page The page object 78 | * @param height The new height of the page 79 | */ 80 | ZATHURA_PLUGIN_API void zathura_page_set_height(zathura_page_t* page, double height); 81 | 82 | /** 83 | * Returns the visibility of the page 84 | * 85 | * @param page The page object 86 | * @return true if the page is visible 87 | * @return false if the page is hidden 88 | */ 89 | ZATHURA_PLUGIN_API bool zathura_page_get_visibility(zathura_page_t* page); 90 | 91 | /** 92 | * Sets the visibility of the page 93 | * 94 | * @param page The page object 95 | * @param visibility The new visibility value 96 | */ 97 | ZATHURA_PLUGIN_API void zathura_page_set_visibility(zathura_page_t* page, bool visibility); 98 | 99 | /** 100 | * Returns the custom data 101 | * 102 | * @param page The page object 103 | * @return The custom data or NULL 104 | */ 105 | ZATHURA_PLUGIN_API void* zathura_page_get_data(zathura_page_t* page); 106 | 107 | /** 108 | * Sets the custom data 109 | * 110 | * @param page The page object 111 | * @param data The custom data 112 | */ 113 | ZATHURA_PLUGIN_API void zathura_page_set_data(zathura_page_t* page, void* data); 114 | 115 | /** 116 | * Search page 117 | * 118 | * @param page The page object 119 | * @param text Search item 120 | * @param error Set to an error value (see \ref zathura_error_t) if an 121 | * error occurred 122 | * @return List of results 123 | */ 124 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_search_text(zathura_page_t* page, const char* text, 125 | zathura_error_t* error); 126 | 127 | /** 128 | * Get page links 129 | * 130 | * @param page The page object 131 | * @param error Set to an error value (see \ref zathura_error_t) if an 132 | * error occurred 133 | * @return List of links 134 | */ 135 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_links_get(zathura_page_t* page, zathura_error_t* error); 136 | 137 | /** 138 | * Free page links 139 | * 140 | * @param list List of links 141 | * @return ZATHURA_ERROR_OK when no error occurred, otherwise see 142 | * zathura_error_t 143 | */ 144 | ZATHURA_PLUGIN_API zathura_error_t zathura_page_links_free(girara_list_t* list); 145 | 146 | /** 147 | * Get list of form fields 148 | * 149 | * @param page The page object 150 | * @param error Set to an error value (see \ref zathura_error_t) if an 151 | * error occurred 152 | * @return List of form fields 153 | */ 154 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_form_fields_get(zathura_page_t* page, zathura_error_t* error); 155 | 156 | /** 157 | * Free list of form fields 158 | * 159 | * @param list List of form fields 160 | * @return ZATHURA_ERROR_OK when no error occurred, otherwise see 161 | * zathura_error_t 162 | */ 163 | ZATHURA_PLUGIN_API zathura_error_t zathura_page_form_fields_free(girara_list_t* list); 164 | 165 | /** 166 | * Get list of images 167 | * 168 | * @param page Page 169 | * @param error Set to an error value (see \ref zathura_error_t) if an 170 | * error occurred 171 | * @return List of images or NULL if an error occurred 172 | */ 173 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_images_get(zathura_page_t* page, zathura_error_t* error); 174 | 175 | /** 176 | * Get image 177 | * 178 | * @param page Page 179 | * @param image Image identifier 180 | * @param error Set to an error value (see \ref zathura_error_t) if an 181 | * error occurred 182 | * @return The cairo image surface or NULL if an error occurred 183 | */ 184 | ZATHURA_PLUGIN_API cairo_surface_t* zathura_page_image_get_cairo(zathura_page_t* page, zathura_image_t* image, 185 | zathura_error_t* error); 186 | 187 | /** 188 | * Get text for selection 189 | * @param page Page 190 | * @param rectangle Selection 191 | * @param error Set to an error value (see \ref zathura_error_t) if an error 192 | * occurred 193 | * @return The selected text (needs to be deallocated with g_free) 194 | */ 195 | ZATHURA_PLUGIN_API char* zathura_page_get_text(zathura_page_t* page, zathura_rectangle_t rectangle, 196 | zathura_error_t* error); 197 | 198 | /** 199 | * Get rectangles from selection 200 | * @param page Page 201 | * @param rectangle Selection 202 | * @param error Set to an error value (see \ref zathura_error_t) if an error 203 | * occurred 204 | * @return List of rectangles or NULL if an error occurred 205 | */ 206 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_get_selection(zathura_page_t* page, zathura_rectangle_t rectangle, 207 | zathura_error_t* error); 208 | 209 | /** 210 | * Render page 211 | * 212 | * @param page The page object 213 | * @param cairo Cairo object 214 | * @param printing render for printing 215 | * @return ZATHURA_ERROR_OK when no error occurred, otherwise see 216 | * zathura_error_t 217 | */ 218 | ZATHURA_PLUGIN_API zathura_error_t zathura_page_render(zathura_page_t* page, cairo_t* cairo, bool printing); 219 | 220 | /** 221 | * Get page label. Note that the page label might not exist, in this case NULL 222 | * is returned. 223 | * 224 | * @param page Page 225 | * @param error Set to an error value (see \ref zathura_error_t) if an error 226 | * occurred. 227 | * @return Page label 228 | */ 229 | ZATHURA_PLUGIN_API const char* zathura_page_get_label(zathura_page_t* page, zathura_error_t* error); 230 | 231 | /** 232 | * Get whether the page label equals the page number 233 | * 234 | * @param page Page 235 | * @return Boolean indicating whether the page label equals the page number 236 | */ 237 | ZATHURA_PLUGIN_API bool zathura_page_label_is_number(zathura_page_t* page); 238 | 239 | /** 240 | * Get signatures of a page 241 | * 242 | * @param page Page 243 | * @param error Set to an error value (see \ref zathura_error_t) if an error 244 | * occurred. 245 | * @return List of signatures 246 | */ 247 | ZATHURA_PLUGIN_API girara_list_t* zathura_page_get_signatures(zathura_page_t* page, zathura_error_t* error); 248 | 249 | #endif // PAGE_H 250 | -------------------------------------------------------------------------------- /zathura/render.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef RENDER_H 4 | #define RENDER_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "types.h" 12 | 13 | typedef struct zathura_renderer_class_s ZathuraRendererClass; 14 | 15 | struct zathura_renderer_s { 16 | GObject parent; 17 | }; 18 | 19 | struct zathura_renderer_class_s { 20 | GObjectClass parent_class; 21 | }; 22 | 23 | #define ZATHURA_TYPE_RENDERER (zathura_renderer_get_type()) 24 | #define ZATHURA_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_RENDERER, ZathuraRenderer)) 25 | #define ZATHURA_RENDERER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_RENDERER, ZathuraRendererClass)) 26 | #define ZATHURA_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_RENDERER)) 27 | #define ZATHURA_IS_RENDERER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_RENDERER)) 28 | #define ZATHURA_RENDERER_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_RENDERER, ZathuraRendererClass)) 29 | 30 | /** 31 | * Returns the type of the renderer. 32 | * @return the type 33 | */ 34 | GType zathura_renderer_get_type(void) G_GNUC_CONST; 35 | /** 36 | * Create a renderer. 37 | * @return a renderer object 38 | */ 39 | ZathuraRenderer* zathura_renderer_new(size_t cache_size); 40 | 41 | /** 42 | * Return whether recoloring is enabled. 43 | * @param renderer a renderer object 44 | * @returns true if recoloring is enabled, false otherwise 45 | */ 46 | bool zathura_renderer_recolor_enabled(ZathuraRenderer* renderer); 47 | /** 48 | * Enable/disable recoloring. 49 | * @param renderer a renderer object 50 | * @param enable whether to enable or disable recoloring 51 | */ 52 | void zathura_renderer_enable_recolor(ZathuraRenderer* renderer, bool enable); 53 | /** 54 | * Return whether hue should be preserved while recoloring. 55 | * @param renderer a renderer object 56 | * @returns true if hue should be preserved, false otherwise 57 | */ 58 | bool zathura_renderer_recolor_hue_enabled(ZathuraRenderer* renderer); 59 | /** 60 | * Enable/disable preservation of hue while recoloring. 61 | * @param renderer a renderer object 62 | * @param enable whether to enable or disable hue preservation 63 | */ 64 | void zathura_renderer_enable_recolor_hue(ZathuraRenderer* renderer, bool enable); 65 | /** 66 | * Return whether images should be recolored while recoloring. 67 | * @param renderer a renderer object 68 | * @returns true if images should be recolored, false otherwise 69 | */ 70 | bool zathura_renderer_recolor_reverse_video_enabled(ZathuraRenderer* renderer); 71 | /** 72 | * Enable/disable recoloring of images while recoloring. 73 | * @param renderer a renderer object 74 | * @param enable or disable images recoloring 75 | */ 76 | void zathura_renderer_enable_recolor_reverse_video(ZathuraRenderer* renderer, bool enable); 77 | /** 78 | * Return whether lightness should be adjusted while recoloring. 79 | * @param renderer a renderer object 80 | * @returns true if lightness should be adjusted, false otherwise 81 | */ 82 | bool zathura_renderer_recolor_adjust_lightness_enabled(ZathuraRenderer* renderer); 83 | /** 84 | * Enable/disable adjusting lightness while recoloring. 85 | * @param renderer a renderer object 86 | * @param enable or disable adjusting lightness 87 | */ 88 | void zathura_renderer_enable_recolor_adjust_lightness(ZathuraRenderer* renderer, bool enable); 89 | /** 90 | * Set light and dark colors for recoloring. 91 | * @param renderer a renderer object 92 | * @param light light color 93 | * @param dark dark color 94 | */ 95 | void zathura_renderer_set_recolor_colors(ZathuraRenderer* renderer, const GdkRGBA* light, const GdkRGBA* dark); 96 | /** 97 | * Set light and dark colors for recoloring. 98 | * @param renderer a renderer object 99 | * @param light light color 100 | * @param dark dark color 101 | */ 102 | void zathura_renderer_set_recolor_colors_str(ZathuraRenderer* renderer, const char* light, const char* dark); 103 | /** 104 | * Get light and dark colors for recoloring. 105 | * @param renderer a renderer object 106 | * @param light light color 107 | * @param dark dark color 108 | */ 109 | void zathura_renderer_get_recolor_colors(ZathuraRenderer* renderer, GdkRGBA* light, GdkRGBA* dark); 110 | /** 111 | * Stop rendering. 112 | * @param renderer a render object 113 | */ 114 | void zathura_renderer_stop(ZathuraRenderer* renderer); 115 | 116 | /** 117 | * Lock the render thread. This is useful if you want to render on your own (e.g 118 | * for printing). 119 | * 120 | * @param renderer renderer object 121 | */ 122 | void zathura_renderer_lock(ZathuraRenderer* renderer); 123 | 124 | /** 125 | * Unlock the render thread. 126 | * 127 | * @param renderer renderer object. 128 | */ 129 | void zathura_renderer_unlock(ZathuraRenderer* renderer); 130 | 131 | /** 132 | * Add a page to the page cache. 133 | * 134 | * @param renderer renderer object. 135 | * @param page_index The index of the page to be cached. 136 | */ 137 | void zathura_renderer_page_cache_add(ZathuraRenderer* renderer, unsigned int page_index); 138 | 139 | typedef struct zathura_render_request_class_s ZathuraRenderRequestClass; 140 | 141 | struct zathura_render_request_s { 142 | GObject parent; 143 | }; 144 | 145 | struct zathura_render_request_class_s { 146 | GObjectClass parent_class; 147 | }; 148 | 149 | #define ZATHURA_TYPE_RENDER_REQUEST (zathura_render_request_get_type()) 150 | #define ZATHURA_RENDER_REQUEST(obj) \ 151 | (G_TYPE_CHECK_INSTANCE_CAST((obj), ZATHURA_TYPE_RENDER_REQUEST, ZathuraRenderRequest)) 152 | #define ZATHURA_RENDER_REQUEST_CLASS(obj) \ 153 | (G_TYPE_CHECK_CLASS_CAST((obj), ZATHURA_TYPE_RENDER_REQUEST, ZathuraRenderRequestClass)) 154 | #define ZATHURA_IS_RENDER_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZATHURA_TYPE_RENDER_REQUEST)) 155 | #define ZATHURA_IS_RENDER_REQUEST_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((obj), ZATHURA_TYPE_RENDER_REQUEST)) 156 | #define ZATHURA_RENDER_REQUEST_GET_CLASS \ 157 | (G_TYPE_INSTANCE_GET_CLASS((obj), ZATHURA_TYPE_RENDER_REQUEST, ZathuraRenderRequestClass)) 158 | 159 | /** 160 | * Returns the type of the render request. 161 | * @return the type 162 | */ 163 | GType zathura_render_request_get_type(void) G_GNUC_CONST; 164 | /** 165 | * Create a render request object 166 | * @param renderer a renderer object 167 | * @param page the page to be displayed 168 | * @returns render request object 169 | */ 170 | ZathuraRenderRequest* zathura_render_request_new(ZathuraRenderer* renderer, zathura_page_t* page); 171 | 172 | /** 173 | * Add a page to the render thread list that should be rendered. 174 | * 175 | * @param request request object of the page that should be renderer 176 | * @param last_view_time last view time of the page 177 | */ 178 | void zathura_render_request(ZathuraRenderRequest* request, gint64 last_view_time); 179 | 180 | /** 181 | * Abort an existing render request. 182 | * 183 | * @param request request that should be aborted 184 | */ 185 | void zathura_render_request_abort(ZathuraRenderRequest* request); 186 | 187 | /** 188 | * Update the time the page associated to the render request has been viewed the 189 | * last time. 190 | * 191 | * @param request request that should be updated 192 | */ 193 | void zathura_render_request_update_view_time(ZathuraRenderRequest* request); 194 | 195 | /** 196 | * Set "plain" rendering mode, i.e. disabling scaling, recoloring, etc. 197 | * @param request request that should be updated 198 | * @param render_plain "plain" rendering setting 199 | */ 200 | void zathura_render_request_set_render_plain(ZathuraRenderRequest* request, bool render_plain); 201 | 202 | /** 203 | * Get "plain" rendering mode, i.e. disabling scaling, recoloring, etc. 204 | * @param request request that should be updated 205 | * @returns "plain" rendering setting 206 | */ 207 | bool zathura_render_request_get_render_plain(ZathuraRenderRequest* request); 208 | 209 | /** 210 | * This function is used to unmark all pages as not rendered. This should 211 | * be used if all pages should be rendered again (e.g.: the zoom level or the 212 | * colors have changed) 213 | * 214 | * @param zathura Zathura object 215 | */ 216 | void render_all(zathura_t* zathura); 217 | 218 | #endif // RENDER_H 219 | -------------------------------------------------------------------------------- /zathura/main-sandbox.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "zathura.h" 15 | #include "plugin.h" 16 | #include "utils.h" 17 | #ifdef WITH_SECCOMP 18 | #include "seccomp-filters.h" 19 | #endif 20 | #ifdef WITH_LANDLOCK 21 | #include "landlock.h" 22 | #endif 23 | 24 | static zathura_t* init_zathura(const char* config_dir, const char* data_dir, const char* cache_dir, 25 | const char* plugin_path, char** argv, Window embed) { 26 | /* create zathura session */ 27 | zathura_t* zathura = zathura_create(); 28 | if (zathura == NULL) { 29 | return NULL; 30 | } 31 | 32 | zathura_set_xid(zathura, embed); 33 | zathura_set_config_dir(zathura, config_dir); 34 | zathura_set_data_dir(zathura, data_dir); 35 | zathura_set_cache_dir(zathura, cache_dir); 36 | zathura_set_plugin_dir(zathura, plugin_path); 37 | zathura_set_argv(zathura, argv); 38 | 39 | /* Init zathura */ 40 | if (zathura_init(zathura) == false) { 41 | zathura_free(zathura); 42 | return NULL; 43 | } 44 | 45 | girara_debug("Strict sandbox preventing write and network access."); 46 | #ifdef WITH_LANDLOCK 47 | landlock_drop_write(); 48 | #endif 49 | #ifdef WITH_SECCOMP 50 | if (seccomp_enable_strict_filter(zathura) != 0) { 51 | girara_error("Failed to initialize strict seccomp filter."); 52 | zathura_free(zathura); 53 | return NULL; 54 | } 55 | #endif 56 | #ifdef __OpenBSD__ 57 | if (pledge("stdio rpath", "") != 0) { 58 | girara_error("Failed to pledge: %s", strerror(errno)); 59 | zathura_free(zathura); 60 | return NULL; 61 | } 62 | #endif 63 | /* unset the input method to avoid communication with external services */ 64 | unsetenv("GTK_IM_MODULE"); 65 | 66 | return zathura; 67 | } 68 | 69 | /* main function */ 70 | GIRARA_VISIBLE int main(int argc, char* argv[]) { 71 | zathura_init_locale(); 72 | 73 | /* parse command line arguments */ 74 | g_autofree gchar* config_dir = NULL; 75 | g_autofree gchar* data_dir = NULL; 76 | g_autofree gchar* cache_dir = NULL; 77 | g_autofree gchar* plugin_path = NULL; 78 | g_autofree gchar* loglevel = NULL; 79 | g_autofree gchar* password = NULL; 80 | g_autofree gchar* mode = NULL; 81 | g_autofree gchar* bookmark_name = NULL; 82 | g_autofree gchar* search_string = NULL; 83 | bool forkback = false; 84 | bool print_version = false; 85 | int page_number = ZATHURA_PAGE_NUMBER_UNSPECIFIED; 86 | Window embed = 0; 87 | 88 | GOptionEntry entries[] = { 89 | {"reparent", 'e', 0, G_OPTION_ARG_INT, &embed, _("Reparents to window specified by xid (X11)"), "xid"}, 90 | {"config-dir", 'c', 0, G_OPTION_ARG_FILENAME, &config_dir, _("Path to the config directory"), "path"}, 91 | {"data-dir", 'd', 0, G_OPTION_ARG_FILENAME, &data_dir, _("Path to the data directory"), "path"}, 92 | {"cache-dir", '\0', 0, G_OPTION_ARG_FILENAME, &cache_dir, _("Path to the cache directory"), "path"}, 93 | {"plugins-dir", 'p', 0, G_OPTION_ARG_STRING, &plugin_path, _("Path to the directories containing plugins"), 94 | "path"}, 95 | {"fork", '\0', 0, G_OPTION_ARG_NONE, &forkback, _("Fork into the background"), NULL}, 96 | {"password", 'w', 0, G_OPTION_ARG_STRING, &password, _("Document password"), "password"}, 97 | {"page", 'P', 0, G_OPTION_ARG_INT, &page_number, _("Page number to go to"), "number"}, 98 | {"log-level", 'l', 0, G_OPTION_ARG_STRING, &loglevel, _("Log level (debug, info, warning, error)"), "level"}, 99 | {"version", 'v', 0, G_OPTION_ARG_NONE, &print_version, _("Print version information"), NULL}, 100 | {"mode", '\0', 0, G_OPTION_ARG_STRING, &mode, _("Start in a non-default mode"), "mode"}, 101 | {"bookmark", 'b', 0, G_OPTION_ARG_STRING, &bookmark_name, _("Bookmark to go to"), "bookmark"}, 102 | {"find", 'f', 0, G_OPTION_ARG_STRING, &search_string, _("Search for the given phrase and display results"), 103 | "string"}, 104 | {NULL, '\0', 0, 0, NULL, NULL, NULL}, 105 | }; 106 | 107 | g_autoptr(GOptionContext) context = g_option_context_new(" [file1] [file2] [...]"); 108 | g_option_context_add_main_entries(context, entries, NULL); 109 | 110 | g_autoptr(GError) error = NULL; 111 | if (g_option_context_parse(context, &argc, &argv, &error) == false) { 112 | girara_error("Error parsing command line arguments: %s\n", error->message); 113 | return -1; 114 | } 115 | 116 | zathura_set_log_level(loglevel); 117 | 118 | /* check mode */ 119 | if (mode != NULL && g_strcmp0(mode, "presentation") != 0 && g_strcmp0(mode, "fullscreen") != 0) { 120 | girara_error("Invalid argument for --mode: %s", mode); 121 | return -1; 122 | } 123 | 124 | /* g_option_context_parse has some funny (documented) behavior: 125 | * * for "-- a b c" you get no -- in argv 126 | * * for "-- --" you get -- in argv twice 127 | * * for "-- -a" you get -- in argv 128 | * 129 | * So if there is one -- in argv, we need to ignore it. */ 130 | const bool has_double_dash = argc > 1 && g_strcmp0(argv[1], "--") == 0; 131 | const int file_idx_base = has_double_dash ? 2 : 1; 132 | 133 | int file_idx = argc > file_idx_base ? file_idx_base : 0; 134 | /* Fork instances for other files. */ 135 | if (print_version == false && argc > file_idx_base + 1) { 136 | for (int idx = file_idx_base + 1; idx < argc; ++idx) { 137 | const pid_t pid = fork(); 138 | if (pid == 0) { /* child */ 139 | file_idx = idx; 140 | if (setsid() == -1) { 141 | girara_error("Could not start new process group: %s", strerror(errno)); 142 | return -1; 143 | } 144 | break; 145 | } else if (pid < 0) { /* error */ 146 | girara_error("Could not fork: %s", strerror(errno)); 147 | return -1; 148 | } 149 | } 150 | } 151 | 152 | /* Fork into the background if the user really wants to ... */ 153 | if (print_version == false && forkback == true && file_idx < file_idx_base + 1) { 154 | const pid_t pid = fork(); 155 | if (pid > 0) { /* parent */ 156 | return 0; 157 | } else if (pid < 0) { /* error */ 158 | girara_error("Could not fork: %s", strerror(errno)); 159 | return -1; 160 | } 161 | 162 | if (setsid() == -1) { 163 | girara_error("Could not start new process group: %s", strerror(errno)); 164 | return -1; 165 | } 166 | } 167 | 168 | /* Print version */ 169 | if (print_version == true) { 170 | zathura_plugin_manager_t* plugin_manager = zathura_plugin_manager_new(); 171 | zathura_plugin_manager_set_dir(plugin_manager, plugin_path); 172 | zathura_plugin_manager_load(plugin_manager); 173 | 174 | char* string = zathura_get_version_string(plugin_manager, false); 175 | if (string != NULL) { 176 | fprintf(stdout, "%s\n", string); 177 | g_free(string); 178 | } 179 | zathura_plugin_manager_free(plugin_manager); 180 | 181 | return 0; 182 | } 183 | 184 | girara_debug("Running zathura-sandbox, disable IPC services"); 185 | /* Prevent default gtk dbus connection */ 186 | g_setenv("DBUS_SESSION_BUS_ADDRESS", "disabled:", TRUE); 187 | 188 | /* disable dconf writing - uses /var/empty as alternative to /dev/null to avoid ioctl call */ 189 | g_setenv("DCONF_PROFILE", "/var/empty", TRUE); 190 | 191 | /* Initialize GTK+ */ 192 | gtk_init(&argc, &argv); 193 | 194 | /* Create zathura session */ 195 | g_autoptr(zathura_t) zathura = init_zathura(config_dir, data_dir, cache_dir, plugin_path, argv, embed); 196 | if (zathura == NULL) { 197 | girara_error("Could not initialize zathura."); 198 | return -1; 199 | } 200 | 201 | /* open document if passed */ 202 | if (file_idx != 0) { 203 | if (page_number > 0) { 204 | --page_number; 205 | } 206 | document_open_idle(zathura, argv[file_idx], password, page_number, mode, NULL, bookmark_name, search_string); 207 | } else if (bookmark_name != NULL) { 208 | girara_error("Can not use bookmark argument when no file is given"); 209 | return -1; 210 | } else if (search_string != NULL) { 211 | girara_error("Can not use find argument when no file is given"); 212 | return -1; 213 | } 214 | 215 | /* run zathura */ 216 | gtk_main(); 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /zathura/marks.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "callbacks.h" 10 | #include "database.h" 11 | #include "document.h" 12 | #include "marks.h" 13 | #include "render.h" 14 | #include "utils.h" 15 | 16 | static void mark_add(zathura_t* zathura, int key); 17 | static void mark_evaluate(zathura_t* zathura, int key); 18 | 19 | static gboolean cb_marks_view_key_press_event_add(GtkWidget* UNUSED(widget), GdkEventKey* event, gpointer user_data) { 20 | g_return_val_if_fail(user_data != NULL, FALSE); 21 | girara_session_t* session = user_data; 22 | g_return_val_if_fail(session->gtk.view != NULL, FALSE); 23 | g_return_val_if_fail(session->global.data != NULL, FALSE); 24 | zathura_t* zathura = session->global.data; 25 | 26 | /* reset signal handler */ 27 | g_signal_handler_disconnect(G_OBJECT(session->gtk.view), session->signals.view_key_pressed); 28 | session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event", 29 | G_CALLBACK(girara_callback_view_key_press_event), session); 30 | 31 | /* evaluate key */ 32 | if (((event->keyval >= '0' && event->keyval <= '9') || (event->keyval >= 'a' && event->keyval <= 'z') || 33 | (event->keyval >= 'A' && event->keyval <= 'Z')) == false) { 34 | return FALSE; 35 | } 36 | 37 | mark_add(zathura, event->keyval); 38 | 39 | return TRUE; 40 | } 41 | 42 | static gboolean cb_marks_view_key_press_event_evaluate(GtkWidget* UNUSED(widget), GdkEventKey* event, 43 | gpointer user_data) { 44 | g_return_val_if_fail(user_data != NULL, FALSE); 45 | girara_session_t* session = user_data; 46 | g_return_val_if_fail(session->gtk.view != NULL, FALSE); 47 | g_return_val_if_fail(session->global.data != NULL, FALSE); 48 | zathura_t* zathura = session->global.data; 49 | 50 | /* reset signal handler */ 51 | g_signal_handler_disconnect(G_OBJECT(session->gtk.view), session->signals.view_key_pressed); 52 | session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event", 53 | G_CALLBACK(girara_callback_view_key_press_event), session); 54 | 55 | /* evaluate key */ 56 | if (((event->keyval >= '0' && event->keyval <= '9') || (event->keyval >= 'a' && event->keyval <= 'z') || 57 | (event->keyval >= 'A' && event->keyval <= 'Z')) == false) { 58 | return TRUE; 59 | } 60 | 61 | mark_evaluate(zathura, event->keyval); 62 | 63 | return TRUE; 64 | } 65 | 66 | bool sc_mark_add(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), 67 | unsigned int UNUSED(t)) { 68 | g_return_val_if_fail(session != NULL, false); 69 | g_return_val_if_fail(session->gtk.view != NULL, false); 70 | 71 | /* redirect signal handler */ 72 | g_signal_handler_disconnect(G_OBJECT(session->gtk.view), session->signals.view_key_pressed); 73 | session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event", 74 | G_CALLBACK(cb_marks_view_key_press_event_add), session); 75 | 76 | return true; 77 | } 78 | 79 | bool sc_mark_evaluate(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), 80 | unsigned int UNUSED(t)) { 81 | g_return_val_if_fail(session != NULL, false); 82 | g_return_val_if_fail(session->gtk.view != NULL, false); 83 | 84 | /* redirect signal handler */ 85 | g_signal_handler_disconnect(G_OBJECT(session->gtk.view), session->signals.view_key_pressed); 86 | session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event", 87 | G_CALLBACK(cb_marks_view_key_press_event_evaluate), session); 88 | 89 | return true; 90 | } 91 | 92 | bool cmd_marks_add(girara_session_t* session, girara_list_t* argument_list) { 93 | g_return_val_if_fail(session != NULL, false); 94 | g_return_val_if_fail(session->global.data != NULL, false); 95 | zathura_t* zathura = (zathura_t*)session->global.data; 96 | 97 | if (girara_list_size(argument_list) < 1) { 98 | return false; 99 | } 100 | 101 | const char* key_string = girara_list_nth(argument_list, 0); 102 | 103 | if (key_string == NULL || strlen(key_string) != 1) { 104 | return false; 105 | } 106 | 107 | const char key = key_string[0]; 108 | if (((key >= 0x41 && key <= 0x5A) || (key >= 0x61 && key <= 0x7A)) == false) { 109 | return false; 110 | } 111 | 112 | mark_add(zathura, key); 113 | 114 | return false; 115 | } 116 | 117 | bool cmd_marks_delete(girara_session_t* session, girara_list_t* argument_list) { 118 | g_return_val_if_fail(session != NULL, false); 119 | g_return_val_if_fail(session->global.data != NULL, false); 120 | zathura_t* zathura = (zathura_t*)session->global.data; 121 | 122 | if (girara_list_size(argument_list) < 1) { 123 | return false; 124 | } 125 | 126 | if (girara_list_size(zathura->global.marks) == 0) { 127 | return false; 128 | } 129 | 130 | for (size_t idx = 0; idx != girara_list_size(argument_list); ++idx) { 131 | char* key_string = girara_list_nth(argument_list, idx); 132 | if (key_string == NULL) { 133 | continue; 134 | } 135 | 136 | for (unsigned int i = 0; i < strlen(key_string); i++) { 137 | char key = key_string[i]; 138 | if (((key >= 0x41 && key <= 0x5A) || (key >= 0x61 && key <= 0x7A)) == false) { 139 | continue; 140 | } 141 | 142 | /* search for existing mark */ 143 | for (size_t inner_idx = girara_list_size(zathura->global.marks); inner_idx; --inner_idx) { 144 | zathura_mark_t* mark = girara_list_nth(zathura->global.marks, inner_idx - 1); 145 | if (mark == NULL) { 146 | continue; 147 | } 148 | 149 | if (mark->key == key) { 150 | girara_list_remove(zathura->global.marks, mark); 151 | } 152 | } 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | 159 | static void mark_add(zathura_t* zathura, int key) { 160 | if (zathura_has_document(zathura) == false || zathura->global.marks == NULL) { 161 | return; 162 | } 163 | 164 | zathura_document_t* document = zathura_get_document(zathura); 165 | unsigned int page_id = zathura_document_get_current_page_number(document); 166 | double position_x = zathura_document_get_position_x(document); 167 | double position_y = zathura_document_get_position_y(document); 168 | 169 | double zoom = zathura_document_get_zoom(document); 170 | 171 | /* search for existing mark */ 172 | for (size_t idx = 0; idx != girara_list_size(zathura->global.marks); ++idx) { 173 | zathura_mark_t* mark = girara_list_nth(zathura->global.marks, idx); 174 | if (mark->key == key) { 175 | mark->page = page_id; 176 | mark->position_x = position_x; 177 | mark->position_y = position_y; 178 | mark->zoom = zoom; 179 | return; 180 | } 181 | } 182 | 183 | /* add new mark */ 184 | zathura_mark_t* mark = g_try_malloc0(sizeof(zathura_mark_t)); 185 | if (mark == NULL) { 186 | return; 187 | } 188 | 189 | mark->key = key; 190 | mark->page = page_id; 191 | mark->position_x = position_x; 192 | mark->position_y = position_y; 193 | mark->zoom = zoom; 194 | 195 | girara_list_append(zathura->global.marks, mark); 196 | } 197 | 198 | static void mark_evaluate(zathura_t* zathura, int key) { 199 | if (zathura == NULL || zathura->global.marks == NULL) { 200 | return; 201 | } 202 | 203 | /* search for existing mark */ 204 | for (size_t idx = 0; idx != girara_list_size(zathura->global.marks); ++idx) { 205 | zathura_mark_t* mark = girara_list_nth(zathura->global.marks, idx); 206 | if (mark != NULL && mark->key == key) { 207 | zathura_document_set_zoom(zathura_get_document(zathura), 208 | zathura_correct_zoom_value(zathura->ui.session, mark->zoom)); 209 | 210 | adjust_view(zathura); 211 | render_all(zathura); 212 | 213 | zathura_jumplist_add(zathura); 214 | page_set(zathura, mark->page); 215 | position_set(zathura, mark->position_x, mark->position_y); 216 | zathura_jumplist_add(zathura); 217 | 218 | return; 219 | } 220 | } 221 | } 222 | 223 | bool zathura_quickmarks_load(zathura_t* zathura, const gchar* file) { 224 | g_return_val_if_fail(zathura, false); 225 | g_return_val_if_fail(file, false); 226 | 227 | if (zathura->database == NULL) { 228 | return false; 229 | } 230 | 231 | girara_list_t* marks = zathura_db_load_quickmarks(zathura->database, file); 232 | if (marks == NULL) { 233 | return false; 234 | } 235 | 236 | girara_list_free(zathura->global.marks); 237 | zathura->global.marks = marks; 238 | 239 | return true; 240 | } -------------------------------------------------------------------------------- /zathura/plugin-api.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #ifndef PLUGIN_API_H 4 | #define PLUGIN_API_H 5 | 6 | #include 7 | 8 | #include "types.h" 9 | #include "page.h" 10 | #include "document.h" 11 | #include "links.h" 12 | #include "zathura-version.h" 13 | 14 | typedef struct zathura_plugin_functions_s zathura_plugin_functions_t; 15 | 16 | /** 17 | * Opens a document 18 | */ 19 | typedef zathura_error_t (*zathura_plugin_document_open_t)(zathura_document_t* document); 20 | 21 | /** 22 | * Frees the document 23 | */ 24 | typedef zathura_error_t (*zathura_plugin_document_free_t)(zathura_document_t* document, void* data); 25 | 26 | /** 27 | * Generates the document index 28 | */ 29 | typedef girara_tree_node_t* (*zathura_plugin_document_index_generate_t)(zathura_document_t* document, void* data, 30 | zathura_error_t* error); 31 | 32 | /** 33 | * Save the document 34 | */ 35 | typedef zathura_error_t (*zathura_plugin_document_save_as_t)(zathura_document_t* document, void* data, 36 | const char* path); 37 | 38 | /** 39 | * Get list of attachments 40 | */ 41 | typedef girara_list_t* (*zathura_plugin_document_attachments_get_t)(zathura_document_t* document, void* data, 42 | zathura_error_t* error); 43 | 44 | /** 45 | * Save attachment to a file 46 | */ 47 | typedef zathura_error_t (*zathura_plugin_document_attachment_save_t)(zathura_document_t* document, void* data, 48 | const char* attachment, const char* file); 49 | 50 | /** 51 | * Get document information 52 | */ 53 | typedef girara_list_t* (*zathura_plugin_document_get_information_t)(zathura_document_t* document, void* data, 54 | zathura_error_t* error); 55 | 56 | /** 57 | * Gets the page object 58 | */ 59 | typedef zathura_error_t (*zathura_plugin_page_init_t)(zathura_page_t* page); 60 | 61 | /** 62 | * Free page 63 | */ 64 | typedef zathura_error_t (*zathura_plugin_page_clear_t)(zathura_page_t* page, void* data); 65 | 66 | /** 67 | * Search text 68 | */ 69 | typedef girara_list_t* (*zathura_plugin_page_search_text_t)(zathura_page_t* page, void* data, const char* text, 70 | zathura_error_t* error); 71 | 72 | /** 73 | * Get links on a page 74 | */ 75 | typedef girara_list_t* (*zathura_plugin_page_links_get_t)(zathura_page_t* page, void* data, zathura_error_t* error); 76 | 77 | /** 78 | * Get form fields 79 | */ 80 | typedef girara_list_t* (*zathura_plugin_page_form_fields_get_t)(zathura_page_t* page, void* data, 81 | zathura_error_t* error); 82 | 83 | /** 84 | * Get list of images 85 | */ 86 | typedef girara_list_t* (*zathura_plugin_page_images_get_t)(zathura_page_t* page, void* data, zathura_error_t* error); 87 | 88 | /** 89 | * Get the image 90 | */ 91 | typedef cairo_surface_t* (*zathura_plugin_page_image_get_cairo_t)(zathura_page_t* page, void* data, 92 | zathura_image_t* image, zathura_error_t* error); 93 | 94 | /** 95 | * Get text for selection 96 | */ 97 | typedef char* (*zathura_plugin_page_get_text_t)(zathura_page_t* page, void* data, zathura_rectangle_t rectangle, 98 | zathura_error_t* error); 99 | 100 | /** 101 | * Get rectangles from selection 102 | */ 103 | typedef girara_list_t* (*zathura_plugin_page_get_selection_t)(zathura_page_t* page, void* data, 104 | zathura_rectangle_t rectangle, zathura_error_t* error); 105 | 106 | /** 107 | * Renders the page to a cairo surface. 108 | */ 109 | typedef zathura_error_t (*zathura_plugin_page_render_cairo_t)(zathura_page_t* page, void* data, cairo_t* cairo, 110 | bool printing); 111 | 112 | /** 113 | * Get page label. 114 | */ 115 | typedef zathura_error_t (*zathura_plugin_page_get_label_t)(zathura_page_t* page, void* data, char** label); 116 | 117 | /** 118 | * Get signatures 119 | */ 120 | typedef girara_list_t* (*zathura_plugin_page_get_signatures)(zathura_page_t* page, void* data, zathura_error_t* error); 121 | 122 | struct zathura_plugin_functions_s { 123 | /** 124 | * Opens a document 125 | */ 126 | zathura_plugin_document_open_t document_open; 127 | 128 | /** 129 | * Frees the document 130 | */ 131 | zathura_plugin_document_free_t document_free; 132 | 133 | /** 134 | * Generates the document index 135 | */ 136 | zathura_plugin_document_index_generate_t document_index_generate; 137 | 138 | /** 139 | * Save the document 140 | */ 141 | zathura_plugin_document_save_as_t document_save_as; 142 | 143 | /** 144 | * Get list of attachments 145 | */ 146 | zathura_plugin_document_attachments_get_t document_attachments_get; 147 | 148 | /** 149 | * Save attachment to a file 150 | */ 151 | zathura_plugin_document_attachment_save_t document_attachment_save; 152 | 153 | /** 154 | * Get document information 155 | */ 156 | zathura_plugin_document_get_information_t document_get_information; 157 | 158 | /** 159 | * Gets the page object 160 | */ 161 | zathura_plugin_page_init_t page_init; 162 | 163 | /** 164 | * Free page 165 | */ 166 | zathura_plugin_page_clear_t page_clear; 167 | 168 | /** 169 | * Search text 170 | */ 171 | zathura_plugin_page_search_text_t page_search_text; 172 | 173 | /** 174 | * Get links on a page 175 | */ 176 | zathura_plugin_page_links_get_t page_links_get; 177 | 178 | /** 179 | * Get form fields 180 | */ 181 | zathura_plugin_page_form_fields_get_t page_form_fields_get; 182 | 183 | /** 184 | * Get list of images 185 | */ 186 | zathura_plugin_page_images_get_t page_images_get; 187 | 188 | /** 189 | * Get the image 190 | */ 191 | zathura_plugin_page_image_get_cairo_t page_image_get_cairo; 192 | 193 | /** 194 | * Get text for selection 195 | */ 196 | zathura_plugin_page_get_text_t page_get_text; 197 | 198 | /** 199 | * Get text for selection 200 | */ 201 | zathura_plugin_page_get_selection_t page_get_selection; 202 | 203 | /** 204 | * Renders the page to a cairo surface. 205 | */ 206 | zathura_plugin_page_render_cairo_t page_render_cairo; 207 | 208 | /** 209 | * Get page label. 210 | */ 211 | zathura_plugin_page_get_label_t page_get_label; 212 | 213 | /** 214 | * Get signatures. 215 | */ 216 | zathura_plugin_page_get_signatures page_get_signatures; 217 | }; 218 | 219 | typedef struct zathura_plugin_version_s { 220 | unsigned int major; /**< Major */ 221 | unsigned int minor; /**< Minor */ 222 | unsigned int rev; /**< Revision */ 223 | } zathura_plugin_version_t; 224 | 225 | typedef struct zathura_plugin_definition_s { 226 | const char* name; 227 | const zathura_plugin_version_t version; 228 | zathura_plugin_functions_t functions; 229 | const size_t mime_types_size; 230 | const char** mime_types; 231 | } zathura_plugin_definition_t; 232 | 233 | #define JOIN(x, y) JOIN2(x, y) 234 | #define JOIN2(x, y) x##_##y 235 | 236 | #define ZATHURA_PLUGIN_DEFINITION_SYMBOL JOIN(zathura_plugin, JOIN(ZATHURA_API_VERSION, ZATHURA_ABI_VERSION)) 237 | 238 | /** 239 | * Register a plugin. 240 | * 241 | * @param plugin_name the name of the plugin 242 | * @param major the plugin's major version 243 | * @param minor the plugin's minor version 244 | * @param rev the plugin's revision 245 | * @param plugin_functions function to register the plugin's document functions 246 | * @param mimetypes a char array of mime types supported by the plugin 247 | */ 248 | #define ZATHURA_PLUGIN_REGISTER_WITH_FUNCTIONS(plugin_name, major, minor, rev, plugin_functions, mimetypes) \ 249 | static const char* zathura_plugin_mime_types[] = mimetypes; \ 250 | \ 251 | ZATHURA_PLUGIN_API const zathura_plugin_definition_t ZATHURA_PLUGIN_DEFINITION_SYMBOL = { \ 252 | .name = plugin_name, \ 253 | .version = {major, minor, rev}, \ 254 | .functions = plugin_functions, \ 255 | .mime_types_size = sizeof(zathura_plugin_mime_types) / sizeof(zathura_plugin_mime_types[0]), \ 256 | .mime_types = zathura_plugin_mime_types, \ 257 | }; 258 | 259 | #define ZATHURA_PLUGIN_MIMETYPES(...) __VA_ARGS__ 260 | #define ZATHURA_PLUGIN_FUNCTIONS(...) __VA_ARGS__ 261 | 262 | #endif // PLUGIN_API_H 263 | -------------------------------------------------------------------------------- /zathura/links.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "adjustment.h" 10 | #include "links.h" 11 | #include "zathura.h" 12 | #include "document.h" 13 | #include "utils.h" 14 | #include "page.h" 15 | #include "render.h" 16 | 17 | struct zathura_link_s { 18 | zathura_rectangle_t position; /**< Position of the link */ 19 | zathura_link_target_t target; /**< Link target */ 20 | zathura_link_type_t type; /**< Link type */ 21 | }; 22 | 23 | zathura_link_t* zathura_link_new(zathura_link_type_t type, zathura_rectangle_t position, zathura_link_target_t target) { 24 | zathura_link_t* link = g_try_malloc0(sizeof(zathura_link_t)); 25 | if (link == NULL) { 26 | return NULL; 27 | } 28 | 29 | link->position = position; 30 | link->target = target; 31 | link->type = type; 32 | 33 | /* duplicate target.value if necessary */ 34 | switch (type) { 35 | case ZATHURA_LINK_NONE: 36 | case ZATHURA_LINK_GOTO_DEST: 37 | if (target.value != NULL) { 38 | link->target.value = g_strdup(target.value); 39 | } 40 | break; 41 | case ZATHURA_LINK_GOTO_REMOTE: 42 | case ZATHURA_LINK_URI: 43 | case ZATHURA_LINK_LAUNCH: 44 | case ZATHURA_LINK_NAMED: 45 | /* target.value is required for these cases */ 46 | if (target.value == NULL) { 47 | g_free(link); 48 | return NULL; 49 | } 50 | 51 | link->target.value = g_strdup(target.value); 52 | break; 53 | default: 54 | g_free(link); 55 | return NULL; 56 | } 57 | 58 | return link; 59 | } 60 | 61 | void zathura_link_free(zathura_link_t* link) { 62 | if (link == NULL) { 63 | return; 64 | } 65 | 66 | switch (link->type) { 67 | case ZATHURA_LINK_NONE: 68 | case ZATHURA_LINK_GOTO_DEST: 69 | case ZATHURA_LINK_GOTO_REMOTE: 70 | case ZATHURA_LINK_URI: 71 | case ZATHURA_LINK_LAUNCH: 72 | case ZATHURA_LINK_NAMED: 73 | if (link->target.value != NULL) { 74 | g_free(link->target.value); 75 | } 76 | break; 77 | default: 78 | break; 79 | } 80 | 81 | g_free(link); 82 | } 83 | 84 | zathura_link_type_t zathura_link_get_type(zathura_link_t* link) { 85 | if (link == NULL) { 86 | return ZATHURA_LINK_INVALID; 87 | } 88 | 89 | return link->type; 90 | } 91 | 92 | zathura_rectangle_t zathura_link_get_position(zathura_link_t* link) { 93 | if (link == NULL) { 94 | const zathura_rectangle_t position = {0, 0, 0, 0}; 95 | return position; 96 | } 97 | 98 | return link->position; 99 | } 100 | 101 | zathura_link_target_t zathura_link_get_target(zathura_link_t* link) { 102 | if (link == NULL) { 103 | const zathura_link_target_t target = {0, NULL, 0, 0, 0, 0, 0, 0}; 104 | return target; 105 | } 106 | 107 | return link->target; 108 | } 109 | 110 | static void link_goto_dest(zathura_t* zathura, const zathura_link_t* link) { 111 | if (link->target.destination_type == ZATHURA_LINK_DESTINATION_UNKNOWN) { 112 | girara_warning("link destination type unknown"); 113 | return; 114 | } 115 | 116 | bool link_zoom = true; 117 | girara_setting_get(zathura->ui.session, "link-zoom", &link_zoom); 118 | 119 | zathura_document_t* document = zathura_get_document(zathura); 120 | if (link->target.zoom >= DBL_EPSILON && link_zoom == true) { 121 | zathura_document_set_zoom(document, zathura_correct_zoom_value(zathura->ui.session, link->target.zoom)); 122 | adjust_view(zathura); 123 | render_all(zathura); 124 | } 125 | 126 | /* get page */ 127 | zathura_page_t* page = zathura_document_get_page(document, link->target.page_number); 128 | if (page == NULL) { 129 | girara_warning("link to non-existing page %u", link->target.page_number); 130 | return; 131 | } 132 | 133 | /* compute the position with the page aligned to the top and left 134 | of the viewport */ 135 | double pos_x = 0; 136 | double pos_y = 0; 137 | page_number_to_position(document, link->target.page_number, 0.0, 0.0, &pos_x, &pos_y); 138 | 139 | /* correct to place the target position at the top of the viewport */ 140 | /* NOTE: link->target is in page units, needs to be scaled and rotated */ 141 | unsigned int cell_height = 0; 142 | unsigned int cell_width = 0; 143 | zathura_document_get_cell_size(document, &cell_height, &cell_width); 144 | 145 | unsigned int doc_height = 0; 146 | unsigned int doc_width = 0; 147 | zathura_document_get_document_size(document, &doc_height, &doc_width); 148 | 149 | bool link_hadjust = true; 150 | girara_setting_get(zathura->ui.session, "link-hadjust", &link_hadjust); 151 | 152 | /* scale and rotate */ 153 | const double scale = zathura_document_get_scale(document); 154 | double shiftx = link->target.left * scale / cell_width; 155 | double shifty = link->target.top * scale / cell_height; 156 | page_calc_position(document, shiftx, shifty, &shiftx, &shifty); 157 | 158 | /* shift the position or set to auto */ 159 | if (link->target.destination_type == ZATHURA_LINK_DESTINATION_XYZ && link->target.left != -1 && 160 | link_hadjust == true) { 161 | pos_x += shiftx * cell_width / doc_width; 162 | } else { 163 | pos_x = -1; /* -1 means automatic */ 164 | } 165 | 166 | if (link->target.destination_type == ZATHURA_LINK_DESTINATION_XYZ && link->target.top != -1) { 167 | pos_y += shifty * cell_height / doc_height; 168 | } else { 169 | pos_y = -1; /* -1 means automatic */ 170 | } 171 | 172 | /* move to position */ 173 | zathura_jumplist_add(zathura); 174 | zathura_document_set_current_page_number(document, link->target.page_number); 175 | position_set(zathura, pos_x, pos_y); 176 | zathura_jumplist_add(zathura); 177 | } 178 | 179 | #ifndef WITH_SANDBOX 180 | static void link_remote(zathura_t* zathura, const char* file) { 181 | if (zathura_has_document(zathura) == false || file == NULL) { 182 | return; 183 | } 184 | 185 | const char* path = zathura_document_get_path(zathura_get_document(zathura)); 186 | g_autofree char* dir = g_path_get_dirname(path); 187 | g_autofree char* uri = g_build_filename(file, NULL); 188 | 189 | char* argv[] = {*zathura->global.arguments, uri, NULL}; 190 | 191 | g_autoptr(GError) error = NULL; 192 | if (g_spawn_async(dir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error) == FALSE) { 193 | girara_error("Failed to execute command: %s", error->message); 194 | } 195 | } 196 | 197 | static void link_launch(zathura_t* zathura, const zathura_link_t* link) { 198 | /* get file path */ 199 | if (link->target.value == NULL) { 200 | return; 201 | } 202 | 203 | const char* document = zathura_document_get_path(zathura_get_document(zathura)); 204 | g_autofree char* dir = g_path_get_dirname(document); 205 | 206 | if (girara_xdg_open_with_working_directory(link->target.value, dir) == false) { 207 | girara_notify(zathura->ui.session, GIRARA_ERROR, _("Failed to run xdg-open.")); 208 | } 209 | } 210 | #endif 211 | 212 | void zathura_link_evaluate(zathura_t* zathura, zathura_link_t* link) { 213 | if (zathura_has_document(zathura) == false || link == NULL) { 214 | return; 215 | } 216 | 217 | #ifdef WITH_SANDBOX 218 | if (link->type != ZATHURA_LINK_GOTO_DEST) { 219 | girara_notify(zathura->ui.session, GIRARA_ERROR, 220 | _("Opening external applications in strict sandbox mode is not permitted")); 221 | return; 222 | } 223 | #endif 224 | 225 | switch (link->type) { 226 | case ZATHURA_LINK_GOTO_DEST: 227 | girara_debug("Going to link destination: page: %d", link->target.page_number); 228 | link_goto_dest(zathura, link); 229 | break; 230 | #ifndef WITH_SANDBOX 231 | case ZATHURA_LINK_GOTO_REMOTE: 232 | girara_debug("Going to remote destination: %s", link->target.value); 233 | link_remote(zathura, link->target.value); 234 | break; 235 | case ZATHURA_LINK_URI: 236 | girara_debug("Opening URI: %s", link->target.value); 237 | link_launch(zathura, link); 238 | break; 239 | case ZATHURA_LINK_LAUNCH: 240 | girara_debug("Launching link: %s", link->target.value); 241 | link_launch(zathura, link); 242 | break; 243 | #endif 244 | default: 245 | girara_error("Unhandled link type: %d", link->type); 246 | break; 247 | } 248 | } 249 | 250 | void zathura_link_display(zathura_t* zathura, zathura_link_t* link) { 251 | zathura_link_type_t type = zathura_link_get_type(link); 252 | zathura_link_target_t target = zathura_link_get_target(link); 253 | switch (type) { 254 | case ZATHURA_LINK_GOTO_DEST: 255 | girara_notify(zathura->ui.session, GIRARA_INFO, _("Link: page %d"), target.page_number); 256 | break; 257 | case ZATHURA_LINK_GOTO_REMOTE: 258 | case ZATHURA_LINK_URI: 259 | case ZATHURA_LINK_LAUNCH: 260 | case ZATHURA_LINK_NAMED: 261 | girara_notify(zathura->ui.session, GIRARA_INFO, _("Link: %s"), target.value); 262 | break; 263 | default: 264 | girara_notify(zathura->ui.session, GIRARA_ERROR, _("Link: Invalid")); 265 | } 266 | } 267 | 268 | void zathura_link_copy(zathura_t* zathura, zathura_link_t* link, GdkAtom* selection) { 269 | zathura_link_type_t type = zathura_link_get_type(link); 270 | zathura_link_target_t target = zathura_link_get_target(link); 271 | switch (type) { 272 | case ZATHURA_LINK_GOTO_DEST: { 273 | g_autofree gchar* tmp = g_strdup_printf("%d", target.page_number); 274 | gtk_clipboard_set_text(gtk_clipboard_get(*selection), tmp, -1); 275 | girara_notify(zathura->ui.session, GIRARA_INFO, _("Copied page number: %d"), target.page_number); 276 | break; 277 | } 278 | case ZATHURA_LINK_GOTO_REMOTE: 279 | case ZATHURA_LINK_URI: 280 | case ZATHURA_LINK_LAUNCH: 281 | case ZATHURA_LINK_NAMED: 282 | gtk_clipboard_set_text(gtk_clipboard_get(*selection), target.value, -1); 283 | girara_notify(zathura->ui.session, GIRARA_INFO, _("Copied link: %s"), target.value); 284 | break; 285 | default: 286 | girara_notify(zathura->ui.session, GIRARA_ERROR, _("Link: Invalid")); 287 | } 288 | } 289 | --------------------------------------------------------------------------------