dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

commit 4455a4eda08dd6e84b0159dd4ab2cde4fb75fbdb
parent a08a9ce6307ea87cb75a14353e26a9e8edecbcf8
Author: Lukas Henkel <lh@entf.net>
Date:   Mon, 13 Dec 2021 22:15:21 +0100

Add pdf-tools

Diffstat:
Aelpa/pdf-tools-20211110.513/README | 388+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/Makefile | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/.gitignore | 25+++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/Makefile.am | 44++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/autobuild | 534+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/autogen.sh | 5+++++
Aelpa/pdf-tools-20211110.513/build/server/configure.ac | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/epdfinfo.c | 3718+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/epdfinfo.h | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/install_test.cpp | 7+++++++
Aelpa/pdf-tools-20211110.513/build/server/m4/ax_check_compile_flag.m4 | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/poppler-hack.cc | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/poppler-versions | 12++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser.c | 8924+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser.h | 429+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_advanced.h | 554+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_local.h | 3+++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_readme.txt | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_utils.c | 570+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_utils.h | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/synctex_parser_version.txt | 1+
Aelpa/pdf-tools-20211110.513/build/server/synctex_version.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/.gitignore | 2++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/lib/run-tests | 9+++++++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/lib/yes-or-enter | 9+++++++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/arch.Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/centos-7.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/debian-10.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/debian-8.Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/debian-9.Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/fedora-32.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/fedora-33.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/fedora-34.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/fedora-35.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/gentoo.Dockerfile.in | 5+++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-14.Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-16.Dockerfile.in | 4++++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-18.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-20.10.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-20.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/build/server/test/docker/templates/ubuntu-21.Dockerfile.in | 3+++
Aelpa/pdf-tools-20211110.513/epdfinfo | 0
Aelpa/pdf-tools-20211110.513/pdf-annot.el | 1791+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-annot.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-cache.el | 458+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-cache.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-dev.el | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-dev.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-history.el | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-history.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-info.el | 1744+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-info.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-isearch.el | 832+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-isearch.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-links.el | 379+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-links.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-loader.el | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-loader.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-macs.el | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-macs.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-misc.el | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-misc.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-occur.el | 826+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-occur.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-outline.el | 606+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-outline.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-sync.el | 780+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-sync.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-tools-autoloads.el | 502+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-tools-pkg.el | 14++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-tools.el | 529+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-tools.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-util.el | 1360+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-util.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-view.el | 1684+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-view.elc | 0
Aelpa/pdf-tools-20211110.513/pdf-virtual.el | 1038+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/pdf-tools-20211110.513/pdf-virtual.elc | 0
Aelpa/tablist-20200427.2205/tablist-autoloads.el | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/tablist-20200427.2205/tablist-filter.el | 464+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/tablist-20200427.2205/tablist-filter.elc | 0
Aelpa/tablist-20200427.2205/tablist-pkg.el | 11+++++++++++
Aelpa/tablist-20200427.2205/tablist.el | 1945+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/tablist-20200427.2205/tablist.elc | 0
Minit.el | 2+-
86 files changed, 32073 insertions(+), 1 deletion(-)

diff --git a/elpa/pdf-tools-20211110.513/README b/elpa/pdf-tools-20211110.513/README @@ -0,0 +1,388 @@ +#+TITLE: PDF Tools README +#+AUTHOR: Andreas Politz +#+EMAIL: politza@fh-trier.de +#+Maintainer: Vedang Manerikar +#+Maintainer_Email: vedang.manerikar@gmail.com + +[[https://app.circleci.com/pipelines/github/vedang/pdf-tools][https://circleci.com/gh/vedang/pdf-tools.svg?style=svg]] +[[https://stable.melpa.org/#/pdf-tools][http://stable.melpa.org/packages/pdf-tools-badge.svg]] +[[https://melpa.org/#/pdf-tools][http://melpa.org/packages/pdf-tools-badge.svg]] [[https://ci.appveyor.com/project/vedang/pdf-tools][https://ci.appveyor.com/api/projects/status/yqic2san0wi7o5v8/branch/master?svg=true]] + +** About this package + PDF Tools is, among other things, a replacement of DocView for PDF + files. The key difference is that pages are not pre-rendered by + e.g. ghostscript and stored in the file-system, but rather created + on-demand and stored in memory. + + This rendering is performed by a special library named, for + whatever reason, poppler, running inside a server program. This + program is called ~epdfinfo~ and its job is to successively + read requests from Emacs and produce the proper results, i.e. the + PNG image of a PDF page. + + Actually, displaying PDF files is just one part of PDF Tools. + Since poppler can provide us with all kinds of information about a + document and is also able to modify it, there is a lot more we can + do with it. [[http://www.dailymotion.com/video/x2bc1is_pdf-tools-tourdeforce_tech?forcedQuality%3Dhd720][Watch]] + + Please read also about [[#known-problems][known problems.]] + +** Features + + View :: View PDF documents in a buffer with DocView-like + bindings. + + Isearch :: Interactively search PDF documents like any other + buffer, either for a string or a PCRE. + + Occur :: List lines matching a string or regexp in one or more + PDF documents. + + Follow :: + Click on highlighted links, moving to some part of a different + page, some external file, a website or any other URI. Links may + also be followed by keyboard commands. + + Annotations :: Display and list text and markup annotations (like + underline), edit their contents and attributes + (e.g. color), move them around, delete them or + create new ones and then save the modifications + back to the PDF file. + + Attachments :: Save files attached to the PDF-file or list them + in a dired buffer. + + Outline :: Use imenu or a special buffer to examine and navigate + the PDF's outline. + + SyncTeX :: Jump from a position on a page directly to the TeX + source and vice versa. + + Virtual :: + Use a collection of documents as if it were one, big single PDF. + + + Misc :: + - Display PDF's metadata. + - Mark a region and kill the text from the PDF. + - Keep track of visited pages via a history. + - Apply a color filter for reading in low light conditions. + +** Installation + The package may be installed via MELPA and it will try to build the + server part when it is activated the first time. Though the next + section regarding build-prerequisites is still relevant, the rest + of the installation instructions assume a build from within a git + repository. (The MELPA package has a different directory + structure.) + +*** Server prerequisites + You'll need GNU Emacs \ge 24.3 and some form of a GNU/Linux OS. + Other operating systems are currently not supported (patches + welcome). The following instructions assume a Debian-based + system. (The prerequisites may be installed automatically on this + kind of systems, see [[#compilation][Compilation]] .) + + First make sure a suitable build-system is installed. We need at + least a C/C++ compiler (both ~gcc~ and ~g++~), ~make~, ~automake~ + and ~autoconf~. + + Next we need to install a few libraries PDF Tools depends on, some + of which are probably already on your system. +#+begin_src sh + $ sudo aptitude install libpng-dev zlib1g-dev + $ sudo aptitude install libpoppler-glib-dev + $ sudo aptitude install libpoppler-private-dev +#+end_src + On some older Ubuntu systems, the final command will possibly give + an error. This should be no problem, since in some versions this + package was contained in the main package ~libpoppler-dev~. Also + note, that ~zlib1g-dev~ was for a long time called ~libz-dev~, + which it still may be on your system. + + Debian wheezy comes with libpoppler version 0.18, which is pretty + old. The minimally required version is 0.16, but some features of + PDF Tools depend on a more recent version of this library. See + the following table for what they are and what version they + require. + + | You want to ... | Required version | + |-------------------------------------------+------------------| + | ... create and modify text annotations. | \ge 0.19.4 | + | ... search case-sensitive. | \ge 0.22 | + | ... create and modify markup annotations. | \ge 0.26 | + |-------------------------------------------+------------------| + + In case you decide to install libpoppler from source, make sure + to run its configure script with the ~--enable-xpdf-headers~ + option. + + Finally there is one feature (following links of a PDF document by + plain keystrokes) which requires imagemagick's convert utility. + This requirement is optional and you may install it like so: +#+begin_src sh + $ sudo aptitude install imagemagick +#+end_src +**** Compiling on macOS + Although macOS is not officially supported, it has been reported + to have been successfully compiled. You will need to install + poppler which you can get with Homebrew via +#+BEGIN_SRC sh + $ brew install poppler automake +#+END_SRC + + You will also have to help ~pkg-config~ find some libraries by + setting ~PKG_CONFIG_PATH~, e.g. +#+BEGIN_SRC sh + $ export PKG_CONFIG_PATH=/usr/local/Cellar/zlib/1.2.8/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig +#+END_SRC + or likewise within Emacs using `setenv`. + + After that, compilation should proceed as normal. +**** FreeBSD + Although not officially supported, it has been reported that + pdf-tools work well on FreeBSD. Instead of building pdf-tools, you + can install one of the OS packages with, e.g. +#+BEGIN_SRC sh + $ pkg install pdf-tools-emacs26 +#+END_SRC + To see the current list of pdf-tools packages for FreeBSD visit + [[https://repology.org/metapackages/?search=pdf-tools&inrepo=freebsd][the Repology list]]. + + To build pdf-tools from either MELPA or directly from the source + repository, install the dependencies with +#+BEGIN_SRC sh + $ pkg install autotools gmake poppler-glib +#+END_SRC + + If you choose not to install from MELPA, you must substitute + ~gmake~ for ~make~ in the instructions below. +**** Compiling on CentOS + It is possible to compile pdf-tools on CentOS. Install poppler the dependencies with: +#+BEGIN_SRC sh + $ yum install poppler-devel poppler-glib-devel +#+END_SRC + +**** Compiling on Fedora +#+BEGIN_SRC sh + $ sudo dnf install make automake autoconf gcc gcc-c++ ImageMagick libpng-devel zlib-devel poppler-glib-devel +#+END_SRC + +**** Compiling on Alpine Linux +#+BEGIN_SRC sh + $ apk add build-base g++ gcc automake autoconf libpng-dev glib-dev poppler-dev +#+END_SRC + +**** Compiling on Windows + PDF Tools can be built and used on Windows using the MSYS2 + compiler. This will work with native (not cygwin) Windows builds of + emacs. This includes the standard binaries provided by the GNU + project, those available as MSYS2 packages and numerous third-party + binaries. It has been tested with Emacs 25.1. Instructions are + provided under [[#compilation-and-installation-on-windows][Compilation and installation on Windows]], below. + PDF Tools will successfully compile using Cygwin, but it will not be + able to open PDFs properly due to the way binaries compiled with Cygwin + handle file paths. + +*** Compilation + :PROPERTIES: + :CUSTOM_ID: compilation + :END: + Now it's time to compile the source. +#+begin_src sh + $ cd /path/to/pdf-tools + $ make install-server-deps # optional + $ make -s +#+end_src + The ~make install-server-deps~ command will try to install all + necessary programs and libraries to build the package, though + it'll only work, if ~sudo~ and ~apt-get~ are available. + + This should compile the source code and create a Emacs Lisp + Package in the root directory of the project. The configure script + also tells you at the very end, which features, depending on the + libpoppler version, will be available. These commands should give + no error, otherwise you are in trouble. +**** Compilation and installation on Windows + :PROPERTIES: + :CUSTOM_ID: compilation-and-installation-on-windows + :END: + If using the GNU binaries for Windows, support for PNG and zlib + must first be installed by copying the appropriate dlls into + emacs' ~bin/~ directory. Most third-party binaries come with this + already done. + + First, install [[http://www.msys2.org/][install MSYS2]] and update + the package database and core packages using the instructions + provided. Then, to compile PDF tools itself: + + 1. Open msys2 shell + + 2. Update and install dependencies, skipping any you already have + #+BEGIN_SRC sh + $ pacman -Syu + $ pacman -S base-devel + $ pacman -S mingw-w64-x86_64-toolchain + $ pacman -S mingw-w64-x86_64-zlib + $ pacman -S mingw-w64-x86_64-libpng + $ pacman -S mingw-w64-x86_64-poppler + $ pacman -S mingw-w64-x86_64-imagemagick + #+END_SRC + + 3. Install PDF tools in Emacs, but do not try to compile the + server. Instead, get a separate copy of the source somewhere + else. + #+BEGIN_SRC sh + $ git clone https://github.com/politza/pdf-tools + #+END_SRC + + 4. Open ~mingw64~ shell (*Note:* You must use ~mingw64.exe~ and not ~msys2.exe~) + + 5. Compile pdf-tools + #+BEGIN_SRC sh + $ cd /path/to/pdf-tools + $ make -s + #+END_SRC + + 6. This should produce a file ~server/epdfinfo.exe~. Copy this file + into the ~pdf-tools/~ installation directory in your Emacs. + + 7. Start Emacs and activate the package. + #+BEGIN_SRC + M-x pdf-tools-install RET + #+END_SRC + + 8. Test. + #+BEGIN_SRC + M-x pdf-info-check-epdfinfo RET + #+END_SRC + + If this is successful, ~(pdf-tools-install)~ can be added to Emacs' + config. Note that libraries from other GNU utilities, such as Git + for Windows, may interfere with those needed by PDF Tools. + ~pdf-info-check-epdinfo~ will succeed, but errors occur when trying + to view a PDF file. This can be fixed by ensuring that the MSYS + libraries are always preferred in Emacs: + + #+BEGIN_SRC emacs-lisp + (setenv "PATH" (concat "C:\\msys64\\mingw64\\bin;" (getenv "PATH"))) + #+END_SRC + +*** Elisp prerequisites + This package depends on the following Elisp packages, which should + be installed before installing the PDF Tools package. + + | Package | Required version | + |-----------+----------------------------------| + | [[https://elpa.gnu.org/packages/let-alist.html][let-alist]] | >= 1.0.4 (comes with Emacs 25.2) | + | [[http://melpa.org/#/tablist][tablist]] | >= 0.70 | + |-----------+----------------------------------| + +*** Installing + If ~make~ produced the ELP file ~pdf-tools-${VERSION}.tar~ you are + fine. This package contains all the necessary files for Emacs + and may be installed by either using +#+begin_src sh + $ make install-package +#+end_src + or executing the Emacs command +#+begin_src elisp + M-x package-install-file RET pdf-tools-${VERSION}.tar RET +#+end_src + + To complete the installation process, you need to activate the + package by putting +#+begin_src elisp + (pdf-tools-install) +#+end_src + somewhere in your ~.emacs~. Alternatively, and if you care about + start-up time, you may want to use +#+begin_src elisp + (pdf-loader-install) +#+end_src + instead. Next you probably want to take a look at the various + features of what you've just installed. The following two commands + might be of help for doing so. +#+begin_src elisp + M-x pdf-tools-help RET + M-x pdf-tools-customize RET +#+end_src + +*** Updating + Some day you might want to update this package via ~git pull~ and + then reinstall it. Sometimes this may fail, especially if + Lisp-Macros are involved and the version hasn't changed. To avoid + this kind of problems, you should delete the old package via + ~list-packages~, restart Emacs and then reinstall the package. + + This also applies when updating via package and MELPA. + +** Known problems + :PROPERTIES: + :CUSTOM_ID: known-problems + :END: + +*** linum-mode + PDF Tools does not work well together with ~linum-mode~ and + activating it in a ~pdf-view-mode~, e.g. via ~global-linum-mode~, + might make Emacs choke. + +*** auto-revert + Autorevert works by polling the file-system every + ~auto-revert-interval~ seconds, optionally combined with some + event-based reverting via [[https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Notifications.html][file notification]]. But this currently + does not work reliably, such that Emacs may revert the PDF-buffer + while the corresponding file is still being written to (e.g. by + LaTeX), leading to a potential error. + + With a recent [[https://www.gnu.org/software/auctex/][AUCTeX]] installation, you might want to put the + following somewhere in your dotemacs, which will revert the PDF-buffer + *after* the TeX compilation has finished. +#+BEGIN_SRC emacs-lisp + (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer) +#+END_SRC + +*** sublimity + L/R scrolling breaks while zoomed into a pdf, with usage of sublimity smooth scrolling features + +** Some keybindings + +| Navigation | | +|--------------------------------------------+-----------------------| +| Scroll Up / Down by Page-full | ~space~ / ~backspace~ | +| Scroll Up / Down by Line | ~C-n~ / ~C-p~ | +| Scroll Right / Left | ~C-f~ / ~C-b~ | +| Top of Page / Bottom of Page | ~<~ / ~>~ | +| Next Page / Previous Page | ~n~ / ~p~ | +| First Page / Last Page | ~M-<~ / ~M->~ | +| Incremental Search Forward / Backward | ~C-s~ / ~C-r~ | +| Occur (list all lines containing a phrase) | ~M-s o~ | +| Jump to Occur Line | ~RETURN~ | +| Pick a Link and Jump | ~F~ | +| Incremental Search in Links | ~f~ | +| History Back / Forwards | ~B~ / ~N~ | +| Display Outline | ~o~ | +| Jump to Section from Outline | ~RETURN~ | +| Jump to Page | ~M-g g~ | + +| Display | | +|------------------------------------------+-----------------| +| Zoom in / Zoom out | ~+~ / ~-~ | +| Fit Height / Fit Width / Fit Page | ~H~ / ~W~ / ~P~ | +| Trim Margins (set slice to bounding box) | ~s b~ | +| Reset Margins | ~s r~ | +| Reset Zoom | 0 | + +| Annotations | | +|-------------------------------+-------------------------------------------------| +| List Annotations | ~C-c C-a l~ | +| Jump to Annotations from List | ~SPACE~ | +| Mark Annotation for Deletion | ~d~ | +| Delete Marked Annotations | ~x~ | +| Unmark Annotations | ~u~ | +| Close Annotation List | ~q~ | +| Add and Edit Annotations | via Mouse selection and left-click context menu | + +| Syncing with AUCTeX | | +|----------------------------------+-------------| +| Jump to PDF Location from Source | ~C-c C-g~ | +| Jump Source Location from PDF | ~C-mouse-1~ | + +| Miscellaneous | | +|-----------------------------------------------+-----------| +| Refresh File (e.g., after recompiling source) | ~g~ | +| Print File | ~C-c C-p~ | + +# Local Variables: +# mode: org +# End: diff --git a/elpa/pdf-tools-20211110.513/build/Makefile b/elpa/pdf-tools-20211110.513/build/Makefile @@ -0,0 +1,97 @@ +CASK = cask +EMACS ?= emacs +# Handle the mess when inside Emacs. +unexport INSIDE_EMACS #cask not like this. +ifeq ($(EMACS), t) +EMACS = emacs +endif + +emacs = $(EMACS) +emacs_version = $(shell $(emacs) --batch --eval \ + '(princ (format "%s.%s" emacs-major-version emacs-minor-version))') +$(info Using Emacs $(emacs_version)) + +version=$(shell sed -ne 's/^;\+ *Version: *\([0-9.]\)/\1/p' lisp/pdf-tools.el) +pkgname=pdf-tools-$(version) +pkgfile=$(pkgname).tar + +.PHONY: all clean distclean bytecompile test check melpa + +all: $(pkgfile) + +# Create a elpa package including the server +$(pkgfile): .cask/$(emacs_version) server/epdfinfo lisp/*.el + $(CASK) package . + +# Compile the Lisp sources +bytecompile: .cask/$(emacs_version) + $(CASK) exec $(emacs) --batch -L lisp -f batch-byte-compile lisp/*.el + +# Run ERT tests +test: all + PACKAGE_TAR=$(pkgfile) $(CASK) exec ert-runner + +check: test + +# Run the autobuild script tests in docker +test-autobuild: server-test + +# Run all tests +test-all: test test-autobuild + +# Init cask +.cask/$(emacs_version): + $(CASK) install + +# Run the autobuild script (installing depends and compiling) +autobuild: + cd server && ./autobuild + +# Soon to be obsolete targets +melpa-build: autobuild + cp build/epdfinfo . +install-server-deps: ; + +# Create a package like melpa would. +melpa-package: $(pkgfile) + cp $(pkgfile) $(pkgname)-melpa.tar + tar -u --transform='s/server/$(pkgname)\/build\/server/' \ + -f $(pkgname)-melpa.tar \ + $$(git ls-files server) + tar -u --transform='s/Makefile/$(pkgname)\/build\/Makefile/' \ + -f $(pkgname)-melpa.tar \ + Makefile + tar -u --transform='s/README\.org/$(pkgname)\/README/' \ + -f $(pkgname)-melpa.tar \ + README.org + -tar --delete $(pkgname)/epdfinfo \ + -f $(pkgname)-melpa.tar + +# Various clean targets +clean: server-clean + rm -f -- $(pkgfile) + rm -f -- lisp/*.elc + rm -f -- pdf-tools-readme.txt + rm -f -- pdf-tools-$(version).entry + +distclean: clean server-distclean + rm -rf -- .cask + +# Server targets +server/epdfinfo: server/Makefile server/*.[ch] + $(MAKE) -C server + +server/Makefile: server/configure + cd server && ./configure -q + +server/configure: server/configure.ac + cd server && ./autogen.sh + +server-test: server/Makefile + $(MAKE) -C server check + +server-clean: + ! [ -f server/Makefile ] || $(MAKE) -C server clean + +server-distclean: + ! [ -f server/Makefile ] || $(MAKE) -C server distclean diff --git a/elpa/pdf-tools-20211110.513/build/server/.gitignore b/elpa/pdf-tools-20211110.513/build/server/.gitignore @@ -0,0 +1,25 @@ +*.o +.deps/ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +config.h +config.h.in +config.log +config.status +configure +depcomp +epdfinfo +install-sh +libsynctex.a +missing +stamp-h1 +ar-lib +compile +config.h.in~ +.clang_complete +callgrind.out.* +config.guess +config.sub +TAGS diff --git a/elpa/pdf-tools-20211110.513/build/server/Makefile.am b/elpa/pdf-tools-20211110.513/build/server/Makefile.am @@ -0,0 +1,44 @@ +bin_PROGRAMS = epdfinfo +epdfinfo_CFLAGS = -Wall $(glib_CFLAGS) $(poppler_glib_CFLAGS) $(poppler_CFLAGS) \ + $(png_CFLAGS) +epdfinfo_CXXFLAGS = -Wall $(epdfinfo_CFLAGS) +epdfinfo_LDADD = $(glib_LIBS) $(poppler_glib_LIBS) $(poppler_LIBS) \ + $(png_LIBS) libsynctex.a $(zlib_LIBS) +epdfinfo_SOURCES = epdfinfo.c epdfinfo.h poppler-hack.cc + +noinst_LIBRARIES = libsynctex.a +libsynctex_a_SOURCES = synctex_parser.c synctex_parser_utils.c synctex_parser.h \ + synctex_parser_local.h synctex_parser_utils.h +libsynctex_a_CFLAGS = -w $(zlib_CFLAGS) -DSYNCTEX_USE_LOCAL_HEADER + +if HAVE_W32 +epdfinfo_LDADD += -lshlwapi +endif + +SYNCTEX_UPSTREAM = svn://tug.org/texlive/trunk/Build/source/texk/web2c/synctexdir +SYNCTEX_FILES = synctex_parser.c \ + synctex_parser.h \ + synctex_parser_readme.txt \ + synctex_parser_utils.c \ + synctex_parser_utils.h \ + synctex_parser_version.txt \ + synctex_version.h \ + synctex_parser_advanced.h + + +check-local: + @if $(MAKE) --version 2>&1 | grep -q GNU; then \ + cd test && $(MAKE) $(AM_MAKEFLAGS); \ + else \ + echo "Skipping tests in server/test (requires GNU make)"; \ + fi + +synctex-pull: + @if [ -n "$$(git status --porcelain)" ]; then \ + git status; \ + echo "Not checking-out files into a dirty work-directory"; \ + false; \ + fi + for file in $(SYNCTEX_FILES); do \ + svn export --force $(SYNCTEX_UPSTREAM)/$$file; \ + done diff --git a/elpa/pdf-tools-20211110.513/build/server/autobuild b/elpa/pdf-tools-20211110.513/build/server/autobuild @@ -0,0 +1,534 @@ +#!/usr/bin/env sh + +## +## Installs package dependencies and builds the application. +## + +# Don't exit if some command fails. +set +e +# Disable file globbing. +set -f + +# Boolean variables are true if non-empty and false otherwise. + +# Command to install packages. +PKGCMD= +# Args to pass to $PKGCMD. +PKGARGS= +# Required packages. +PACKAGES= +# Whether package installation requires root permissions. +PKG_INSTALL_AS_ROOT=true +# Whether to skip package installation altogether. +PKG_INSTALL_SKIP= +# Whether to force package installation, even if it does not seem +# necessary. +PKG_INSTALL_FORCE= +# Only test if the OS is handled by this script. +DRY_RUN= +# If and where to install the program. +INSTALL_DIR= +# Whether we can install packages. +OS_IS_HANDLED=true +# Which OSs installer to use +OS= + +## +-----------------------------------------------------------+ +## * Utility Functions +## +-----------------------------------------------------------+ + +usage() +{ + cat <<EOF +usage:$(basename "$0") [--help|-n|-i DIR|[-d -D]|[--os OS]] + + -n Don't do anything, but check if this OS is handled. + + -i DIR Install the program in the given directory. + + -d Force dependency installattion. + + -D Skip dependency installattion. + + --os OS Use the given OS's installer + + --help Display this message. + +EOF + exit "$1" +} + +# Search for command $1 in PATH. Print its absolute filename. +which() +{ + if [ -z "$1" ]; then + return 1 + fi + command -v "$1" +} + +# Quote $@ for the shell. +quote() +{ + quoted= + for arg; do + qarg=$(printf "%s" "$arg" | sed -e 's/[|&;<>()$\`"'\'' ]/\\&/g') + if [ -z "$quoted" ]; then + quoted=$qarg + else + quoted="$quoted $qarg" + fi + done + printf "%s" "$quoted" +} + +# Attempt to exec $@ as root. +exec_privileged() { + if [ -z "$1" ]; then + echo "internal error: command is empty" + exit 2 + fi + if [ -w / ]; then + "$@" + elif which sudo >/dev/null 2>&1; then + sudo -- "$@" + retval=$? + sudo -k + return $retval + elif which su >/dev/null 2>&1; then + su -c "$(quote "$@")" + else + echo "No such program: sudo or su" + exit 1 + fi +} + +# Test if $1 is in PATH or exit with a failure status. +assert_program() +{ + if ! which "$1" >/dev/null 2>&1; then + echo "No such program: $1" + exit 1 + fi +} + +# Source filename $1 and echo variable $2. +source_var() +{ + if ! [ -f "$1" ] || ! [ -r "$1" ] || [ -z "$2" ]; then + return 1 + fi + # shellcheck source=/dev/null + . "$1" + eval "printf '%s\n' \$$2" + return 0 +} + +exit_success() +{ + echo "===========================" + echo " Build succeeded. :O) " + echo "===========================" + exit 0 +} + +exit_fail() +{ + echo "===========================" + echo " Build failed. ;o( " + echo "===========================" + if [ -z "$PKG_INSTALL_FORCE" ]; then + echo "Note: maybe try the '-d' option." + fi + exit 1 +} + +# Return 0, if all required packages seem to be installed. +have_packages_installed() +{ + { + which pkg-config || return 1 + if ! [ -f configure ];then + which autoreconf || return 1 + which automake || return 1 + fi + for lib in libpng glib-2.0 poppler poppler-glib zlib; do + pkg-config --exists $lib || return 1 + done + which make || return 1 + which gcc || which cc || return 1 + which g++ || which c++ || return 1 + c++ $(pkg-config --cflags poppler) -o /dev/null -E install_test.cpp 2>/dev/null + [ $? -eq 0 ] || return 1 + return 0 + } >/dev/null 2>&1 +} + +handle_options() +{ + while [ $# -gt 0 ]; do + case $1 in + --help) usage 0;; + -n) DRY_RUN=true;; + -d) PKG_INSTALL_FORCE=true ;; + -D) PKG_INSTALL_SKIP=true ;; + -i) + shift + [ $# -gt 0 ] || usage 1 + if [ "${1%%/}" != "${PWD%%/}" ]; then + INSTALL_DIR=$1 + fi ;; + --os) + shift + [ $# -gt 0 ] || usage 1 + OS="$1" + ;; + *) usage 1 ;; + esac + shift + done + if [ -n "$PKG_INSTALL_SKIP" ] && [ -n "$PKG_INSTALL_FORCE" ]; then + usage 1 + fi +} + +## +-----------------------------------------------------------+ +## * OS Functions +## +-----------------------------------------------------------+ + +# Archlinux +os_arch() { + if ! [ -e "/etc/arch-release" ]; then + return 1; + fi + PKGCMD=pacman + PKGARGS="-S --needed" + PACKAGES="base-devel libpng zlib poppler-glib" + return 0; +} + +# CentOS +os_centos() { + if ! [ -e "/etc/centos-release" ]; then + return 1 + fi + PKGCMD=yum + if yum help install-n >/dev/null 2>&1; then + PKGARGS=install-n + else + PKGARGS=install + fi + PACKAGES="autoconf + automake + gcc + gcc-c++ + libpng-devel + make + pkgconfig + poppler-devel + poppler-glib-devel + zlib-devel" + return 0 +} + +# FreeBSD +os_freebsd() { + if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "FreeBSD" ]; then + return 1 + fi + PKGCMD=pkg + PKGARGS=install + PACKAGES="autotools poppler-glib png pkgconf" + return 0 +} + +# OpenBSD +os_openbsd() { + if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "OpenBSD" ]; then + return 1 + fi + PKGCMD=pkg_add + PKGARGS="-uU" + PACKAGES="autoconf-2.69p2 automake-1.15.1 poppler poppler-utils png" + export AUTOCONF_VERSION=2.69 + export AUTOMAKE_VERSION=1.15 + if whereis clang++ ;then + export CXX=clang++ + elif whereis eg++ ;then + export CXX=eg++ + else + export CXX=eg++ + PACKAGES="${PACKAGES} g++" + fi + export CXXFLAGS="-std=c++11 -I/usr/local/include/poppler -I/usr/local/include" + return 0 +} + +# Fedora +os_fedora() { + if ! [ -e "/etc/fedora-release" ]; then + return 1 + fi + PKGCMD=dnf + PKGARGS=install + PACKAGES="autoconf + automake + gcc + gcc-c++ + libpng-devel + make + poppler-devel + poppler-glib-devel + zlib-devel" + VERSION=$(source_var /etc/os-release VERSION_ID) + if [ -n "$VERSION" ] && [ "$VERSION" -ge 26 ]; then + PACKAGES="$PACKAGES pkgconf" + else + PACKAGES="$PACKAGES pkgconfig" + fi + return 0 +} + +# Debian/Ubuntu +os_debian() { + if ! [ -e "/etc/debian_version" ]; then + return 1 + fi + PACKAGES="autoconf + automake + g++ + gcc + libpng-dev + libpoppler-dev + libpoppler-glib-dev + libpoppler-private-dev + libz-dev + make + pkg-config" + PKGCMD=apt-get + PKGARGS="install -y" + return 0 +} + +# Msys2 +os_msys2() { + if [ -z "$MSYSTEM" ] || ! [ -r "/etc/profile" ]; then + return 1 + fi + case $MSYSTEM in + MINGW64) + PACKAGES="base-devel + mingw-w64-x86_64-libpng + mingw-w64-x86_64-poppler + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-zlib" ;; + MINGW32) + PACKAGES="base-devel + mingw-w64-i686-libpng + mingw-w64-i686-poppler + mingw-w64-i686-toolchain + mingw-w64-i686-zlib" ;; + MSYS) + case $(uname -m) in + x86_64) + MSYSTEM=MINGW64 ;; + *) + MSYSTEM=MINGW32 ;; + esac + export MSYSTEM + # shellcheck source=/dev/null + . /etc/profile + eval "exec $(quote "$0" "$@")" ;; + *) + echo "Unrecognized MSYSTEM value: $MSYSTEM" + exit 1 ;; + esac + PKGCMD=pacman + PKGARGS="-S --needed" + PKG_INSTALL_AS_ROOT= + return 0 +} + +# MacOS +os_macos() { + if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "Darwin" ]; then + return 1 + elif which brew >/dev/null 2>&1; then + PKGCMD=brew + PKGARGS=install + PACKAGES="pkg-config poppler automake" + PKG_INSTALL_AS_ROOT= + # homebrew install libffi as keg-only, meaning we need to set + # PKG_CONFIG_PATH manually so configure can find it + export PKG_CONFIG_PATH="$(brew --prefix libffi)/lib/pkgconfig/" + elif which port >/dev/null 2>&1; then + PKGCMD=port + PKGARGS=install + PACKAGES="pkgconfig poppler automake libpng" + else + return 1 + fi + return 0 +} + +# NixOS +os_nixos() { + # Already in the nix-shell. + if [ -n "$AUTOBUILD_NIX_SHELL" ]; then + return 0 + fi + if ! which nix-shell >/dev/null 2>&1; then + return 1 + fi + if [ -n "$DRY_RUN" ]; then + return 0 + fi + command="AUTOBUILD_NIX_SHELL=true" + command="$command;export AUTOBUILD_NIX_SHELL" + command="$command;$(quote "$0" "$@")" + exec nix-shell --pure --command "$command" \ + -p gcc gnumake automake autoconf pkgconfig libpng zlib poppler +} + +# Gentoo +os_gentoo() { + if ! [ -e "/etc/gentoo-release" ]; then + return 1 + fi + PKGCMD=emerge + PKGARGS=--noreplace + PACKAGES="app-text/poppler + dev-util/pkgconfig + media-libs/libpng + sys-devel/autoconf + sys-devel/automake + sys-devel/gcc + sys-devel/make + sys-libs/zlib" + return 0 +} + +# By Parameter --os +os_argument() { + [ -z "$OS" ] && return 1 + case $OS in + macos) os_macos "$@";; + freebsd) os_freebsd "$@";; + arch) os_arch "$@";; + centos) os_centos "$@";; + openbsd) os_openbsd "$@";; + fedora) os_fedora "$@";; + debian) os_debian "$@";; + gentoo) os_gentoo "$@";; + msys2) os_msys2 "$@";; + nixos) os_nixos "$@";; + *) echo "Invalid --os argument: $OS" + exit 1 + esac || { + echo "Unable to install on this system as $OS" + exit 1 + } +} + +## +-----------------------------------------------------------+ +## * Figure out were we are, install deps and build the program +## +-----------------------------------------------------------+ + +handle_options "$@" + +os_argument "$@" || \ +os_macos "$@" || \ +os_freebsd "$@" || \ +os_arch "$@" || \ +os_centos "$@" || \ +os_openbsd "$@" || \ +os_fedora "$@" || \ +os_debian "$@" || \ +os_gentoo "$@" || \ +os_msys2 "$@" || \ +os_nixos "$@" || \ +{ + OS_IS_HANDLED= + if [ -z "$DRY_RUN" ]; then + echo "Failed to recognize this system, trying to continue." + fi +} + +if [ -n "$DRY_RUN" ]; then + [ -n "$OS_IS_HANDLED" ] + exit $? +fi + +if [ -n "$PKGCMD" ];then + echo "---------------------------" + echo " Installing packages " + echo "---------------------------" + if [ -n "$PKG_INSTALL_SKIP" ]; then + echo "Skipping package installation (as requested)" + elif [ -z "$PKG_INSTALL_FORCE" ] && have_packages_installed; then + echo "Skipping package installation (already installed)" + else + assert_program "$PKGCMD" + echo "$PKGCMD $PKGARGS $PACKAGES" + if [ -n "$PKG_INSTALL_AS_ROOT" ]; then + exec_privileged $PKGCMD $PKGARGS $PACKAGES + else + $PKGCMD $PKGARGS $PACKAGES + fi + fi + echo +fi + +# Try to be in the correct directory. +if which dirname >/dev/null 2>&1; then + cd "$(dirname "$0")" || { + echo "Failed to change into the source directory" + exit 1 + } +fi + +echo "---------------------------" +echo " Configuring and compiling " +echo "---------------------------" + +# Create the configure script. +if ! [ -f ./configure ]; then + assert_program autoreconf + echo "autoreconf -i" + autoreconf -i + [ -f ./configure ] || exit_fail +fi + +# Build the program. +if [ -n "$INSTALL_DIR" ]; then + prefix=--bindir=$INSTALL_DIR +fi + +echo "./configure -q $prefix && make clean && make -s" +eval "./configure -q $(quote "$prefix") && make clean && make -s || exit_fail" +echo +if [ -n "$INSTALL_DIR" ]; then + echo "---------------------------" + echo " Installing " + echo "---------------------------" + echo make -s install + if mkdir -p -- "$INSTALL_DIR" && [ -w "$INSTALL_DIR" ]; then + make install || exit_fail + else + exec_privileged make install || exit_fail + fi + # Copy dynamic libraries on windows. + if [ -f epdfinfo.exe ]; then + assert_program awk + assert_program ldd + for dll in $(ldd epdfinfo.exe | awk '/\/mingw/ {print $3}'); do + cp -u "$dll" "$INSTALL_DIR" + done + fi + echo +fi +exit_success + +# Local Variables: +# compile-command: "shellcheck autobuild" +# End: diff --git a/elpa/pdf-tools-20211110.513/build/server/autogen.sh b/elpa/pdf-tools-20211110.513/build/server/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Running autoreconf..." + +autoreconf -i diff --git a/elpa/pdf-tools-20211110.513/build/server/configure.ac b/elpa/pdf-tools-20211110.513/build/server/configure.ac @@ -0,0 +1,113 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.67]) +AC_INIT([epdfinfo], 1.0, [politza@fh-trier.de]) +AM_INIT_AUTOMAKE([-Wall -Wno-override foreign silent-rules]) +AC_CONFIG_SRCDIR([epdfinfo.h]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_RANLIB +AM_PROG_AR + +# Checks for libraries. +HAVE_POPPLER_FIND_OPTS="no (requires poppler-glib >= 0.22)" +HAVE_POPPLER_ANNOT_WRITE="no (requires poppler-glib >= 0.19.4)" +HAVE_POPPLER_ANNOT_MARKUP="no (requires poppler-glib >= 0.26)" + +PKG_CHECK_MODULES([png], [libpng]) +PKG_CHECK_MODULES([glib], [glib-2.0]) +PKG_CHECK_MODULES([poppler], [poppler]) +PKG_CHECK_MODULES([poppler_glib], [poppler-glib >= 0.16.0]) +PKG_CHECK_EXISTS([poppler-glib >= 0.19.4], [HAVE_POPPLER_ANNOT_WRITE=yes]) +PKG_CHECK_EXISTS([poppler-glib >= 0.22], [HAVE_POPPLER_FIND_OPTS=yes]) +PKG_CHECK_EXISTS([poppler-glib >= 0.26], [HAVE_POPPLER_ANNOT_MARKUP=yes]) +PKG_CHECK_MODULES([zlib], [zlib]) + +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #ifndef _WIN32 + error + #endif + ]])], [have_w32=true], [have_w32=false]) +AM_CONDITIONAL(HAVE_W32, [test "$have_w32" = true]) + +if test "$have_w32" = true; then + if test "$MSYSTEM" = MINGW32 -o "$MSYSTEM" = MINGW64; then + # glib won't work properly on msys2 without it. + CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CFLAGS" + fi +fi + +SAVED_CPPFLAGS=$CPPFLAGS +CPPFLAGS=$poppler_CFLAGS +AC_LANG_PUSH([C++]) +# Check if we can use the -std=c++11 option. +m4_include([m4/ax_check_compile_flag.m4]) +AX_CHECK_COMPILE_FLAG([-std=c++11], [HAVE_STD_CXX11=yes]) + +if test "$HAVE_STD_CXX11" = yes; then + CXXFLAGS="-std=c++11 $CXXFLAGS" +fi +# Check for private poppler header. +AC_CHECK_HEADERS([Annot.h PDFDocEncoding.h], [], + AC_MSG_ERROR([cannot find necessary poppler-private header (see README.org)])) +AC_LANG_POP([C++]) +CPPFLAGS=$SAVED_CPPFLAGS + +# Setup compile time features. +if test "$HAVE_POPPLER_FIND_OPTS" = yes; then + AC_DEFINE([HAVE_POPPLER_FIND_OPTS],1, + [Define to 1 to enable case sensitive searching (requires poppler-glib >= 0.22).]) +fi + +if test "$HAVE_POPPLER_ANNOT_WRITE" = yes; then + AC_DEFINE([HAVE_POPPLER_ANNOT_WRITE],1, + [Define to 1 to enable writing of annotations (requires poppler-glib >= 0.19.4).]) +fi + +if test "$HAVE_POPPLER_ANNOT_MARKUP" = yes; then + AC_DEFINE([HAVE_POPPLER_ANNOT_MARKUP],1, + [Define to 1 to enable adding of markup annotations (requires poppler-glib >= 0.26).]) +fi + +AC_CANONICAL_HOST +# Checks for header files. +AC_CHECK_HEADERS([stdlib.h string.h strings.h err.h]) + +AC_MSG_CHECKING([for error.h]) +SAVED_CFLAGS=$CFLAGS +CFLAGS="$poppler_CFLAGS $poppler_glib_CFLAGS" +AC_LANG_PUSH([C]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ + #include <error.h> + ],[error (0, 0, "");])], + [AC_DEFINE([HAVE_ERROR_H],1, [Define to 1 if error.h is usable.]) + AC_MSG_RESULT([yes])], + AC_MSG_RESULT([no])) +AC_LANG_POP([C]) +CFLAGS=$SAVED_CFLAGS + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_CHECK_TYPES([ptrdiff_t]) +AC_C_BIGENDIAN + +# Checks for library functions. +AC_FUNC_ERROR_AT_LINE +AC_FUNC_STRTOD +AC_CHECK_FUNCS([strcspn strtol getline]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT + +echo +echo "Is case-sensitive searching enabled ? ${HAVE_POPPLER_FIND_OPTS}" +echo "Is modifying text annotations enabled ? ${HAVE_POPPLER_ANNOT_WRITE}" +echo "Is modifying markup annotations enabled ? ${HAVE_POPPLER_ANNOT_MARKUP}" +echo diff --git a/elpa/pdf-tools-20211110.513/build/server/epdfinfo.c b/elpa/pdf-tools-20211110.513/build/server/epdfinfo.c @@ -0,0 +1,3718 @@ +/* Copyright (C) 2013, 2014 Andreas Politz + * + * Author: Andreas Politz <politza@fh-trier.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <assert.h> +#ifdef HAVE_ERR_H +# include <err.h> +#endif +#ifdef HAVE_ERROR_H +# include <error.h> +#endif +#include <glib.h> +#include <poppler.h> +#include <cairo.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <png.h> +#include <math.h> +#include <regex.h> +#include "synctex_parser.h" +#include "epdfinfo.h" + + +/* ================================================================== * + * Helper Functions + * ================================================================== */ + +#ifndef HAVE_ERR_H +/** + * Print error message and quit. + * + * @param eval Return code + * @param fmt Formatting string + */ +static void +err(int eval, const char *fmt, ...) +{ + va_list args; + + fprintf (stderr, "epdfinfo: "); + if (fmt != NULL) + { + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, ": %s\n", strerror(errno)); + } + else + { + fprintf (stderr, "\n"); + } + + fflush (stderr); + exit (eval); +} +#endif + +#ifndef HAVE_GETLINE +/** + * Read one line from a file. + * + * @param lineptr Pointer to malloc() allocated buffer + * @param n Pointer to size of buffer + * @param stream File pointer to read from + */ +static ssize_t +getline(char **lineptr, size_t *n, FILE *stream) +{ + size_t len = 0; + int ch; + + if ((lineptr == NULL) || (n == NULL)) + { + errno = EINVAL; + return -1; + } + + if (*lineptr == NULL) + { + *lineptr = malloc (128); + *n = 128; + } + + while ((ch = fgetc (stream)) != EOF) + { + (*lineptr)[len] = ch; + + if (++len >= *n) + { + *n += 128; + *lineptr = realloc (*lineptr, *n); + } + + if (ch == '\n') + break; + } + (*lineptr)[len] = '\0'; + + if (!len) + { + len = -1; + } + + return len; +} +#endif + +/** + * Free a list of command arguments. + * + * @param args An array of command arguments. + * @param n The length of the array. + */ +static void +free_command_args (command_arg_t *args, size_t n) +{ + if (! args) + return; + + g_free (args); +} + +/** + * Free resources held by document. + * + * @param doc The document to be freed. + */ +static void +free_document (document_t *doc) +{ + if (! doc) + return; + + g_free (doc->filename); + g_free (doc->passwd); + if (doc->annotations.pages) + { + int npages = poppler_document_get_n_pages (doc->pdf); + int i; + for (i = 0; i < npages; ++i) + { + GList *item; + GList *annots = doc->annotations.pages[i]; + for (item = annots; item; item = item->next) + { + annotation_t *a = (annotation_t*) item->data; + poppler_annot_mapping_free(a->amap); + g_free (a->key); + g_free (a); + } + g_list_free (annots); + } + g_hash_table_destroy (doc->annotations.keys); + g_free (doc->annotations.pages); + } + g_object_unref (doc->pdf); + g_free (doc); +} + +/** + * Parse a list of whitespace separated double values. + * + * @param str The input string. + * @param values[out] Values are put here. + * @param nvalues How many values to parse. + * + * @return TRUE, if str contained exactly nvalues, else FALSE. + */ +static gboolean +parse_double_list (const char *str, gdouble *values, size_t nvalues) +{ + char *end; + int i; + + if (! str) + return FALSE; + + errno = 0; + for (i = 0; i < nvalues; ++i) + { + gdouble n = g_ascii_strtod (str, &end); + + if (str == end || errno) + return FALSE; + + values[i] = n; + str = end; + } + + if (*end) + return FALSE; + + return TRUE; +} + +static gboolean +parse_rectangle (const char *str, PopplerRectangle *r) +{ + gdouble values[4]; + + if (! r) + return FALSE; + + if (! parse_double_list (str, values, 4)) + return FALSE; + + r->x1 = values[0]; + r->y1 = values[1]; + r->x2 = values[2]; + r->y2 = values[3]; + + return TRUE; +} + +static gboolean +parse_edges_or_position (const char *str, PopplerRectangle *r) +{ + return (parse_rectangle (str, r) + && r->x1 >= 0 && r->x1 <= 1 + && r->x2 <= 1 + && r->y1 >= 0 && r->y1 <= 1 + && r->y2 <= 1); +} + +static gboolean +parse_edges (const char *str, PopplerRectangle *r) +{ + return (parse_rectangle (str, r) + && r->x1 >= 0 && r->x1 <= 1 + && r->x2 >= 0 && r->x2 <= 1 + && r->y1 >= 0 && r->y1 <= 1 + && r->y2 >= 0 && r->y2 <= 1); +} + +/** + * Print a string properly escaped for a response. + * + * @param str The string to be printed. + * @param suffix_char Append a newline if NEWLINE, a colon if COLON. + */ +static void +print_response_string (const char *str, enum suffix_char suffix) +{ + if (str) + { + while (*str) + { + switch (*str) + { + case '\n': + printf ("\\n"); + break; + case '\\': + printf ("\\\\"); + break; + case ':': + printf ("\\:"); + break; + default: + putchar (*str); + } + ++str; + } + } + + switch (suffix) + { + case NEWLINE: + putchar ('\n'); + break; + case COLON: + putchar (':'); + break; + default: ; + } +} + + +/** + * Print a formatted error response. + * + * @param fmt The printf-like format string. + */ +static void +printf_error_response (const char *fmt, ...) +{ + va_list va; + puts ("ERR"); + va_start (va, fmt); + vprintf (fmt, va); + va_end (va); + puts ("\n."); + fflush (stdout); +} + +/** + * Remove one trailing newline character. Does nothing, if str does + * not end with a newline. + * + * @param str The string. + * + * @return str with trailing newline removed. + */ +static char* +strchomp (char *str) +{ + size_t length; + + if (! str) + return str; + + length = strlen (str); + if (str[length - 1] == '\n') + str[length - 1] = '\0'; + + return str; +} + +/** + * Create a new, temporary file and returns it's name. + * + * @return The filename. + */ +static char* +mktempfile() +{ + char *filename = NULL; + int tries = 3; + while (! filename && tries-- > 0) + { + + filename = tempnam(NULL, "epdfinfo"); + if (filename) + { + int fd = open(filename, O_CREAT | O_EXCL | O_RDONLY, S_IRWXU); + if (fd > 0) + close (fd); + else + { + free (filename); + filename = NULL; + } + } + } + if (! filename) + fprintf (stderr, "Unable to create tempfile"); + + return filename; +} + +static void +image_recolor (cairo_surface_t * surface, const PopplerColor * fg, + const PopplerColor * bg) +{ + /* uses a representation of a rgb color as follows: + - a lightness scalar (between 0,1), which is a weighted average of r, g, b, + - a hue vector, which indicates a radian direction from the grey axis, + inside the equal lightness plane. + - a saturation scalar between 0,1. It is 0 when grey, 1 when the color is + in the boundary of the rgb cube. + */ + + const unsigned int page_width = cairo_image_surface_get_width (surface); + const unsigned int page_height = cairo_image_surface_get_height (surface); + const int rowstride = cairo_image_surface_get_stride (surface); + unsigned char *image = cairo_image_surface_get_data (surface); + + /* RGB weights for computing lightness. Must sum to one */ + static const double a[] = { 0.30, 0.59, 0.11 }; + + const double f = 65535.; + const double rgb_fg[] = { + fg->red / f, fg->green / f, fg->blue / f + }; + const double rgb_bg[] = { + bg->red / f, bg->green / f, bg->blue / f + }; + + const double rgb_diff[] = { + rgb_bg[0] - rgb_fg[0], + rgb_bg[1] - rgb_fg[1], + rgb_bg[2] - rgb_fg[2] + }; + + unsigned int y; + for (y = 0; y < page_height * rowstride; y += rowstride) + { + unsigned char *data = image + y; + + unsigned int x; + for (x = 0; x < page_width; x++, data += 4) + { + /* Careful. data color components blue, green, red. */ + const double rgb[3] = { + (double) data[2] / 256., + (double) data[1] / 256., + (double) data[0] / 256. + }; + + /* compute h, s, l data */ + double l = a[0] * rgb[0] + a[1] * rgb[1] + a[2] * rgb[2]; + + /* linear interpolation between dark and light with color ligtness as + * a parameter */ + data[2] = + (unsigned char) round (255. * (l * rgb_diff[0] + rgb_fg[0])); + data[1] = + (unsigned char) round (255. * (l * rgb_diff[1] + rgb_fg[1])); + data[0] = + (unsigned char) round (255. * (l * rgb_diff[2] + rgb_fg[2])); + } + } +} + +/** + * Render a PDF page. + * + * @param pdf The PDF document. + * @param page The page to be rendered. + * @param width The desired width of the image. + * + * @return A cairo_t context encapsulating the rendered image, or + * NULL, if rendering failed for some reason. + */ +static cairo_surface_t* +image_render_page(PopplerDocument *pdf, PopplerPage *page, + int width, gboolean do_render_annotaions, + const render_options_t *options) +{ + cairo_t *cr = NULL; + cairo_surface_t *surface = NULL; + double pt_width, pt_height; + int height; + double scale = 1; + + if (! page || ! pdf) + return NULL; + + if (width < 1) + width = 1; + + poppler_page_get_size (page, &pt_width, &pt_height); + scale = width / pt_width; + height = (int) ((scale * pt_height) + 0.5); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + + if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) + { + fprintf (stderr, "Failed to create cairo surface\n"); + goto error; + } + + cr = cairo_create (surface); + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) + { + fprintf (stderr, "Failed to create cairo handle\n"); + goto error; + } + + cairo_translate (cr, 0, 0); + cairo_scale (cr, scale, scale); + /* Render w/o annotations. */ + if (! do_render_annotaions || (options && options->printed)) + poppler_page_render_for_printing_with_options + (page, cr, POPPLER_PRINT_DOCUMENT); + else + poppler_page_render (page, cr) ; + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) + { + fprintf (stderr, "Failed to render page\n"); + goto error; + } + + /* This makes the colors look right. */ + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb (cr, 1., 1., 1.); + + cairo_paint (cr); + + if (options && options->usecolors) + image_recolor (surface, &options->fg, &options->bg); + + cairo_destroy (cr); + + return surface; + + error: + if (surface != NULL) + cairo_surface_destroy (surface); + if (cr != NULL) + cairo_destroy (cr); + return NULL; +} + +/** + * Write an image to a filename. + * + * @param cr The cairo context encapsulating the image. + * @param filename The filename to be written to. + * @param type The desired image type. + * + * @return 1 if the image was written successfully, else 0. + */ +static gboolean +image_write (cairo_surface_t *surface, const char *filename, enum image_type type) +{ + + int i, j; + unsigned char *data; + int width, height; + FILE *file = NULL; + gboolean success = 0; + + if (! surface || + cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) + { + fprintf (stderr, "Invalid cairo surface\n"); + return 0; + } + + if (! (file = fopen (filename, "wb"))) + { + fprintf (stderr, "Can not open file: %s\n", filename); + return 0; + } + + cairo_surface_flush (surface); + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_height (surface); + data = cairo_image_surface_get_data (surface); + + switch (type) + { + case PPM: + { + unsigned char *buffer = g_malloc (width * height * 3); + unsigned char *buffer_p = buffer; + + fprintf (file, "P6\n%d %d\n255\n", width, height); + for (i = 0; i < width * height; ++i, data += 4, buffer_p += 3) + ARGB_TO_RGB (buffer_p, data); + fwrite (buffer, 1, width * height * 3, file); + g_free (buffer); + success = 1; + } + break; + case PNG: + { + png_infop info_ptr = NULL; + png_structp png_ptr = NULL; + unsigned char *row = NULL; + + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto finalize; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + goto finalize; + + if (setjmp(png_jmpbuf(png_ptr))) + goto finalize; + + png_init_io (png_ptr, file); + png_set_compression_level (png_ptr, 1); + png_set_IHDR (png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_DEFAULT); + + png_set_filter (png_ptr, PNG_FILTER_TYPE_BASE, + PNG_FILTER_NONE); + png_write_info (png_ptr, info_ptr); + row = g_malloc (3 * width); + for (i = 0; i < height; ++i) + { + unsigned char *row_p = row; + for (j = 0; j < width; ++j, data += 4, row_p += 3) + { + ARGB_TO_RGB (row_p, data); + } + png_write_row (png_ptr, row); + } + png_write_end (png_ptr, NULL); + success = 1; + finalize: + if (png_ptr) + png_destroy_write_struct (&png_ptr, &info_ptr); + if (row) + g_free (row); + if (! success) + fprintf (stderr, "Error writing png data\n"); + } + break; + default: + internal_error ("switch fell through"); + } + + fclose (file); + return success; +} + +static void +image_write_print_response(cairo_surface_t *surface, enum image_type type) +{ + char *filename = mktempfile (); + + perror_if_not (filename, "Unable to create temporary file"); + if (image_write (surface, filename, type)) + { + OK_BEGIN (); + print_response_string (filename, NEWLINE); + OK_END (); + } + else + { + printf_error_response ("Unable to write image"); + } + free (filename); + error: + return; +} + +static void +region_print (cairo_region_t *region, double width, double height) +{ + int i; + + for (i = 0; i < cairo_region_num_rectangles (region); ++i) + { + cairo_rectangle_int_t r; + + cairo_region_get_rectangle (region, i, &r); + printf ("%f %f %f %f", + r.x / width, + r.y / height, + (r.x + r.width) / width, + (r.y + r.height) / height); + if (i < cairo_region_num_rectangles (region) - 1) + putchar (':'); + } + if (0 == cairo_region_num_rectangles (region)) + printf ("0.0 0.0 0.0 0.0"); +} + +/** + * Return a string representation of a PopplerActionType. + * + * @param type The PopplerActionType. + * + * @return It's string representation. + */ +static const char * +xpoppler_action_type_string(PopplerActionType type) +{ + switch (type) + { + case POPPLER_ACTION_UNKNOWN: return "unknown"; + case POPPLER_ACTION_NONE: return "none"; + case POPPLER_ACTION_GOTO_DEST: return "goto-dest"; + case POPPLER_ACTION_GOTO_REMOTE: return "goto-remote"; + case POPPLER_ACTION_LAUNCH: return "launch"; + case POPPLER_ACTION_URI: return "uri"; + case POPPLER_ACTION_NAMED: return "goto-dest"; /* actually "named" */ + case POPPLER_ACTION_MOVIE: return "movie"; + case POPPLER_ACTION_RENDITION: return "rendition"; + case POPPLER_ACTION_OCG_STATE: return "ocg-state"; + case POPPLER_ACTION_JAVASCRIPT: return "javascript"; + default: return "invalid"; + } +} + +/** + * Return a string representation of a PopplerAnnotType. + * + * @param type The PopplerAnnotType. + * + * @return It's string representation. + */ +static const char * +xpoppler_annot_type_string (PopplerAnnotType type) +{ + switch (type) + { + case POPPLER_ANNOT_UNKNOWN: return "unknown"; + case POPPLER_ANNOT_TEXT: return "text"; + case POPPLER_ANNOT_LINK: return "link"; + case POPPLER_ANNOT_FREE_TEXT: return "free-text"; + case POPPLER_ANNOT_LINE: return "line"; + case POPPLER_ANNOT_SQUARE: return "square"; + case POPPLER_ANNOT_CIRCLE: return "circle"; + case POPPLER_ANNOT_POLYGON: return "polygon"; + case POPPLER_ANNOT_POLY_LINE: return "poly-line"; + case POPPLER_ANNOT_HIGHLIGHT: return "highlight"; + case POPPLER_ANNOT_UNDERLINE: return "underline"; + case POPPLER_ANNOT_SQUIGGLY: return "squiggly"; + case POPPLER_ANNOT_STRIKE_OUT: return "strike-out"; + case POPPLER_ANNOT_STAMP: return "stamp"; + case POPPLER_ANNOT_CARET: return "caret"; + case POPPLER_ANNOT_INK: return "ink"; + case POPPLER_ANNOT_POPUP: return "popup"; + case POPPLER_ANNOT_FILE_ATTACHMENT: return "file"; + case POPPLER_ANNOT_SOUND: return "sound"; + case POPPLER_ANNOT_MOVIE: return "movie"; + case POPPLER_ANNOT_WIDGET: return "widget"; + case POPPLER_ANNOT_SCREEN: return "screen"; + case POPPLER_ANNOT_PRINTER_MARK: return "printer-mark"; + case POPPLER_ANNOT_TRAP_NET: return "trap-net"; + case POPPLER_ANNOT_WATERMARK: return "watermark"; + case POPPLER_ANNOT_3D: return "3d"; + default: return "invalid"; + } +} + +/** + * Return a string representation of a PopplerAnnotTextState. + * + * @param type The PopplerAnnotTextState. + * + * @return It's string representation. + */ +static const char * +xpoppler_annot_text_state_string (PopplerAnnotTextState state) +{ + switch (state) + { + case POPPLER_ANNOT_TEXT_STATE_MARKED: return "marked"; + case POPPLER_ANNOT_TEXT_STATE_UNMARKED: return "unmarked"; + case POPPLER_ANNOT_TEXT_STATE_ACCEPTED: return "accepted"; + case POPPLER_ANNOT_TEXT_STATE_REJECTED: return "rejected"; + case POPPLER_ANNOT_TEXT_STATE_CANCELLED: return "cancelled"; + case POPPLER_ANNOT_TEXT_STATE_COMPLETED: return "completed"; + case POPPLER_ANNOT_TEXT_STATE_NONE: return "none"; + case POPPLER_ANNOT_TEXT_STATE_UNKNOWN: + default: return "unknown"; + } +}; + +static document_t* +document_open (const epdfinfo_t *ctx, const char *filename, + const char *passwd, GError **gerror) +{ + char *uri; + document_t *doc = g_hash_table_lookup (ctx->documents, filename); + + if (NULL != doc) + return doc; + + doc = g_malloc0(sizeof (document_t)); + uri = g_filename_to_uri (filename, NULL, gerror); + if (uri != NULL) + doc->pdf = poppler_document_new_from_file(uri, passwd, gerror); + + if (NULL == doc->pdf) + { + g_free (doc); + doc = NULL; + } + else + { + doc->filename = g_strdup (filename); + doc->passwd = g_strdup (passwd); + g_hash_table_insert (ctx->documents, doc->filename, doc); + } + g_free (uri); + return doc; +} + +/** + * Split command args into a list of strings. + * + * @param args The colon separated list of arguments. + * @param nargs[out] The number of returned arguments. + * + * @return The list of arguments, which should be freed by the caller. + */ +static char ** +command_arg_split (const char *args, int *nargs) +{ + char **list = g_malloc (sizeof (char*) * 16); + int i = 0; + size_t allocated = 16; + char *buffer = NULL; + gboolean last = FALSE; + + if (! args) + goto theend; + + buffer = g_malloc (strlen (args) + 1); + + while (*args || last) + { + gboolean esc = FALSE; + char *buffer_p = buffer; + + while (*args && (*args != ':' || esc)) + { + if (esc) + { + if (*args == 'n') + { + ++args; + *buffer_p++ = '\n'; + } + else + { + *buffer_p++ = *args++; + } + esc = FALSE; + } + else if (*args == '\\') + { + ++args; + esc = TRUE; + } + else + { + *buffer_p++ = *args++; + } + } + + *buffer_p = '\0'; + + if (i >= allocated) + { + allocated = 2 * allocated + 1; + list = g_realloc (list, sizeof (char*) * allocated); + } + list[i++] = g_strdup (buffer); + + last = FALSE; + if (*args) + { + ++args; + if (! *args) + last = TRUE; + } + } + + theend: + g_free (buffer); + *nargs = i; + + return list; +} + +static gboolean +command_arg_parse_arg (const epdfinfo_t *ctx, const char *arg, + command_arg_t *cmd_arg, command_arg_type_t type, + gchar **error_msg) +{ + GError *gerror = NULL; + + if (! arg || !cmd_arg) + return FALSE; + + switch (type) + { + case ARG_DOC: + { + document_t *doc = document_open (ctx, arg, NULL, &gerror); + cerror_if_not (doc, error_msg, + "Error opening %s:%s", arg, + gerror ? gerror->message : "Unknown reason"); + + cmd_arg->value.doc = doc; + break; + } + case ARG_BOOL: + cerror_if_not (! strcmp (arg, "0") || ! strcmp (arg, "1"), + error_msg, "Expected 0 or 1:%s", arg); + cmd_arg->value.flag = *arg == '1'; + break; + case ARG_NONEMPTY_STRING: + cerror_if_not (*arg, error_msg, "Non-empty string expected"); + /* fall through */ + case ARG_STRING: + cmd_arg->value.string = arg; + break; + case ARG_NATNUM: + { + char *endptr; + long n = strtol (arg, &endptr, 0); + cerror_if_not (! (*endptr || (n < 0)), error_msg, + "Expected natural number:%s", arg); + cmd_arg->value.natnum = n; + } + break; + case ARG_EDGES_OR_POSITION: + { + PopplerRectangle *r = &cmd_arg->value.rectangle; + cerror_if_not (parse_edges_or_position (arg, r), + error_msg, + "Expected a relative position or rectangle: %s", arg); + } + break; + case ARG_EDGES: + { + PopplerRectangle *r = &cmd_arg->value.rectangle; + cerror_if_not (parse_edges (arg, r), + error_msg, + "Expected a relative rectangle: %s", arg); + } + break; + case ARG_EDGE_OR_NEGATIVE: + case ARG_EDGE: + { + char *endptr; + double n = strtod (arg, &endptr); + cerror_if_not (! (*endptr || (type != ARG_EDGE_OR_NEGATIVE && n < 0.0) || n > 1.0), + error_msg, "Expected a relative edge: %s", arg); + cmd_arg->value.edge = n; + } + break; + case ARG_COLOR: + { + guint r,g,b; + cerror_if_not ((strlen (arg) == 7 + && 3 == sscanf (arg, "#%2x%2x%2x", &r, &g, &b)), + error_msg, "Invalid color: %s", arg); + cmd_arg->value.color.red = r << 8; + cmd_arg->value.color.green = g << 8; + cmd_arg->value.color.blue = b << 8; + } + break; + case ARG_INVALID: + default: + internal_error ("switch fell through"); + } + + cmd_arg->type = type; + + return TRUE; + error: + if (gerror) + { + g_error_free (gerror); + gerror = NULL; + } + return FALSE; +} + +/** + * Parse arguments for a command. + * + * @param ctx The epdfinfo context. + * @param args A string holding the arguments. This is either empty + * or the suffix of the command starting at the first + * colon after the command name. + * @param len The length of args. + * @param cmd The command for which the arguments should be parsed. + * + * @return + */ +static command_arg_t* +command_arg_parse(epdfinfo_t *ctx, char **args, int nargs, + const command_t *cmd, gchar **error_msg) +{ + command_arg_t *cmd_args = g_malloc0 (cmd->nargs * sizeof (command_arg_t)); + int i; + + if (nargs < cmd->nargs - 1 + || (nargs == cmd->nargs - 1 + && cmd->args_spec[cmd->nargs - 1] != ARG_REST) + || (nargs > cmd->nargs + && (cmd->nargs == 0 + || cmd->args_spec[cmd->nargs - 1] != ARG_REST))) + { + if (error_msg) + { + *error_msg = + g_strdup_printf ("Command `%s' expects %d argument(s), %d given", + cmd->name, cmd->nargs, nargs); + } + goto failure; + } + + for (i = 0; i < cmd->nargs; ++i) + { + if (i == cmd->nargs - 1 && cmd->args_spec[i] == ARG_REST) + { + cmd_args[i].value.rest.args = args + i; + cmd_args[i].value.rest.nargs = nargs - i; + cmd_args[i].type = ARG_REST; + } + else if (i >= nargs + || ! command_arg_parse_arg (ctx, args[i], cmd_args + i, + cmd->args_spec[i], error_msg)) + { + goto failure; + } + } + + return cmd_args; + + failure: + free_command_args (cmd_args, cmd->nargs); + return NULL; +} + +static void +command_arg_print(const command_arg_t *arg) +{ + switch (arg->type) + { + case ARG_INVALID: + printf ("[invalid]"); + break; + case ARG_DOC: + print_response_string (arg->value.doc->filename, NONE); + break; + case ARG_BOOL: + printf ("%d", arg->value.flag ? 1 : 0); + break; + case ARG_NONEMPTY_STRING: /* fall */ + case ARG_STRING: + print_response_string (arg->value.string, NONE); + break; + case ARG_NATNUM: + printf ("%ld", arg->value.natnum); + break; + case ARG_EDGE_OR_NEGATIVE: /* fall */ + case ARG_EDGE: + printf ("%f", arg->value.edge); + break; + case ARG_EDGES_OR_POSITION: /* fall */ + case ARG_EDGES: + { + const PopplerRectangle *r = &arg->value.rectangle; + if (r->x2 < 0 && r->y2 < 0) + printf ("%f %f", r->x1, r->y1); + else + printf ("%f %f %f %f", r->x1, r->y1, r->x2, r->y2); + break; + } + case ARG_COLOR: + { + const PopplerColor *c = &arg->value.color; + printf ("#%.2x%.2x%.2x", c->red >> 8, + c->green >> 8, c->blue >> 8); + break; + } + case ARG_REST: + { + int i; + for (i = 0; i < arg->value.rest.nargs; ++i) + print_response_string (arg->value.rest.args[i], COLON); + if (arg->value.rest.nargs > 0) + print_response_string (arg->value.rest.args[i], NONE); + break; + } + default: + internal_error ("switch fell through"); + } +} + +static size_t +command_arg_type_size(command_arg_type_t type) +{ + command_arg_t arg; + switch (type) + { + case ARG_INVALID: return 0; + case ARG_DOC: return sizeof (arg.value.doc); + case ARG_BOOL: return sizeof (arg.value.flag); + case ARG_NONEMPTY_STRING: /* fall */ + case ARG_STRING: return sizeof (arg.value.string); + case ARG_NATNUM: return sizeof (arg.value.natnum); + case ARG_EDGE_OR_NEGATIVE: /* fall */ + case ARG_EDGE: return sizeof (arg.value.edge); + case ARG_EDGES_OR_POSITION: /* fall */ + case ARG_EDGES: return sizeof (arg.value.rectangle); + case ARG_COLOR: return sizeof (arg.value.color); + case ARG_REST: return sizeof (arg.value.rest); + default: + internal_error ("switch fell through"); + return 0; + } +} + + +/* ------------------------------------------------------------------ * + * PDF Actions + * ------------------------------------------------------------------ */ + +static gboolean +action_is_handled (PopplerAction *action) +{ + if (! action) + return FALSE; + + switch (action->any.type) + { + case POPPLER_ACTION_GOTO_REMOTE: + case POPPLER_ACTION_GOTO_DEST: + case POPPLER_ACTION_NAMED: + /* case POPPLER_ACTION_LAUNCH: */ + case POPPLER_ACTION_URI: + return TRUE; + default: ; + } + return FALSE; +} + +static void +action_print_destination (PopplerDocument *doc, PopplerAction *action) +{ + PopplerDest *dest = NULL; + gboolean free_dest = FALSE; + double width, height, top; + PopplerPage *page; + int saved_stdin; + + if (action->any.type == POPPLER_ACTION_GOTO_DEST + && action->goto_dest.dest->type == POPPLER_DEST_NAMED) + { + DISCARD_STDOUT (saved_stdin); + /* poppler_document_find_dest reports errors to stdout, so + discard them. */ + dest = poppler_document_find_dest + (doc, action->goto_dest.dest->named_dest); + UNDISCARD_STDOUT (saved_stdin); + free_dest = TRUE; + } + else if (action->any.type == POPPLER_ACTION_NAMED) + + { + DISCARD_STDOUT (saved_stdin); + dest = poppler_document_find_dest (doc, action->named.named_dest); + UNDISCARD_STDOUT (saved_stdin); + free_dest = TRUE; + } + + else if (action->any.type == POPPLER_ACTION_GOTO_REMOTE) + { + print_response_string (action->goto_remote.file_name, COLON); + dest = action->goto_remote.dest; + } + else if (action->any.type == POPPLER_ACTION_GOTO_DEST) + dest = action->goto_dest.dest; + + if (!dest + || dest->type == POPPLER_DEST_UNKNOWN + || dest->page_num < 1 + || dest->page_num > poppler_document_get_n_pages (doc)) + { + printf (":"); + goto theend; + } + + printf ("%d:", dest->page_num); + + if (action->type == POPPLER_ACTION_GOTO_REMOTE + || NULL == (page = poppler_document_get_page (doc, dest->page_num - 1))) + { + goto theend; + } + + poppler_page_get_size (page, &width, &height); + g_object_unref (page); + top = (height - dest->top) / height; + + /* adapted from xpdf */ + switch (dest->type) + { + case POPPLER_DEST_XYZ: + if (dest->change_top) + printf ("%f", top); + break; + case POPPLER_DEST_FIT: + case POPPLER_DEST_FITB: + case POPPLER_DEST_FITH: + case POPPLER_DEST_FITBH: + putchar ('0'); + break; + case POPPLER_DEST_FITV: + case POPPLER_DEST_FITBV: + case POPPLER_DEST_FITR: + printf ("%f", top); + break; + default: ; + } + + theend: + if (free_dest) + poppler_dest_free (dest); +} + +static void +action_print (PopplerDocument *doc, PopplerAction *action) +{ + if (! action_is_handled (action)) + return; + + print_response_string (xpoppler_action_type_string (action->any.type), COLON); + print_response_string (action->any.title, COLON); + switch (action->any.type) + { + case POPPLER_ACTION_GOTO_REMOTE: + case POPPLER_ACTION_GOTO_DEST: + case POPPLER_ACTION_NAMED: + action_print_destination (doc, action); + putchar ('\n'); + break; + case POPPLER_ACTION_LAUNCH: + print_response_string (action->launch.file_name, COLON); + print_response_string (action->launch.params, NEWLINE); + break; + case POPPLER_ACTION_URI: + print_response_string (action->uri.uri, NEWLINE); + break; + default: + ; + } +} + + +/* ------------------------------------------------------------------ * + * PDF Annotations and Attachments + * ------------------------------------------------------------------ */ + +/* static gint + * annotation_cmp_edges (const annotation_t *a1, const annotation_t *a2) + * { + * PopplerRectangle *e1 = &a1->amap->area; + * PopplerRectangle *e2 = &a2->amap->area; + * + * return (e1->y1 > e2->y1 ? -1 + * : e1->y1 < e2->y1 ? 1 + * : e1->x1 < e2->x1 ? -1 + * : e1->x1 != e2->x1); + * } */ + +static GList* +annoation_get_for_page (document_t *doc, gint pn) +{ + + GList *annot_list, *item; + PopplerPage *page; + gint i = 0; + gint npages = poppler_document_get_n_pages (doc->pdf); + + if (pn < 1 || pn > npages) + return NULL; + + if (! doc->annotations.pages) + doc->annotations.pages = g_malloc0 (npages * sizeof(GList*)); + + if (doc->annotations.pages[pn - 1]) + return doc->annotations.pages[pn - 1]; + + if (! doc->annotations.keys) + doc->annotations.keys = g_hash_table_new (g_str_hash, g_str_equal); + + page = poppler_document_get_page (doc->pdf, pn - 1); + if (NULL == page) + return NULL; + + annot_list = poppler_page_get_annot_mapping (page); + for (item = annot_list; item; item = item->next) + { + PopplerAnnotMapping *map = (PopplerAnnotMapping *)item->data; + gchar *key = g_strdup_printf ("annot-%d-%d", pn, i); + annotation_t *a = g_malloc (sizeof (annotation_t)); + a->amap = map; + a->key = key; + doc->annotations.pages[pn - 1] = + g_list_prepend (doc->annotations.pages[pn - 1], a); + assert (NULL == g_hash_table_lookup (doc->annotations.keys, key)); + g_hash_table_insert (doc->annotations.keys, key, a); + ++i; + } + g_list_free (annot_list); + g_object_unref (page); + return doc->annotations.pages[pn - 1]; +} + +static annotation_t* +annotation_get_by_key (document_t *doc, const gchar *key) +{ + if (! doc->annotations.keys) + return NULL; + + return g_hash_table_lookup (doc->annotations.keys, key); +} + +#ifdef HAVE_POPPLER_ANNOT_MARKUP +void +annotation_translate_quadrilateral (PopplerPage *page, PopplerQuadrilateral *q, gboolean inverse) +{ + PopplerRectangle cbox; + gdouble xs, ys; + + poppler_page_get_crop_box (page, &cbox); + xs = MIN (cbox.x1, cbox.x2); + ys = MIN (cbox.y1, cbox.y2); + + if (inverse) + { + xs = -xs; ys = -ys; + } + + q->p1.x -= xs, q->p2.x -= xs; q->p3.x -= xs; q->p4.x -= xs; + q->p1.y -= ys, q->p2.y -= ys; q->p3.y -= ys; q->p4.y -= ys; +} + +static cairo_region_t* +annotation_markup_get_text_regions (PopplerPage *page, PopplerAnnotTextMarkup *a) +{ + GArray *quads = poppler_annot_text_markup_get_quadrilaterals (a); + int i; + cairo_region_t *region = cairo_region_create (); + gdouble height; + + poppler_page_get_size (page, NULL, &height); + + for (i = 0; i < quads->len; ++i) + { + PopplerQuadrilateral *q = &g_array_index (quads, PopplerQuadrilateral, i); + cairo_rectangle_int_t r; + + annotation_translate_quadrilateral (page, q, FALSE); + q->p1.y = height - q->p1.y; + q->p2.y = height - q->p2.y; + q->p3.y = height - q->p3.y; + q->p4.y = height - q->p4.y; + + r.x = (int) (MIN (q->p1.x, MIN (q->p2.x, MIN (q->p3.x, q->p4.x))) + 0.5); + r.y = (int) (MIN (q->p1.y, MIN (q->p2.y, MIN (q->p3.y, q->p4.y))) + 0.5); + r.width = (int) (MAX (q->p1.x, MAX (q->p2.x, MAX (q->p3.x, q->p4.x))) + 0.5) + - r.x; + r.height = (int) (MAX (q->p1.y, MAX (q->p2.y, MAX (q->p3.y, q->p4.y))) + 0.5) + - r.y; + + cairo_region_union_rectangle (region, &r); + } + g_array_unref (quads); + return region; +} + +/** + * Append quadrilaterals equivalent to region to an array. + * + * @param page The page of the annotation. This is used to get the + * text regions and pagesize. + * @param region The region to add. + * @param garray[in,out] An array of PopplerQuadrilateral, where the + * new quadrilaterals will be appended. + */ +static void +annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *region, + GArray *garray) +{ + gdouble height; + /* poppler_page_get_selection_region is deprecated w/o a + replacement. (poppler_page_get_selected_region returns a union + of rectangles.) */ + GList *regions = + poppler_page_get_selection_region (page, 1.0, POPPLER_SELECTION_GLYPH, region); + GList *item; + + poppler_page_get_size (page, NULL, &height); + for (item = regions; item; item = item->next) + { + PopplerRectangle *r = item->data; + PopplerQuadrilateral q; + + q.p1.x = r->x1; + q.p1.y = height - r->y1; + q.p2.x = r->x2; + q.p2.y = height - r->y1; + q.p4.x = r->x2; + q.p4.y = height - r->y2; + q.p3.x = r->x1; + q.p3.y = height - r->y2; + + annotation_translate_quadrilateral (page, &q, TRUE); + g_array_append_val (garray, q); + } + g_list_free (regions); +} + +#endif +/** + * Create a new annotation. + * + * @param doc The document for which to create it. + * @param type The type of the annotation. + * @param r The rectangle where annotation will end up on the page. + * + * @return The new annotation, or NULL, if the annotation type is + * not available. + */ +static PopplerAnnot* +annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page, + const char *type, PopplerRectangle *r, + const command_arg_t *rest, char **error_msg) +{ + + PopplerAnnot *a = NULL; + int nargs = rest->value.rest.nargs; +#ifdef HAVE_POPPLER_ANNOT_MARKUP + char * const *args = rest->value.rest.args; + int i; + GArray *garray = NULL; + command_arg_t carg; + double width, height; + cairo_region_t *region = NULL; +#endif + + if (! strcmp (type, "text")) + { + cerror_if_not (nargs == 0, error_msg, "%s", "Too many arguments"); + return poppler_annot_text_new (doc->pdf, r); + } + +#ifdef HAVE_POPPLER_ANNOT_MARKUP + garray = g_array_new (FALSE, FALSE, sizeof (PopplerQuadrilateral)); + poppler_page_get_size (page, &width, &height); + for (i = 0; i < nargs; ++i) + { + PopplerRectangle *rr = &carg.value.rectangle; + + error_if_not (command_arg_parse_arg (ctx, args[i], &carg, + ARG_EDGES, error_msg)); + rr->x1 *= width; rr->x2 *= width; + rr->y1 *= height; rr->y2 *= height; + annotation_markup_append_text_region (page, rr, garray); + } + cerror_if_not (garray->len > 0, error_msg, "%s", + "Unable to create empty markup annotation"); + + if (! strcmp (type, "highlight")) + a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray); + else if (! strcmp (type, "squiggly")) + a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray); + else if (! strcmp (type, "strike-out")) + a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray); + else if (! strcmp (type, "underline")) + a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray); + else + cerror_if_not (0, error_msg, "Unknown annotation type: %s", type); + +#endif + error: +#ifdef HAVE_POPPLER_ANNOT_MARKUP + if (garray) g_array_unref (garray); + if (region) cairo_region_destroy (region); +#endif + return a; +} + +static gboolean +annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest, + PopplerAnnot *annotation, char **error_msg) +{ + int nargs = rest->value.rest.nargs; + char * const *args = rest->value.rest.args; + int i = 0; + command_arg_t carg; + + const char* error_fmt = + "Can modify `%s' property only for %s annotations"; + + while (i < nargs) + { + command_arg_type_t atype = ARG_INVALID; + const char *key = args[i++]; + + cerror_if_not (i < nargs, error_msg, "Missing a value argument"); + + if (! strcmp (key, "flags")) + atype = ARG_NATNUM; + else if (! strcmp (key, "color")) + atype = ARG_COLOR; + else if (! strcmp (key, "contents")) + atype = ARG_STRING; + else if (! strcmp (key, "edges")) + atype = ARG_EDGES_OR_POSITION; + else if (! strcmp (key, "label")) + { + cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, + error_fmt, key, "markup"); + atype = ARG_STRING; + } + else if (! strcmp (key, "opacity")) + { + cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, + error_fmt, key, "markup"); + atype = ARG_EDGE; + } + else if (! strcmp (key, "popup")) + { + cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, + error_fmt, key, "markup"); + atype = ARG_EDGES; + } + else if (! strcmp (key, "popup-is-open")) + { + cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, + error_fmt, key, "markup"); + atype = ARG_BOOL; + } + else if (! strcmp (key, "icon")) + { + cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, + error_fmt, key, "text"); + atype = ARG_STRING; + } + else if (! strcmp (key, "is-open")) + { + cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, + error_fmt, key, "text"); + atype = ARG_BOOL; + } + else + { + cerror_if_not (0, error_msg, + "Unable to modify property `%s'", key); + } + + if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg)) + return FALSE; + } + + return TRUE; + + error: + return FALSE; +} + +static void +annotation_print (const annotation_t *annot, /* const */ PopplerPage *page) +{ + double width, height; + PopplerAnnotMapping *m; + const gchar *key; + PopplerAnnot *a; + PopplerAnnotMarkup *ma; + PopplerAnnotText *ta; + PopplerRectangle r; + PopplerColor *color; + gchar *text; + gdouble opacity; + cairo_region_t *region = NULL; + + if (! annot || ! page) + return; + + m = annot->amap; + key = annot->key; + a = m->annot; + poppler_page_get_size (page, &width, &height); + + r.x1 = m->area.x1; + r.x2 = m->area.x2; + r.y1 = height - m->area.y2; + r.y2 = height - m->area.y1; + +#ifdef HAVE_POPPLER_ANNOT_MARKUP + if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) + { + region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a)); + perror_if_not (region, "%s", "Unable to extract annotation's text regions"); + } +#endif + + /* >>> Any Annotation >>> */ + /* Page */ + printf ("%d:", poppler_page_get_index (page) + 1); + /* Area */ + printf ("%f %f %f %f:", r.x1 / width, r.y1 / height + , r.x2 / width, r.y2 / height); + + /* Type */ + printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a))); + /* Internal Key */ + print_response_string (key, COLON); + + /* Flags */ + printf ("%d:", poppler_annot_get_flags (a)); + + /* Color */ + color = poppler_annot_get_color (a); + if (color) + { + /* Reduce 2 Byte to 1 Byte color space */ + printf ("#%.2x%.2x%.2x", (color->red >> 8) + , (color->green >> 8) + , (color->blue >> 8)); + g_free (color); + } + + putchar (':'); + + /* Text Contents */ + text = poppler_annot_get_contents (a); + print_response_string (text, COLON); + g_free (text); + + /* Modified Date */ + text = poppler_annot_get_modified (a); + print_response_string (text, NONE); + g_free (text); + + /* <<< Any Annotation <<< */ + + /* >>> Markup Annotation >>> */ + if (! POPPLER_IS_ANNOT_MARKUP (a)) + { + putchar ('\n'); + goto theend; + } + + putchar (':'); + ma = POPPLER_ANNOT_MARKUP (a); + /* Label */ + text = poppler_annot_markup_get_label (ma); + print_response_string (text, COLON); + g_free (text); + + /* Subject */ + text = poppler_annot_markup_get_subject (ma); + print_response_string (text, COLON); + g_free (text); + + /* Opacity */ + opacity = poppler_annot_markup_get_opacity (ma); + printf ("%f:", opacity); + + /* Popup (Area + isOpen) */ + if (poppler_annot_markup_has_popup (ma) + && poppler_annot_markup_get_popup_rectangle (ma, &r)) + { + gdouble tmp = r.y1; + r.y1 = height - r.y2; + r.y2 = height - tmp; + printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height + , r.x2 / width, r.y2 / height + , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0); + + } + else + printf ("::"); + + /* Creation Date */ + text = xpoppler_annot_markup_get_created (ma); + if (text) + { + print_response_string (text, NONE); + g_free (text); + } + + /* <<< Markup Annotation <<< */ + + /* >>> Text Annotation >>> */ + if (POPPLER_IS_ANNOT_TEXT (a)) + { + putchar (':'); + ta = POPPLER_ANNOT_TEXT (a); + /* Text Icon */ + text = poppler_annot_text_get_icon (ta); + print_response_string (text, COLON); + g_free (text); + /* Text State */ + printf ("%s:%d", + xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)), + poppler_annot_text_get_is_open (ta)); + } +#ifdef HAVE_POPPLER_ANNOT_MARKUP + /* <<< Text Annotation <<< */ + else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) + { + /* >>> Markup Text Annotation >>> */ + putchar (':'); + region_print (region, width, height); + /* <<< Markup Text Annotation <<< */ + } +#endif + putchar ('\n'); + theend: +#ifdef HAVE_POPPLER_ANNOT_MARKUP + error: +#endif + if (region) cairo_region_destroy (region); +} + +static void +attachment_print (PopplerAttachment *att, const char *id, gboolean do_save) +{ + time_t time; + + print_response_string (id, COLON); + print_response_string (att->name, COLON); + print_response_string (att->description, COLON); + if (att->size + 1 != 0) + printf ("%" G_GSIZE_FORMAT ":", att->size); + else + printf ("-1:"); + time = (time_t) att->mtime; + print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); + time = (time_t) att->ctime; + print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); + print_response_string (att->checksum ? att->checksum->str : "" , COLON); + if (do_save) + { + char *filename = mktempfile (); + GError *error = NULL; + if (filename) + { + if (! poppler_attachment_save (att, filename, &error)) + { + fprintf (stderr, "Writing attachment failed: %s" + , error ? error->message : "reason unknown"); + if (error) + g_free (error); + } + else + { + print_response_string (filename, NONE); + } + free (filename); + } + } + putchar ('\n'); +} + + + +/* ================================================================== * + * Server command implementations + * ================================================================== */ + +/* Name: features + Args: None + Returns: A list of compile-time features. + Errors: None +*/ + +const command_arg_type_t cmd_features_spec[] = {}; + +static void +cmd_features (const epdfinfo_t *ctx, const command_arg_t *args) +{ + const char *features[] = { +#ifdef HAVE_POPPLER_FIND_OPTS + "case-sensitive-search", +#else + "no-case-sensitive-search", +#endif +#ifdef HAVE_POPPLER_ANNOT_WRITE + "writable-annotations", +#else + "no-writable-annotations", +#endif +#ifdef HAVE_POPPLER_ANNOT_MARKUP + "markup-annotations" +#else + "no-markup-annotations" +#endif + }; + int i; + OK_BEGIN (); + for (i = 0; i < G_N_ELEMENTS (features); ++i) + { + printf ("%s", features[i]); + if (i < G_N_ELEMENTS (features) - 1) + putchar (':'); + } + putchar ('\n'); + OK_END (); +} + + +/* Name: open + Args: filename password + Returns: Nothing + Errors: If file can't be opened or is not a PDF document. +*/ + +const command_arg_type_t cmd_open_spec[] = + { + ARG_NONEMPTY_STRING, /* filename */ + ARG_STRING, /* password */ + }; + +static void +cmd_open (const epdfinfo_t *ctx, const command_arg_t *args) +{ + const char *filename = args[0].value.string; + const char *passwd = args[1].value.string; + GError *gerror = NULL; + document_t *doc; + + if (! *passwd) + passwd = NULL; + + doc = document_open(ctx, filename, passwd, &gerror); + perror_if_not (doc, "Error opening %s:%s", filename, + gerror ? gerror->message : "unknown error"); + OK (); + + error: + if (gerror) + { + g_error_free (gerror); + gerror = NULL; + } +} + +/* Name: close + Args: filename + Returns: 1 if file was open, otherwise 0. + Errors: None +*/ + +const command_arg_type_t cmd_close_spec[] = + { + ARG_NONEMPTY_STRING /* filename */ + }; + +static void +cmd_close (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string); + + g_hash_table_remove (ctx->documents, args->value.string); + free_document (doc); + OK_BEGIN (); + puts (doc ? "1" : "0"); + OK_END (); +} + +/* Name: closeall + Args: None + Returns: Nothing + Errors: None +*/ +static void +cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, ctx->documents); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + document_t *doc = (document_t*) value; + free_document (doc); + g_hash_table_iter_remove (&iter); + } + OK (); +} + + +const command_arg_type_t cmd_search_regexp_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* first page */ + ARG_NATNUM, /* last page */ + ARG_NONEMPTY_STRING, /* regexp */ + ARG_NATNUM, /* compile flags */ + ARG_NATNUM /* match flags */ + }; + +static void +cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int first = args[1].value.natnum; + int last = args[2].value.natnum; + const gchar *regexp = args[3].value.string; + GRegexCompileFlags cflags = args[4].value.natnum; + GRegexMatchFlags mflags = args[5].value.natnum; + double width, height; + int pn; + GError *gerror = NULL; + GRegex *re = NULL; + + NORMALIZE_PAGE_ARG (doc, &first, &last); + + re = g_regex_new (regexp, cflags, mflags, &gerror); + perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message); + + OK_BEGIN (); + for (pn = first; pn <= last; ++pn) + { + PopplerPage *page = poppler_document_get_page(doc, pn - 1); + char *text; + PopplerRectangle *rectangles = NULL; + guint nrectangles; + GMatchInfo *match = NULL; + + if (! page) + continue; + + text = poppler_page_get_text (page); + poppler_page_get_text_layout (page, &rectangles, &nrectangles); + poppler_page_get_size (page, &width, &height); + g_regex_match (re, text, 0, &match); + + while (g_match_info_matches (match)) + { + const double scale = 100.0; + gint start, end, ustart, ulen; + gchar *string = NULL; + gchar *line = NULL; + int i; + + /* Does this ever happen ? */ + if (! g_match_info_fetch_pos (match, 0, &start, &end)) + continue; + + string = g_match_info_fetch (match, 0); + ustart = g_utf8_strlen (text, start); + ulen = g_utf8_strlen (string, -1); + + cairo_region_t *region = cairo_region_create (); + /* Merge matched glyph rectangles. Scale them so we're able + to use cairo . */ + if (ulen > 0) + { + assert (ustart < nrectangles + && ustart + ulen <= nrectangles); + line = poppler_page_get_selected_text + (page, POPPLER_SELECTION_LINE, rectangles + ustart); + + for (i = ustart; i < ustart + ulen; ++i) + { + PopplerRectangle *r = rectangles + i; + cairo_rectangle_int_t c; + + c.x = (int) (scale * r->x1 + 0.5); + c.y = (int) (scale * r->y1 + 0.5); + c.width = (int) (scale * (r->x2 - r->x1) + 0.5); + c.height = (int) (scale * (r->y2 - r->y1) + 0.5); + + cairo_region_union_rectangle (region, &c); + } + + } + + printf ("%d:", pn); + print_response_string (string, COLON); + print_response_string (strchomp (line), COLON); + region_print (region, width * scale, height * scale); + putchar ('\n'); + cairo_region_destroy (region); + g_free (string); + g_free (line); + g_match_info_next (match, NULL); + } + g_free (rectangles); + g_object_unref (page); + g_free (text); + g_match_info_free (match); + } + OK_END (); + + error: + if (re) g_regex_unref (re); + if (gerror) g_error_free (gerror); +} + +const command_arg_type_t cmd_regexp_flags_spec[] = + { + }; + +static void +cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args) +{ + OK_BEGIN (); + printf ("caseless:%d\n", G_REGEX_CASELESS); + printf ("multiline:%d\n", G_REGEX_MULTILINE); + printf ("dotall:%d\n", G_REGEX_DOTALL); + printf ("extended:%d\n", G_REGEX_EXTENDED); + printf ("anchored:%d\n", G_REGEX_ANCHORED); + printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY); + printf ("ungreedy:%d\n", G_REGEX_UNGREEDY); + printf ("raw:%d\n", G_REGEX_RAW); + printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE); + printf ("optimize:%d\n", G_REGEX_OPTIMIZE); + printf ("dupnames:%d\n", G_REGEX_DUPNAMES); + printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR); + printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF); + printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF); + + printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED); + printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL); + printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL); + printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY); + printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL); + printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR); + printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF); + printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF); + printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY); + + OK_END (); +} + + +const command_arg_type_t cmd_search_string_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* first page */ + ARG_NATNUM, /* last page */ + ARG_NONEMPTY_STRING, /* search string */ + ARG_BOOL, /* ignore-case */ + }; + +static void +cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int first = args[1].value.natnum; + int last = args[2].value.natnum; + const char *string = args[3].value.string; + gboolean ignore_case = args[4].value.flag; + GList *list, *item; + double width, height; + int pn; +#ifdef HAVE_POPPLER_FIND_OPTS + PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE; +#endif + + NORMALIZE_PAGE_ARG (doc, &first, &last); + OK_BEGIN (); + for (pn = first; pn <= last; ++pn) + { + PopplerPage *page = poppler_document_get_page(doc, pn - 1); + + if (! page) + continue; + +#ifdef HAVE_POPPLER_FIND_OPTS + list = poppler_page_find_text_with_options(page, string, flags); +#else + list = poppler_page_find_text(page, string); +#endif + + poppler_page_get_size (page, &width, &height); + + for (item = list; item; item = item->next) + { + gchar *line, *match; + PopplerRectangle *r = item->data; + gdouble y1 = r->y1; + + r->y1 = height - r->y2; + r->y2 = height - y1; + + printf ("%d:", pn); + line = strchomp (poppler_page_get_selected_text + (page, POPPLER_SELECTION_LINE, r)); + match = strchomp (poppler_page_get_selected_text + (page, POPPLER_SELECTION_GLYPH, r)); + print_response_string (match, COLON); + print_response_string (line, COLON); + printf ("%f %f %f %f\n", + r->x1 / width, r->y1 / height, + r->x2 / width, r->y2 / height); + g_free (line); + g_free (match); + poppler_rectangle_free (r); + } + g_list_free (list); + g_object_unref (page); + } + OK_END (); +} + +/* Name: metadata + Args: filename + Returns: PDF's metadata + Errors: None + + title author subject keywords creator producer pdf-version create-date mod-date + + Dates are in seconds since the epoche. + +*/ + +const command_arg_type_t cmd_metadata_spec[] = + { + ARG_DOC, + }; + +static void +cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + time_t date; + gchar *md[6]; + gchar *title; + int i; + char *time_str; + + OK_BEGIN (); + + title = poppler_document_get_title (doc); + print_response_string (title, COLON); + g_free (title); + + md[0] = poppler_document_get_author (doc); + md[1] = poppler_document_get_subject (doc); + md[2] = poppler_document_get_keywords (doc); + md[3] = poppler_document_get_creator (doc); + md[4] = poppler_document_get_producer (doc); + md[5] = poppler_document_get_pdf_version_string (doc); + + for (i = 0; i < 6; ++i) + { + print_response_string (md[i], COLON); + g_free (md[i]); + } + + date = poppler_document_get_creation_date (doc); + time_str = strchomp (ctime (&date)); + print_response_string (time_str ? time_str : "", COLON); + date = poppler_document_get_modification_date (doc); + time_str = strchomp (ctime (&date)); + print_response_string (time_str ? time_str : "", NEWLINE); + OK_END (); +} + +/* Name: outline + Args: filename + + Returns: The documents outline (or index) as a, possibly empty, + list of records: + + tree-level ACTION + + See cmd_pagelinks for how ACTION is constructed. + + Errors: None +*/ + +static void +cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth) +{ + do + { + PopplerIndexIter *child; + PopplerAction *action = poppler_index_iter_get_action (iter); + + if (! action) + continue; + + if (action_is_handled (action)) + { + printf ("%d:", depth); + action_print (doc, action); + } + + child = poppler_index_iter_get_child (iter); + if (child) + { + cmd_outline_walk (doc, child, depth + 1); + } + poppler_action_free (action); + poppler_index_iter_free (child); + } while (poppler_index_iter_next (iter)); +} + +const command_arg_type_t cmd_outline_spec[] = + { + ARG_DOC, + }; + +static void +cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf); + OK_BEGIN (); + if (iter) + { + cmd_outline_walk (args->value.doc->pdf, iter, 1); + poppler_index_iter_free (iter); + } + OK_END (); +} + +/* Name: quit + Args: None + Returns: Nothing + Errors: None + + Close all documents and exit. +*/ + + +const command_arg_type_t cmd_quit_spec[] = {}; + +static void +cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args) +{ + cmd_closeall (ctx, args); + exit (EXIT_SUCCESS); +} + +/* Name: number-of-pages + Args: filename + Returns: The number of pages. + Errors: None +*/ + + +const command_arg_type_t cmd_number_of_pages_spec[] = + { + ARG_DOC + }; + +static void +cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args) +{ + int npages = poppler_document_get_n_pages (args->value.doc->pdf); + OK_BEGIN (); + printf ("%d\n", npages); + OK_END (); +} + +/* Name: pagelinks + Args: filename page + Returns: A list of linkmaps: + + edges ACTION , + + where ACTION is one of + + 'goto-dest' title page top + 'goto-remote' title filename page top + 'uri' title URI + 'launch' title program arguments + + top is desired vertical position, filename is the target PDF of the + `goto-remote' link. + + Errors: None +*/ + + +const command_arg_type_t cmd_pagelinks_spec[] = + { + ARG_DOC, + ARG_NATNUM /* page number */ + }; + +static void +cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + PopplerPage *page = NULL; + int pn = args[1].value.natnum; + double width, height; + GList *link_map = NULL, *item; + + page = poppler_document_get_page (doc, pn - 1); + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &width, &height); + link_map = poppler_page_get_link_mapping (page); + + OK_BEGIN (); + for (item = g_list_last (link_map); item; item = item->prev) + { + + PopplerLinkMapping *link = item->data; + PopplerRectangle *r = &link->area; + gdouble y1 = r->y1; + /* LinkMappings have a different gravity. */ + r->y1 = height - r->y2; + r->y2 = height - y1; + + if (! action_is_handled (link->action)) + continue; + + printf ("%f %f %f %f:", + r->x1 / width, r->y1 / height, + r->x2 / width, r->y2 / height); + action_print (doc, link->action); + } + OK_END (); + error: + if (page) g_object_unref (page); + if (link_map) poppler_page_free_link_mapping (link_map); +} + +/* Name: gettext + Args: filename page edges selection-style + Returns: The selection's text. + Errors: If page is out of range. + + For the selection-style argument see getselection command. +*/ + + +const command_arg_type_t cmd_gettext_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_EDGES, /* selection */ + ARG_NATNUM /* selection-style */ + }; + +static void +cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int pn = args[1].value.natnum; + PopplerRectangle r = args[2].value.rectangle; + int selection_style = args[3].value.natnum; + PopplerPage *page = NULL; + double width, height; + gchar *text = NULL; + + switch (selection_style) + { + case POPPLER_SELECTION_GLYPH: break; + case POPPLER_SELECTION_LINE: break; + case POPPLER_SELECTION_WORD: break; + default: selection_style = POPPLER_SELECTION_GLYPH; + } + + page = poppler_document_get_page (doc, pn - 1); + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &width, &height); + r.x1 = r.x1 * width; + r.x2 = r.x2 * width; + r.y1 = r.y1 * height; + r.y2 = r.y2 * height; + /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */ + text = poppler_page_get_selected_text (page, selection_style, &r); + + OK_BEGIN (); + print_response_string (text, NEWLINE); + OK_END (); + + error: + g_free (text); + if (page) g_object_unref (page); +} + +/* Name: getselection + Args: filename page edges selection-selection_style + Returns: The selection's text. + Errors: If page is out of range. + + selection-selection_style should be as follows. + + 0 (POPPLER_SELECTION_GLYPH) + glyph is the minimum unit for selection + + 1 (POPPLER_SELECTION_WORD) + word is the minimum unit for selection + + 2 (POPPLER_SELECTION_LINE) + line is the minimum unit for selection +*/ + + +const command_arg_type_t cmd_getselection_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_EDGES, /* selection */ + ARG_NATNUM /* selection-style */ + }; + +static void +cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int pn = args[1].value.natnum; + PopplerRectangle r = args[2].value.rectangle; + int selection_style = args[3].value.natnum; + gdouble width, height; + cairo_region_t *region = NULL; + PopplerPage *page = NULL; + int i; + + switch (selection_style) + { + case POPPLER_SELECTION_GLYPH: break; + case POPPLER_SELECTION_LINE: break; + case POPPLER_SELECTION_WORD: break; + default: selection_style = POPPLER_SELECTION_GLYPH; + } + + page = poppler_document_get_page (doc, pn - 1); + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &width, &height); + + r.x1 = r.x1 * width; + r.x2 = r.x2 * width; + r.y1 = r.y1 * height; + r.y2 = r.y2 * height; + + region = poppler_page_get_selected_region (page, 1.0, selection_style, &r); + + OK_BEGIN (); + for (i = 0; i < cairo_region_num_rectangles (region); ++i) + { + cairo_rectangle_int_t r; + + cairo_region_get_rectangle (region, i, &r); + printf ("%f %f %f %f\n", + r.x / width, + r.y / height, + (r.x + r.width) / width, + (r.y + r.height) / height); + } + OK_END (); + + error: + if (region) cairo_region_destroy (region); + if (page) g_object_unref (page); +} + +/* Name: pagesize + Args: filename page + Returns: width height + Errors: If page is out of range. +*/ + + +const command_arg_type_t cmd_pagesize_spec[] = + { + ARG_DOC, + ARG_NATNUM /* page number */ + }; + +static void +cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int pn = args[1].value.natnum; + PopplerPage *page = NULL; + double width, height; + + + page = poppler_document_get_page (doc, pn - 1); + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &width, &height); + + OK_BEGIN (); + printf ("%f:%f\n", width, height); + OK_END (); + + error: + if (page) g_object_unref (page); +} + +/* Annotations */ + +/* Name: getannots + Args: filename firstpage lastpage + Returns: The list of annotations of this page. + + For all annotations + + page edges type key flags color contents mod-date + + ,where + + name is a document-unique name, + flags is PopplerAnnotFlag bitmask, + color is 3-byte RGB hex number and + + Then + + label subject opacity popup-edges popup-is-open create-date + + if this is a markup annotation and additionally + + text-icon text-state + + for markup text annotations. + + Errors: If page is out of range. +*/ + + +const command_arg_type_t cmd_getannots_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* first page */ + ARG_NATNUM /* last page */ + }; + +static void +cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + gint first = args[1].value.natnum; + gint last = args[2].value.natnum; + GList *list; + gint pn; + + first = MAX(1, first); + if (last <= 0) + last = poppler_document_get_n_pages (doc); + else + last = MIN(last, poppler_document_get_n_pages (doc)); + + OK_BEGIN (); + for (pn = first; pn <= last; ++pn) + { + GList *annots = annoation_get_for_page (args->value.doc, pn); + PopplerPage *page = poppler_document_get_page (doc, pn - 1); + + if (! page) + continue; + + for (list = annots; list; list = list->next) + { + annotation_t *annot = (annotation_t *)list->data; + annotation_print (annot, page); + } + g_object_unref (page); + } + OK_END (); +} + +/* Name: getannot + Args: filename name + Returns: The annotation for name, see cmd_getannots. + Errors: If no annotation named ,name' exists. +*/ + + +const command_arg_type_t cmd_getannot_spec[] = + { + ARG_DOC, + ARG_NONEMPTY_STRING, /* annotation's key */ + }; + +static void +cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + const gchar *key = args[1].value.string; + PopplerPage *page = NULL; + annotation_t *a = annotation_get_by_key (doc, key); + gint index; + + perror_if_not (a, "No such annotation: %s", key); + index = poppler_annot_get_page_index (a->amap->annot); + if (index >= 0) + page = poppler_document_get_page (doc->pdf, index); + perror_if_not (page, "Unable to get page %d", index + 1); + + OK_BEGIN (); + annotation_print (a, page); + OK_END (); + + error: + if (page) g_object_unref (page); +} + +/* Name: getannot_attachment + Args: filename name [output-filename] + Returns: name description size mtime ctime output-filename + Errors: If no annotation named ,name' exists or output-filename is + not writable. +*/ + + +const command_arg_type_t cmd_getattachment_from_annot_spec[] = + { + ARG_DOC, + ARG_NONEMPTY_STRING, /* annotation's name */ + ARG_BOOL /* save attachment */ + }; + +static void +cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + const gchar *key = args[1].value.string; + gboolean do_save = args[2].value.flag; + PopplerAttachment *att = NULL; + annotation_t *a = annotation_get_by_key (doc, key); + gchar *id = NULL; + + perror_if_not (a, "No such annotation: %s", key); + perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot), + "Not a file annotation: %s", key); + att = poppler_annot_file_attachment_get_attachment + (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot)); + perror_if_not (att, "Unable to get attachment: %s", key); + id = g_strdup_printf ("attachment-%s", key); + + OK_BEGIN (); + attachment_print (att, id, do_save); + OK_END (); + + error: + if (att) g_object_unref (att); + if (id) g_free (id); +} + + +/* document-level attachments */ +const command_arg_type_t cmd_getattachments_spec[] = + { + ARG_DOC, + ARG_BOOL, /* save attachments */ + }; + +static void +cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + gboolean do_save = args[1].value.flag; + GList *item; + GList *attmnts = poppler_document_get_attachments (doc->pdf); + int i; + + OK_BEGIN (); + for (item = attmnts, i = 0; item; item = item->next, ++i) + { + PopplerAttachment *att = (PopplerAttachment*) item->data; + gchar *id = g_strdup_printf ("attachment-document-%d", i); + + attachment_print (att, id, do_save); + g_object_unref (att); + g_free (id); + } + g_list_free (attmnts); + + OK_END (); +} + +#ifdef HAVE_POPPLER_ANNOT_WRITE + +const command_arg_type_t cmd_addannot_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_STRING, /* type */ + ARG_EDGES_OR_POSITION, /* edges or position (uses default size) */ + ARG_REST, /* markup regions */ + }; + +static void +cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) +{ + + document_t *doc = args->value.doc; + gint pn = args[1].value.natnum; + const char *type_string = args[2].value.string; + PopplerRectangle r = args[3].value.rectangle; + int i; + PopplerPage *page = NULL; + double width, height; + PopplerAnnot *pa; + PopplerAnnotMapping *amap; + annotation_t *a; + gchar *key; + GList *annotations; + gdouble y2; + char *error_msg = NULL; + + page = poppler_document_get_page (doc->pdf, pn - 1); + perror_if_not (page, "Unable to get page %d", pn); + poppler_page_get_size (page, &width, &height); + r.x1 *= width; r.x2 *= width; + r.y1 *= height; r.y2 *= height; + if (r.y2 < 0) + r.y2 = r.y1 + 24; + if (r.x2 < 0) + r.x2 = r.x1 + 24; + y2 = r.y2; + r.y2 = height - r.y1; + r.y1 = height - y2; + + pa = annotation_new (ctx, doc, page, type_string, &r, &args[4], &error_msg); + perror_if_not (pa, "Creating annotation failed: %s", + error_msg ? error_msg : "Reason unknown"); + amap = poppler_annot_mapping_new (); + amap->area = r; + amap->annot = pa; + annotations = annoation_get_for_page (doc, pn); + + i = g_list_length (annotations); + key = g_strdup_printf ("annot-%d-%d", pn, i); + while (g_hash_table_lookup (doc->annotations.keys, key)) + { + g_free (key); + key = g_strdup_printf ("annot-%d-%d", pn, ++i); + } + a = g_malloc (sizeof (annotation_t)); + a->amap = amap; + a->key = key; + doc->annotations.pages[pn - 1] = + g_list_prepend (annotations, a); + g_hash_table_insert (doc->annotations.keys, key, a); + poppler_page_add_annot (page, pa); + OK_BEGIN (); + annotation_print (a, page); + OK_END (); + + error: + if (page) g_object_unref (page); + if (error_msg) g_free (error_msg); +} + + +const command_arg_type_t cmd_delannot_spec[] = + { + ARG_DOC, + ARG_NONEMPTY_STRING /* Annotation's key */ + }; + +static void +cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + const gchar *key = args[1].value.string; + PopplerPage *page = NULL; + annotation_t *a = annotation_get_by_key (doc, key); + gint pn; + + perror_if_not (a, "No such annotation: %s", key); + pn = poppler_annot_get_page_index (a->amap->annot) + 1; + if (pn >= 1) + page = poppler_document_get_page (doc->pdf, pn - 1); + perror_if_not (page, "Unable to get page %d", pn); + poppler_page_remove_annot (page, a->amap->annot); + doc->annotations.pages[pn - 1] = + g_list_remove (doc->annotations.pages[pn - 1], a); + g_hash_table_remove (doc->annotations.keys, a->key); + poppler_annot_mapping_free(a->amap); + OK (); + + error: + if (a) + { + g_free (a->key); + g_free (a); + } + if (page) g_object_unref (page); +} + +const command_arg_type_t cmd_editannot_spec[] = + { + ARG_DOC, + ARG_NONEMPTY_STRING, /* annotation key */ + ARG_REST /* (KEY VALUE ...) */ + }; + +static void +cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + const char *key = args[1].value.string; + int nrest_args = args[2].value.rest.nargs; + char * const *rest_args = args[2].value.rest.args; + annotation_t *a = annotation_get_by_key (doc, key); + PopplerAnnot *pa; + PopplerPage *page = NULL; + int i = 0; + gint index; + char *error_msg = NULL; + command_arg_t carg; + const char *unexpected_parse_error = "Internal error while parsing arg `%s'"; + + perror_if_not (a, "No such annotation: %s", key); + pa = a->amap->annot; + perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg), + "%s", error_msg); + index = poppler_annot_get_page_index (pa); + page = poppler_document_get_page (doc->pdf, index); + perror_if_not (page, "Unable to get page %d for annotation", index); + + for (i = 0; i < nrest_args; ++i) + { + const char *key = rest_args[i++]; + + if (! strcmp (key, "flags")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_NATNUM, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_set_flags (pa, carg.value.natnum); + } + else if (! strcmp (key, "color")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_COLOR, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_set_color (pa, &carg.value.color); + } + else if (! strcmp (key, "contents")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_STRING, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_set_contents (pa, carg.value.string); + } + else if (! strcmp (key, "edges")) + { + PopplerRectangle *area = &a->amap->area; + gdouble width, height; + PopplerRectangle r; + + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_EDGES_OR_POSITION, NULL), + unexpected_parse_error, rest_args[i]); + r = carg.value.rectangle; + poppler_page_get_size (page, &width, &height); + + /* Translate Gravity and maybe keep the width and height. */ + if (r.x2 < 0) + area->x2 += (r.x1 * width) - area->x1; + else + area->x2 = r.x2 * width; + + if (r.y2 < 0) + area->y1 -= (r.y1 * height) - (height - area->y2); + else + area->y1 = height - (r.y2 * height); + + area->x1 = r.x1 * width; + area->y2 = height - (r.y1 * height); + + xpoppler_annot_set_rectangle (pa, area); + } + else if (! strcmp (key, "label")) + { + PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_STRING, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_markup_set_label (ma, carg.value.string); + } + else if (! strcmp (key, "opacity")) + { + PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_EDGE, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_markup_set_opacity (ma, carg.value.edge); + } + else if (! strcmp (key, "popup")) + { + PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_EDGES, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_markup_set_popup (ma, &carg.value.rectangle); + } + else if (! strcmp (key, "popup-is-open")) + { + PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_BOOL, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_markup_set_popup_is_open (ma, carg.value.flag); + } + else if (! strcmp (key, "icon")) + { + PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_STRING, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_text_set_icon (ta, carg.value.string); + } + else if (! strcmp (key, "is-open")) + { + PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, + ARG_BOOL, NULL), + unexpected_parse_error, rest_args[i]); + poppler_annot_text_set_is_open (ta, carg.value.flag); + } + else + { + perror_if_not (0, "internal error: annotation property validation failed"); + } + } + + OK_BEGIN (); + annotation_print (a, page); + OK_END (); + + error: + if (error_msg) g_free (error_msg); + if (page) g_object_unref (page); +} + +const command_arg_type_t cmd_save_spec[] = + { + ARG_DOC, + }; + +static void +cmd_save (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args->value.doc; + char *filename = mktempfile (); + GError *gerror = NULL; + gchar *uri; + gboolean success = FALSE; + + if (!filename) + { + printf_error_response ("Unable to create temporary file"); + return; + } + + uri = g_filename_to_uri (filename, NULL, &gerror); + + if (uri) + { + success = poppler_document_save (doc->pdf, uri, &gerror); + g_free (uri); + } + if (! success) + { + printf_error_response ("Error while saving %s:%s" + , filename, gerror ? gerror->message : "?"); + if (gerror) + g_error_free (gerror); + return; + } + OK_BEGIN (); + print_response_string (filename, NEWLINE); + OK_END (); +} + +#endif /* HAVE_POPPLER_ANNOT_WRITE */ + + +const command_arg_type_t cmd_synctex_forward_search_spec[] = + { + ARG_DOC, + ARG_NONEMPTY_STRING, /* source file */ + ARG_NATNUM, /* line number */ + ARG_NATNUM /* column number */ + }; + +static void +cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args[0].value.doc; + const char *source = args[1].value.string; + int line = args[2].value.natnum; + int column = args[3].value.natnum; + synctex_scanner_p scanner = NULL; + synctex_node_p node; + float x1, y1, x2, y2; + PopplerPage *page = NULL; + double width, height; + int pn; + + scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); + perror_if_not (scanner, "Unable to create synctex scanner,\ + did you run latex with `--synctex=1' ?"); + + perror_if_not (synctex_display_query (scanner, source, line, column, 0) + && (node = synctex_scanner_next_result (scanner)), + "Destination not found"); + + pn = synctex_node_page (node); + page = poppler_document_get_page(doc->pdf, pn - 1); + perror_if_not (page, "Page not found"); + x1 = synctex_node_box_visible_h (node); + y1 = synctex_node_box_visible_v (node) + - synctex_node_box_visible_height (node); + x2 = synctex_node_box_visible_width (node) + x1; + y2 = synctex_node_box_visible_depth (node) + + synctex_node_box_visible_height (node) + y1; + poppler_page_get_size (page, &width, &height); + x1 /= width; + y1 /= height; + x2 /= width; + y2 /= height; + + OK_BEGIN (); + printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2); + OK_END (); + + error: + if (page) g_object_unref (page); + if (scanner) synctex_scanner_free (scanner); +} + + +const command_arg_type_t cmd_synctex_backward_search_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_EDGE, /* x */ + ARG_EDGE /* y */ + }; + +static void +cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args[0].value.doc; + int pn = args[1].value.natnum; + double x = args[2].value.edge; + double y = args[3].value.edge; + synctex_scanner_p scanner = NULL; + const char *filename; + PopplerPage *page = NULL; + synctex_node_p node; + double width, height; + int line, column; + + scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); + perror_if_not (scanner, "Unable to create synctex scanner,\ + did you run latex with `--synctex=1' ?"); + + page = poppler_document_get_page(doc->pdf, pn - 1); + perror_if_not (page, "Page not found"); + poppler_page_get_size (page, &width, &height); + x = x * width; + y = y * height; + + if (! synctex_edit_query (scanner, pn, x, y) + || ! (node = synctex_scanner_next_result (scanner)) + || ! (filename = + synctex_scanner_get_name (scanner, synctex_node_tag (node)))) + { + printf_error_response ("Destination not found"); + goto error; + } + + line = synctex_node_line (node); + column = synctex_node_column (node); + + OK_BEGIN (); + print_response_string (filename, COLON); + printf("%d:%d\n", line, column); + OK_END (); + + error: + if (page) g_object_unref (page); + if (scanner) synctex_scanner_free (scanner); +} + + +const command_arg_type_t cmd_renderpage_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_NATNUM, /* width */ + ARG_REST, /* commands */ + }; + +static void +cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args[0].value.doc; + int pn = args[1].value.natnum; + int width = args[2].value.natnum; + int nrest_args = args[3].value.rest.nargs; + char * const *rest_args = args[3].value.rest.args; + PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); + cairo_surface_t *surface = NULL; + cairo_t *cr = NULL; + command_arg_t rest_arg; + gchar *error_msg = NULL; + double pt_width, pt_height; + PopplerColor fg = { 0, 0, 0 }; + PopplerColor bg = { 65535, 0, 0 }; + double alpha = 1.0; + double line_width = 1.5; + PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0}; + int i = 0; + + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &pt_width, &pt_height); + surface = image_render_page (doc->pdf, page, width, 1, + &doc->options.render); + perror_if_not (surface, "Failed to render page %d", pn); + + if (! nrest_args) + goto theend; + + cr = cairo_create (surface); + cairo_scale (cr, width / pt_width, width / pt_width); + + while (i < nrest_args) + { + const char* keyword; + + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, + ARG_STRING, &error_msg), + "%s", error_msg); + keyword = rest_arg.value.string; + ++i; + + perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument", + keyword); + + if (! strcmp (keyword, ":foreground") + || ! strcmp (keyword, ":background")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, + ARG_COLOR, &error_msg), + "%s", error_msg); + ++i; + if (! strcmp (keyword, ":foreground")) + fg = rest_arg.value.color; + else + bg = rest_arg.value.color; + + } + else if (! strcmp (keyword, ":alpha")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, + ARG_EDGE, &error_msg), + "%s", error_msg); + ++i; + alpha = rest_arg.value.edge; + } + else if (! strcmp (keyword, ":crop-to") + || ! strcmp (keyword, ":highlight-region") + || ! strcmp (keyword, ":highlight-text") + || ! strcmp (keyword, ":highlight-line")) + { + PopplerRectangle *r; + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, + ARG_EDGES, &error_msg), + "%s", error_msg); + + ++i; + r = &rest_arg.value.rectangle; + + if (! strcmp (keyword, ":crop-to")) + { + gdouble w = (cb.x2 - cb.x1); + gdouble h = (cb.y2 - cb.y1); + gdouble x1 = cb.x1; + gdouble y1 = cb.y1; + + cb.x1 = r->x1 * w + x1; + cb.x2 = r->x2 * w + x1; + cb.y1 = r->y1 * h + y1; + cb.y2 = r->y2 * h + y1; + + } + else + { + r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1; + r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1; + r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1; + r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1; + + if (! strcmp (keyword, ":highlight-region")) + { + const double deg = M_PI / 180.0; + double rad; + + r->x1 += line_width / 2; + r->x2 -= line_width / 2; + r->y1 += line_width / 2; + r->y2 -= line_width / 2; + + rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0); + + cairo_move_to (cr, r->x1 , r->y1 + rad); + cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg); + cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg); + cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg); + cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg); + cairo_close_path (cr); + + cairo_set_source_rgba (cr, + bg.red / 65535.0, + bg.green / 65535.0, + bg.blue / 65535.0, alpha); + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, + fg.red / 65535.0, + fg.green / 65535.0, + fg.blue / 65535.0, 1.0); + cairo_set_line_width (cr, line_width); + cairo_stroke (cr); + } + else + { + gboolean is_single_line = ! strcmp (keyword, ":highlight-line"); + + if (is_single_line) + { + gdouble m = r->y1 + (r->y2 - r->y1) / 2; + + /* Make the rectangle flat, otherwise poppler frequently + renders neighboring lines.*/ + r->y1 = m; + r->y2 = m; + } + + poppler_page_render_selection (page, cr, r, NULL, + POPPLER_SELECTION_GLYPH, &fg, &bg); + } + } + } + else + perror_if_not (0, "Unknown render command: %s", keyword); + } + if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1) + { + int height = cairo_image_surface_get_height (surface); + cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5), + (int) (height * cb.y1 + 0.5), + (int) (width * (cb.x2 - cb.x1) + 0.5), + (int) (height * (cb.y2 - cb.y1) + 0.5)}; + cairo_surface_t *nsurface = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height); + perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS, + "%s", "Failed to create cairo surface"); + cairo_destroy (cr); + cr = cairo_create (nsurface); + perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS, + "%s", "Failed to create cairo context"); + cairo_set_source_surface (cr, surface, -r.x, -r.y); + cairo_paint (cr); + cairo_surface_destroy (surface); + surface = nsurface; + } + + theend: + image_write_print_response (surface, PNG); + + error: + if (error_msg) g_free (error_msg); + if (cr) cairo_destroy (cr); + if (surface) cairo_surface_destroy (surface); + if (page) g_object_unref (page); +} + +const command_arg_type_t cmd_boundingbox_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + /* region */ + }; + +static void +cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args[0].value.doc; + int pn = args[1].value.natnum; + PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); + cairo_surface_t *surface = NULL; + int width, height; + double pt_width, pt_height; + unsigned char *data, *data_p; + PopplerRectangle bbox; + int i, j; + + perror_if_not (page, "No such page %d", pn); + poppler_page_get_size (page, &pt_width, &pt_height); + surface = image_render_page (doc->pdf, page, (int) pt_width, 1, + &doc->options.render); + + perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS, + "Failed to render page"); + + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_height (surface); + data = cairo_image_surface_get_data (surface); + + /* Determine the bbox by comparing each pixel in the 4 corner + stripes with the origin. */ + for (i = 0; i < width; ++i) + { + data_p = data + 4 * i; + for (j = 0; j < height; ++j, data_p += 4 * width) + { + if (! ARGB_EQUAL (data, data_p)) + break; + } + if (j < height) + break; + } + bbox.x1 = i; + + for (i = width - 1; i > -1; --i) + { + data_p = data + 4 * i; + for (j = 0; j < height; ++j, data_p += 4 * width) + { + if (! ARGB_EQUAL (data, data_p)) + break; + } + if (j < height) + break; + } + bbox.x2 = i + 1; + + for (i = 0; i < height; ++i) + { + data_p = data + 4 * i * width; + for (j = 0; j < width; ++j, data_p += 4) + { + if (! ARGB_EQUAL (data, data_p)) + break; + } + if (j < width) + break; + } + bbox.y1 = i; + + for (i = height - 1; i > -1; --i) + { + data_p = data + 4 * i * width; + for (j = 0; j < width; ++j, data_p += 4) + { + if (! ARGB_EQUAL (data, data_p)) + break; + } + if (j < width) + break; + } + bbox.y2 = i + 1; + + OK_BEGIN (); + if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2) + { + /* empty page */ + puts ("0:0:1:1"); + } + else + { + printf ("%f:%f:%f:%f\n", + bbox.x1 / width, + bbox.y1 / height, + bbox.x2 / width, + bbox.y2 / height); + } + OK_END (); + + error: + if (surface) cairo_surface_destroy (surface); + if (page) g_object_unref (page); +} + +const command_arg_type_t cmd_charlayout_spec[] = + { + ARG_DOC, + ARG_NATNUM, /* page number */ + ARG_EDGES_OR_POSITION, /* region or position */ + }; + +static void +cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int pn = args[1].value.natnum; + PopplerRectangle region = args[2].value.rectangle; + double width, height; + PopplerPage *page = poppler_document_get_page(doc, pn - 1); + char *text = NULL; + char *text_p; + PopplerRectangle *rectangles = NULL; + guint nrectangles; + int i; + gboolean have_position = region.y2 < 0; + + perror_if_not (page, "No such page %d", pn); + + text = poppler_page_get_text (page); + text_p = text; + poppler_page_get_text_layout (page, &rectangles, &nrectangles); + poppler_page_get_size (page, &width, &height); + region.x1 *= width; + region.x2 *= width; + region.y1 *= height; + region.y2 *= height; + + OK_BEGIN (); + for (i = 0; i < nrectangles && *text_p; ++i) + { + PopplerRectangle *r = &rectangles[i]; + char *nextc = g_utf8_offset_to_pointer (text_p, 1); + + if ((have_position + && region.x1 >= r->x1 + && region.x1 <= r->x2 + && region.y1 >= r->y1 + && region.y1 <= r->y2) + || (! have_position + && r->x1 >= region.x1 + && r->y1 >= region.y1 + && r->x2 <= region.x2 + && r->y2 <= region.y2)) + { + char endc = *nextc; + + printf ("%f %f %f %f:", + r->x1 / width, r->y1 / height, + r->x2 / width, r->y2 / height); + *nextc = '\0'; + print_response_string (text_p, NEWLINE); + *nextc = endc; + } + text_p = nextc; + } + OK_END (); + + g_free (rectangles); + g_object_unref (page); + g_free (text); + + error: + return; +} + +const document_option_t document_options [] = + { + DEC_DOPT (":render/usecolors", ARG_BOOL, render.usecolors), + DEC_DOPT (":render/printed", ARG_BOOL, render.printed), + DEC_DOPT (":render/foreground", ARG_COLOR, render.fg), + DEC_DOPT (":render/background", ARG_COLOR, render.bg), + }; + +const command_arg_type_t cmd_getoptions_spec[] = + { + ARG_DOC, + }; + +static void +cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args) +{ + document_t *doc = args[0].value.doc; + int i; + OK_BEGIN (); + for (i = 0; i < G_N_ELEMENTS (document_options); ++i) + { + command_arg_t arg; + + arg.type = document_options[i].type; + memcpy (&arg.value, + ((char*) &doc->options) + document_options[i].offset, + command_arg_type_size (arg.type)); + print_response_string (document_options[i].name, COLON); + command_arg_print (&arg); + puts(""); + } + OK_END (); +} + +const command_arg_type_t cmd_setoptions_spec[] = + { + ARG_DOC, + ARG_REST /* key value pairs */ + }; + +static void +cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args) +{ + int i = 0; + document_t *doc = args[0].value.doc; + int nrest = args[1].value.rest.nargs; + char * const *rest = args[1].value.rest.args; + gchar *error_msg = NULL; + document_options_t opts = doc->options; + const size_t nopts = G_N_ELEMENTS (document_options); + + perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected"); + + while (i < nrest) + { + int j; + command_arg_t key, value; + + perror_if_not (command_arg_parse_arg + (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg), + "%s", error_msg); + + ++i; + for (j = 0; j < nopts; ++j) + { + const document_option_t *dopt = &document_options[j]; + if (! strcmp (key.value.string, dopt->name)) + { + perror_if_not (command_arg_parse_arg + (ctx, rest[i], &value, dopt->type, &error_msg), + "%s", error_msg); + memcpy (((char*) &opts) + dopt->offset, + &value.value, command_arg_type_size (value.type)); + break; + } + } + perror_if_not (j < nopts, "Unknown option: %s", key.value.string); + ++i; + } + doc->options = opts; + cmd_getoptions (ctx, args); + + error: + if (error_msg) g_free (error_msg); +} + +const command_arg_type_t cmd_pagelabels_spec[] = + { + ARG_DOC, + }; + +static void +cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args) +{ + PopplerDocument *doc = args[0].value.doc->pdf; + int i; + + OK_BEGIN (); + for (i = 0; i < poppler_document_get_n_pages (doc); ++i) + { + PopplerPage *page = poppler_document_get_page(doc, i); + gchar *label = poppler_page_get_label (page); + + print_response_string (label ? label : "", NEWLINE); + g_object_unref (page); + g_free (label); + } + OK_END (); +} + +const command_arg_type_t cmd_ping_spec[] = + { + ARG_STRING /* any message */ + }; + +static void +cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args) +{ + const gchar *msg = args[0].value.string; + OK_BEGIN (); + print_response_string (msg, NEWLINE); + OK_END (); +} + + +/* ================================================================== * + * Main + * ================================================================== */ + +static const command_t commands [] = + { + /* Basic */ + DEC_CMD (ping), + DEC_CMD (features), + DEC_CMD (open), + DEC_CMD (close), + DEC_CMD (quit), + DEC_CMD (getoptions), + DEC_CMD (setoptions), + + /* Searching */ + DEC_CMD2 (search_string, "search-string"), + DEC_CMD2 (search_regexp, "search-regexp"), + DEC_CMD2 (regexp_flags, "regexp-flags"), + + /* General Information */ + DEC_CMD (metadata), + DEC_CMD (outline), + DEC_CMD2 (number_of_pages, "number-of-pages"), + DEC_CMD (pagelinks), + DEC_CMD (gettext), + DEC_CMD (getselection), + DEC_CMD (pagesize), + DEC_CMD (boundingbox), + DEC_CMD (charlayout), + + /* General Information */ + DEC_CMD (metadata), + DEC_CMD (outline), + DEC_CMD2 (number_of_pages, "number-of-pages"), + DEC_CMD (pagelinks), + DEC_CMD (gettext), + DEC_CMD (getselection), + DEC_CMD (pagesize), + DEC_CMD (boundingbox), + DEC_CMD (charlayout), + DEC_CMD (pagelabels), + + /* Annotations */ + DEC_CMD (getannots), + DEC_CMD (getannot), +#ifdef HAVE_POPPLER_ANNOT_WRITE + DEC_CMD (addannot), + DEC_CMD (delannot), + DEC_CMD (editannot), + DEC_CMD (save), +#endif + + /* Attachments */ + DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"), + DEC_CMD (getattachments), + + /* Synctex */ + DEC_CMD2 (synctex_forward_search, "synctex-forward-search"), + DEC_CMD2 (synctex_backward_search, "synctex-backward-search"), + + /* Rendering */ + DEC_CMD (renderpage), + }; + +int main(int argc, char **argv) +{ + epdfinfo_t ctx = {0}; + char *line = NULL; + ssize_t read; + size_t line_size; + const char *error_log = "/dev/null"; + +#ifdef __MINGW32__ + error_log = "NUL"; + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + + if (argc > 2) + { + fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n"); + exit (EXIT_FAILURE); + } + if (argc == 2) + error_log = argv[1]; + + if (! freopen (error_log, "a", stderr)) + err (2, "Unable to redirect stderr"); + +#if ! GLIB_CHECK_VERSION(2,36,0) + g_type_init (); +#endif + + ctx.documents = g_hash_table_new (g_str_hash, g_str_equal); + + setvbuf (stdout, NULL, _IOFBF, BUFSIZ); + + while ((read = getline (&line, &line_size, stdin)) != -1) + { + int nargs = 0; + command_arg_t *cmd_args = NULL; + char **args = NULL; + gchar *error_msg = NULL; + int i; + + if (read <= 1 || line[read - 1] != '\n') + { + fprintf (stderr, "Skipped parts of a line: `%s'\n", line); + goto next_line; + } + + line[read - 1] = '\0'; + args = command_arg_split (line, &nargs); + if (nargs == 0) + continue; + + for (i = 0; i < G_N_ELEMENTS (commands); i++) + { + if (! strcmp (commands[i].name, args[0])) + { + if (commands[i].nargs == 0 + || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1, + commands + i, &error_msg))) + { + commands[i].execute (&ctx, cmd_args); + if (commands[i].nargs > 0) + free_command_args (cmd_args, commands[i].nargs); + } + else + { + printf_error_response ("%s", error_msg ? error_msg : + "Unknown error (this is a bug)"); + } + if (error_msg) + g_free (error_msg); + break; + } + } + if (G_N_ELEMENTS (commands) == i) + { + printf_error_response ("Unknown command: %s", args[0]); + } + for (i = 0; i < nargs; ++i) + g_free (args[i]); + g_free (args); + next_line: + free (line); + line = NULL; + } + + if (ferror (stdin)) + err (2, NULL); + exit (EXIT_SUCCESS); +} diff --git a/elpa/pdf-tools-20211110.513/build/server/epdfinfo.h b/elpa/pdf-tools-20211110.513/build/server/epdfinfo.h @@ -0,0 +1,252 @@ +// Copyright (C) 2013, 2014 Andreas Politz + +// Author: Andreas Politz <politza@fh-trier.de> + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#ifndef _EPDF_H_ +#define _EPDF_H_ _EPDF_H_ +#include "config.h" +#include <glib.h> +#include <poppler.h> +#include <png.h> + +/* Some library functions print warnings to stdout, inhibit it. */ +#define DISCARD_STDOUT(saved_fd) \ + do { \ + int __fd; \ + fflush(stdout); \ + saved_fd = dup(1); \ + __fd = open("/dev/null", O_WRONLY); \ + dup2(__fd, 1); \ + close(__fd); \ + } while (0) + +#define UNDISCARD_STDOUT(saved_fd) \ + do { \ + fflush(stdout); \ + dup2(saved_fd, 1); \ + close(saved_fd); \ + } while (0) + +/* Writing responses */ +#define OK_BEGIN() \ + do { \ + puts("OK"); \ + } while (0) + +#define OK_END() \ + do { \ + puts("."); \ + fflush (stdout); \ + } while (0) + +#define OK() \ + do { \ + puts ("OK\n."); \ + fflush (stdout); \ + } while (0) + +/* Dealing with image data. */ +#ifdef WORDS_BIGENDIAN +#define ARGB_TO_RGB(rgb, argb) \ + do { \ + rgb[0] = argb[1]; \ + rgb[1] = argb[2]; \ + rgb[2] = argb[3]; \ + } while (0) + +#define ARGB_EQUAL(argb1, argb2) \ + (argb1[1] == argb2[1] \ + && argb1[2] == argb2[2] \ + && argb1[3] == argb2[3]) + +#else +#define ARGB_TO_RGB(rgb, argb) \ + do { \ + rgb[0] = argb[2]; \ + rgb[1] = argb[1]; \ + rgb[2] = argb[0]; \ + } while (0) + +#define ARGB_EQUAL(argb1, argb2) \ + (argb1[0] == argb2[0] \ + && argb1[1] == argb2[1] \ + && argb1[2] == argb2[2]) +#endif + +#define NORMALIZE_PAGE_ARG(doc, first, last) \ + *first = MAX(1, *first); \ + if (*last <= 0) \ + *last = poppler_document_get_n_pages (doc); \ + else \ + *last = MIN(*last, poppler_document_get_n_pages (doc)); + +/* png_jmpbuf is supposed to be not available in older versions of + libpng. */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +#ifndef HAVE_ERROR_H +# define error(status, errno, fmt, args...) \ + do { \ + int error = (errno); \ + fflush (stdout); \ + fprintf (stderr, "%s: " fmt, PACKAGE_NAME, ## args); \ + if (error) \ + fprintf (stderr, ": %s", strerror (error)); \ + fprintf (stderr, "\n"); \ + exit (status); \ + } while (0) +#endif + +#define internal_error(fmt, args...) \ + error (2, 0, "internal error in %s: " fmt, __func__, ## args) + +#define error_if_not(expr) \ + if (! (expr)) goto error; + +#define perror_if_not(expr, fmt, args...) \ + do { \ + if (! (expr)) \ + { \ + printf_error_response ((fmt), ## args); \ + goto error; \ + } \ + } while (0) + +#define cerror_if_not(expr, error_msg, fmt, args...) \ + do { \ + if (! (expr)) \ + { \ + if (error_msg) \ + *(error_msg) = g_strdup_printf((fmt), ## args); \ + goto error; \ + } \ + } while (0) + +/* Declare commands */ +#define DEC_CMD(name) \ + {#name, cmd_ ## name, cmd_ ## name ## _spec, \ + G_N_ELEMENTS (cmd_ ## name ## _spec)} + +#define DEC_CMD2(command, name) \ + {name, cmd_ ## command, cmd_ ## command ## _spec, \ + G_N_ELEMENTS (cmd_ ## command ## _spec)} + +/* Declare option */ +#define DEC_DOPT(name, type, sname) \ + {name, type, offsetof (document_options_t, sname)} + +enum suffix_char { NONE, COLON, NEWLINE}; + +enum image_type { PPM, PNG }; + +typedef struct +{ + PopplerAnnotMapping *amap; + gchar *key; +} annotation_t; + +typedef enum + { + ARG_INVALID = 0, + ARG_DOC, + ARG_BOOL, + ARG_STRING, + ARG_NONEMPTY_STRING, + ARG_NATNUM, + ARG_EDGE, + ARG_EDGE_OR_NEGATIVE, + ARG_EDGES, + ARG_EDGES_OR_POSITION, + ARG_COLOR, + ARG_REST + } command_arg_type_t; + +typedef struct +{ + const char *name; + command_arg_type_t type; + size_t offset; +} document_option_t; + +typedef struct +{ + PopplerColor bg, fg; + gboolean usecolors; + gboolean printed; +} render_options_t; + +typedef struct +{ + render_options_t render; +} document_options_t; + +typedef struct +{ + PopplerDocument *pdf; + char *filename; + char *passwd; + struct + { + GHashTable *keys; /* key => page */ + GList **pages; /* page array */ + } annotations; + document_options_t options; +} document_t; + +typedef struct +{ + command_arg_type_t type; + union + { + gboolean flag; + const char *string; + long natnum; + document_t *doc; + gdouble edge; + PopplerColor color; + PopplerRectangle rectangle; +#ifdef HAVE_POPPLER_ANNOT_MARKUP + PopplerQuadrilateral quadrilateral; +#endif + struct + { + char * const *args; + int nargs; + } rest; + } value; +} command_arg_t; + +typedef struct +{ + GHashTable *documents; +} epdfinfo_t; + +typedef struct +{ + const char *name; + void (* execute) (const epdfinfo_t *ctxt, const command_arg_t *args); + const command_arg_type_t *args_spec; + int nargs; +} command_t; + +/* Defined in poppler-hack.cc */ +#ifdef HAVE_POPPLER_ANNOT_WRITE +extern void xpoppler_annot_set_rectangle (PopplerAnnot*, PopplerRectangle*); +#endif +extern gchar *xpoppler_annot_markup_get_created (PopplerAnnotMarkup*); +#endif /* _EPDF_H_ */ diff --git a/elpa/pdf-tools-20211110.513/build/server/install_test.cpp b/elpa/pdf-tools-20211110.513/build/server/install_test.cpp @@ -0,0 +1,7 @@ +#include <PDFDocEncoding.h> +#include <iostream> + +int main() { + std::cout << "Hello World, pdf-tools!"; + return 0; +} diff --git a/elpa/pdf-tools-20211110.513/build/server/m4/ax_check_compile_flag.m4 b/elpa/pdf-tools-20211110.513/build/server/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/elpa/pdf-tools-20211110.513/build/server/poppler-hack.cc b/elpa/pdf-tools-20211110.513/build/server/poppler-hack.cc @@ -0,0 +1,122 @@ +// Copyright (C) 2013, 2014 Andreas Politz + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include <config.h> +#include <PDFDocEncoding.h> +#include <Annot.h> +#include <glib.h> +#include <glib-object.h> +#include <poppler-features.h> + +extern "C" +{ + +GType poppler_annot_get_type (void) G_GNUC_CONST; +GType poppler_annot_markup_get_type (void) G_GNUC_CONST; + +#define POPPLER_TYPE_ANNOT (poppler_annot_get_type ()) +#define POPPLER_ANNOT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), POPPLER_TYPE_ANNOT, PopplerAnnot)) +#define POPPLER_IS_ANNOT_MARKUP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POPPLER_TYPE_ANNOT_MARKUP)) +#define POPPLER_TYPE_ANNOT_MARKUP (poppler_annot_markup_get_type ()) + +#if POPPLER_CHECK_VERSION(0,72,0) +#define GET_CSTR c_str +#else +#define GET_CSTR getCString +#endif + + struct PopplerAnnot + { + GObject parent_instance; + Annot *annot; + }; + + struct PopplerAnnotMarkup + { + GObject parent_instance; + }; + + struct PopplerRectangle + { + double x1; + double y1; + double x2; + double y2; + }; + + // This function does not modify its argument s, but for + // compatibility reasons (e.g. getLength in GooString.h before 2015) + // with older poppler code, it can't be declared as such. + char *_xpoppler_goo_string_to_utf8(/* const */ GooString *s) + { + char *result; + + if (! s) + return NULL; + + if (s->hasUnicodeMarker()) { + result = g_convert (s->GET_CSTR () + 2, + s->getLength () - 2, + "UTF-8", "UTF-16BE", NULL, NULL, NULL); + } else { + int len; + gunichar *ucs4_temp; + int i; + + len = s->getLength (); + ucs4_temp = g_new (gunichar, len + 1); + for (i = 0; i < len; ++i) { + ucs4_temp[i] = pdfDocEncoding[(unsigned char)s->getChar(i)]; + } + ucs4_temp[i] = 0; + + result = g_ucs4_to_utf8 (ucs4_temp, -1, NULL, NULL, NULL); + + g_free (ucs4_temp); + } + + return result; + } +#ifdef HAVE_POPPLER_ANNOT_WRITE + // Set the rectangle of an annotation. It was first added in v0.26. + void xpoppler_annot_set_rectangle (PopplerAnnot *a, PopplerRectangle *rectangle) + { + GooString *state = (GooString*) a->annot->getAppearState (); + char *ustate = _xpoppler_goo_string_to_utf8 (state); + + a->annot->setRect (rectangle->x1, rectangle->y1, + rectangle->x2, rectangle->y2); + a->annot->setAppearanceState (ustate); + g_free (ustate); + } +#endif + // This function is in the library, but the enforced date parsing is + // incomplete (at least in some versions), because it ignores the + // timezone. + gchar *xpoppler_annot_markup_get_created (PopplerAnnotMarkup *poppler_annot) + { + AnnotMarkup *annot; + GooString *text; + + g_return_val_if_fail (POPPLER_IS_ANNOT_MARKUP (poppler_annot), NULL); + + annot = static_cast<AnnotMarkup *>(POPPLER_ANNOT (poppler_annot)->annot); + text = (GooString*) annot->getDate (); + + return text ? _xpoppler_goo_string_to_utf8 (text) : NULL; + } +} diff --git a/elpa/pdf-tools-20211110.513/build/server/poppler-versions b/elpa/pdf-tools-20211110.513/build/server/poppler-versions @@ -0,0 +1,12 @@ +HAVE_POPPLER_ANNOT_WRITE +0.19.4 solves bug 49080, which potentially corrupts PDF files. + +HAVE_POPPLER_FIND_OPTS +0.22 PopplerFindFlags +0.22 poppler_page_find_text_with_options + +HAVE_POPPLER_ANNOT_SET_RECT +0.26 Adds function poppler_annot_set_rectangle + +HAVE_POPPLER_ANNOT_MARKUP +0.26 poppler_annot_text_markup_new_{highlight,squiggly,strikeout,underline} diff --git a/elpa/pdf-tools-20211110.513/build/server/synctex_parser.c b/elpa/pdf-tools-20211110.513/build/server/synctex_parser.c @@ -0,0 +1,8924 @@ +/* + Copyright (c) 2008-2017 jerome DOT laurens AT u-bourgogne DOT fr + + This file is part of the __SyncTeX__ package. + + [//]: # (Latest Revision: Sun Oct 15 15:09:55 UTC 2017) + [//]: # (Version: 1.21) + + See `synctex_parser_readme.md` for more details + + ## License + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE + + Except as contained in this notice, the name of the copyright holder + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization from the copyright holder. + + Acknowledgments: + ---------------- + The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh, + and significant help from XeTeX developer Jonathan Kew + + Nota Bene: + ---------- + If you include or use a significant part of the synctex package into a software, + I would appreciate to be listed as contributor and see "SyncTeX" highlighted. + + */ + +/* We assume that high level application like pdf viewers will want + * to embed this code as is. We assume that they also have locale.h and setlocale. + * For other tools such as TeXLive tools, you must define SYNCTEX_USE_LOCAL_HEADER, + * when building. You also have to create and customize synctex_parser_local.h to fit your system. + * In particular, the HAVE_LOCALE_H and HAVE_SETLOCALE macros should be properly defined. + * With this design, you should not need to edit this file. */ + +/** + * \file synctex_parser.c + * \brief SyncTeX file parser and controller. + * - author: Jérôme LAURENS + * \version 1.21 + * \date Sun Oct 15 15:09:55 UTC 2017 + * + * Reads and parse *.synctex[.gz] files, + * performs edit and display queries. + * + * See + * - synctex_scanner_new_with_output_file + * - synctex_scanner_parse + * - synctex_scanner_free + * - synctex_display_query + * - synctex_edit_query + * - synctex_scanner_next_result + * - synctex_scanner_reset_result + * + * The data is organized in a graph with multiple entries. + * The root object is a scanner, it is created with the contents on a synctex file. + * Each node of the tree is a synctex_node_t object. + * There are 3 subtrees, two of them sharing the same leaves. + * The first tree is the list of input records, where input file names are associated with tags. + * The second tree is the box tree as given by TeX when shipping pages out. + * First level objects are sheets and forms, containing boxes, glues, kerns... + * The third tree allows to browse leaves according to tag and line. + */ +# if defined(SYNCTEX_USE_LOCAL_HEADER) +# include "synctex_parser_local.h" +# else +# define HAVE_LOCALE_H 1 +# define HAVE_SETLOCALE 1 +# if defined(_MSC_VER) +# define SYNCTEX_INLINE __inline +# else +# define SYNCTEX_INLINE inline +# endif +# endif + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#if defined(HAVE_LOCALE_H) +#include <locale.h> +#endif + +/* Mark unused parameters, so that there will be no compile warnings. */ +#ifdef __DARWIN_UNIX03 +# define SYNCTEX_UNUSED(x) SYNCTEX_PRAGMA(unused(x)) +# define SYNCTEX_PRAGMA(x) _Pragma ( #x ) +#else +# define SYNCTEX_UNUSED(x) (void)(x); +#endif + +#include "synctex_parser_advanced.h" + +SYNCTEX_INLINE static int _synctex_abs(int x) { + return x>0? x: -x; +} +/* These are the possible extensions of the synctex file */ +const char * synctex_suffix = ".synctex"; +const char * synctex_suffix_gz = ".gz"; + +typedef synctex_node_p(*synctex_node_new_f)(synctex_scanner_p); +typedef void(*synctex_node_fld_f)(synctex_node_p); +typedef char *(*synctex_node_str_f)(synctex_node_p); + +/** + * Pseudo class. + * - author: J. Laurens + * + * Each nodes has a class, it is therefore called an object. + * Each class has a unique scanner. + * Each class has a type which is a unique identifier. + * The class points to various methods, + * each of them vary amongst objects. + * Each class has a data model which stores node's attributes. + * Each class has an tree model which stores children and parent. + * Inspectors give access to data and tree elements. + */ + +/* 8 fields + size: spcflnat */ +typedef struct synctex_tree_model_t { + int sibling; + int parent; + int child; + int friend; + int last; + int next_hbox; + int arg_sibling; + int target; + int size; +} synctex_tree_model_s; +typedef const synctex_tree_model_s * synctex_tree_model_p; + +typedef struct synctex_data_model_t { + int tag; + int line; + int column; + int h; + int v; + int width; + int height; + int depth; + int mean_line; + int weight; + int h_V; + int v_V; + int width_V; + int height_V; + int depth_V; + int name; + int page; + int size; +} synctex_data_model_s; + +typedef const synctex_data_model_s * synctex_data_model_p; + +typedef int (*synctex_int_getter_f)(synctex_node_p); +typedef struct synctex_tlcpector_t { + synctex_int_getter_f tag; + synctex_int_getter_f line; + synctex_int_getter_f column; +} synctex_tlcpector_s; +typedef const synctex_tlcpector_s * synctex_tlcpector_p; +static int _synctex_int_none(synctex_node_p node) { + SYNCTEX_UNUSED(node) + return 0; +} +static const synctex_tlcpector_s synctex_tlcpector_none = { + &_synctex_int_none, /* tag */ + &_synctex_int_none, /* line */ + &_synctex_int_none, /* column */ +}; + +typedef struct synctex_inspector_t { + synctex_int_getter_f h; + synctex_int_getter_f v; + synctex_int_getter_f width; + synctex_int_getter_f height; + synctex_int_getter_f depth; +} synctex_inspector_s; +typedef const synctex_inspector_s * synctex_inspector_p; +static const synctex_inspector_s synctex_inspector_none = { + &_synctex_int_none, /* h */ + &_synctex_int_none, /* v */ + &_synctex_int_none, /* width */ + &_synctex_int_none, /* height */ + &_synctex_int_none, /* depth */ +}; + +typedef float (*synctex_float_getter_f)(synctex_node_p); +typedef struct synctex_vispector_t { + synctex_float_getter_f h; + synctex_float_getter_f v; + synctex_float_getter_f width; + synctex_float_getter_f height; + synctex_float_getter_f depth; +} synctex_vispector_s; +static float _synctex_float_none(synctex_node_p node) { + SYNCTEX_UNUSED(node) + return 0; +} +static const synctex_vispector_s synctex_vispector_none = { + &_synctex_float_none, /* h */ + &_synctex_float_none, /* v */ + &_synctex_float_none, /* width */ + &_synctex_float_none, /* height */ + &_synctex_float_none, /* depth */ +}; +typedef const synctex_vispector_s * synctex_vispector_p; + +struct synctex_class_t { + synctex_scanner_p scanner; + synctex_node_type_t type; + synctex_node_new_f new; + synctex_node_fld_f free; + synctex_node_fld_f log; + synctex_node_fld_f display; + synctex_node_str_f abstract; + synctex_tree_model_p navigator; + synctex_data_model_p modelator; + synctex_tlcpector_p tlcpector; + synctex_inspector_p inspector; + synctex_vispector_p vispector; +}; + +/** + * Nota bene: naming convention. + * For static API, when the name contains "proxy", it applies to proxies. + * When the name contains "noxy", it applies to non proxies only. + * When the name contains "node", well it depends... + */ + +typedef synctex_node_p synctex_proxy_p; +typedef synctex_node_p synctex_noxy_p; + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Abstract OBJECTS and METHODS +# endif + +/** + * \def SYNCTEX_MSG_SEND + * \brief Takes care of sending the given message if possible. + * - parameter NODE: of type synctex_node_p + * - parameter SELECTOR: one of the class_ pointer properties + */ +# define SYNCTEX_MSG_SEND(NODE,SELECTOR) do {\ + synctex_node_p N__ = NODE;\ + if (N__ && N__->class_->SELECTOR) {\ + (*(N__->class_->SELECTOR))(N__);\ + }\ +} while (synctex_NO) + +/** + * Free the given node by sending the free message. + * - parameter NODE: of type synctex_node_p + */ +void synctex_node_free(synctex_node_p node) { + SYNCTEX_MSG_SEND(node,free); +} +# if defined(SYNCTEX_TESTING) +# if !defined(SYNCTEX_USE_HANDLE) +# define SYNCTEX_USE_HANDLE 1 +# endif +# if !defined(SYNCTEX_USE_CHARINDEX) +# define SYNCTEX_USE_CHARINDEX 1 +# endif +# endif +SYNCTEX_INLINE static synctex_node_p _synctex_new_handle_with_target(synctex_node_p target); +# if defined(SYNCTEX_USE_HANDLE) +# define SYNCTEX_SCANNER_FREE_HANDLE(SCANR) \ +__synctex_scanner_free_handle(SCANR) +# define SYNCTEX_SCANNER_REMOVE_HANDLE_TO(WHAT) \ +__synctex_scanner_remove_handle_to(WHAT) +# define SYNCTEX_REGISTER_HANDLE_TO(NODE) \ +__synctex_scanner_register_handle_to(NODE) +# else +# define SYNCTEX_SCANNER_FREE_HANDLE(SCANR) +# define SYNCTEX_SCANNER_REMOVE_HANDLE_TO(WHAT) +# define SYNCTEX_REGISTER_HANDLE_TO(NODE) +# endif + +# if defined(SYNCTEX_USE_CHARINDEX) +# define SYNCTEX_CHARINDEX(NODE) (NODE->char_index) +# define SYNCTEX_LINEINDEX(NODE) (NODE->line_index) +# define SYNCTEX_PRINT_CHARINDEX_FMT "#%i" +# define SYNCTEX_PRINT_CHARINDEX_WHAT ,SYNCTEX_CHARINDEX(node) +# define SYNCTEX_PRINT_CHARINDEX \ + printf(SYNCTEX_PRINT_CHARINDEX_FMT SYNCTEX_PRINT_CHARINDEX_WHAT) +# define SYNCTEX_PRINT_LINEINDEX_FMT "L#%i" +# define SYNCTEX_PRINT_LINEINDEX_WHAT ,SYNCTEX_LINEINDEX(node) +# define SYNCTEX_PRINT_LINEINDEX \ + printf(SYNCTEX_PRINT_LINEINDEX_FMT SYNCTEX_PRINT_LINEINDEX_WHAT) +# define SYNCTEX_PRINT_CHARINDEX_NL \ + printf(SYNCTEX_PRINT_CHARINDEX_FMT "\n" SYNCTEX_PRINT_CHARINDEX_WHAT) +# define SYNCTEX_PRINT_LINEINDEX_NL \ + printf(SYNCTEX_PRINT_CHARINDEX_FMT "\n"SYNCTEX_PRINT_LINEINDEX_WHAT) +# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION)\ + NODE->char_index = (synctex_charindex_t)(scanner->reader->charindex_offset+SYNCTEX_CUR-SYNCTEX_START+(CORRECTION)); \ + NODE->line_index = scanner->reader->line_number; +# else +# define SYNCTEX_CHARINDEX(NODE) 0 +# define SYNCTEX_LINEINDEX(NODE) 0 +# define SYNCTEX_PRINT_CHARINDEX_FMT +# define SYNCTEX_PRINT_CHARINDEX_WHAT +# define SYNCTEX_PRINT_CHARINDEX +# define SYNCTEX_PRINT_CHARINDEX +# define SYNCTEX_PRINT_LINEINDEX_FMT +# define SYNCTEX_PRINT_LINEINDEX_WHAT +# define SYNCTEX_PRINT_LINEINDEX +# define SYNCTEX_PRINT_CHARINDEX_NL printf("\n") +# define SYNCTEX_PRINT_LINEINDEX_NL printf("\n") +# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) +# endif + +/** + * The next macros are used to access the node tree info + * SYNCTEX_DATA(node) points to the first synctex integer or pointer data of node + * SYNCTEX_DATA(node)[index] is the information at index + * for example, the page of a sheet is stored in SYNCTEX_DATA(sheet)[_synctex_data_page_idx] + * - parameter NODE: of type synctex_node_p + * If the name starts with "__", the argument is nonullable + */ +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Tree SETGET +# endif + +#if SYNCTEX_DEBUG > 1000 +#define SYNCTEX_PARAMETER_ASSERT(WHAT) \ + do { \ + if (!(WHAT)) { \ + printf("! Parameter failure: %s\n",#WHAT); \ + } \ + } while (synctex_NO) +#define DEFINE_SYNCTEX_TREE_HAS(WHAT)\ +static synctex_bool_t _synctex_tree_has_##WHAT(synctex_node_p node) {\ + if (node) {\ + if (node->class_->navigator->WHAT>=0) {\ + return synctex_YES; \ + } else {\ + printf("WARNING: NO tree %s for %s\n", #WHAT, synctex_node_isa(node));\ + }\ + }\ + return synctex_NO;\ +} +#else +#define SYNCTEX_PARAMETER_ASSERT(WHAT) +#define DEFINE_SYNCTEX_TREE_HAS(WHAT) \ +static synctex_bool_t _synctex_tree_has_##WHAT(synctex_node_p node) {\ + return (node && (node->class_->navigator->WHAT>=0));\ +} +#endif + +# define DEFINE_SYNCTEX_TREE__GET(WHAT) \ +SYNCTEX_INLINE static synctex_node_p __synctex_tree_##WHAT(synctex_non_null_node_p node) {\ + return node->data[node->class_->navigator->WHAT].as_node;\ +} +# define DEFINE_SYNCTEX_TREE_GET(WHAT) \ +DEFINE_SYNCTEX_TREE__GET(WHAT) \ +static synctex_node_p _synctex_tree_##WHAT(synctex_node_p node) {\ + if (_synctex_tree_has_##WHAT(node)) {\ + return __synctex_tree_##WHAT(node);\ + }\ + return 0;\ +} +# define DEFINE_SYNCTEX_TREE__RESET(WHAT) \ +SYNCTEX_INLINE static synctex_node_p __synctex_tree_reset_##WHAT(synctex_non_null_node_p node) {\ + synctex_node_p old = node->data[node->class_->navigator->WHAT].as_node;\ + node->data[node->class_->navigator->WHAT].as_node=NULL;\ + return old;\ +} +# define DEFINE_SYNCTEX_TREE_RESET(WHAT) \ +DEFINE_SYNCTEX_TREE__RESET(WHAT) \ +SYNCTEX_INLINE static synctex_node_p _synctex_tree_reset_##WHAT(synctex_node_p node) {\ + return _synctex_tree_has_##WHAT(node)? \ + __synctex_tree_reset_##WHAT(node): NULL; \ +} +# define DEFINE_SYNCTEX_TREE__SET(WHAT) \ +SYNCTEX_INLINE static synctex_node_p __synctex_tree_set_##WHAT(synctex_non_null_node_p node, synctex_node_p new_value) {\ + synctex_node_p old = __synctex_tree_##WHAT(node);\ + node->data[node->class_->navigator->WHAT].as_node=new_value;\ + return old;\ +} +# define DEFINE_SYNCTEX_TREE_SET(WHAT) \ +DEFINE_SYNCTEX_TREE__SET(WHAT) \ +SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_##WHAT(synctex_node_p node, synctex_node_p new_value) {\ + return _synctex_tree_has_##WHAT(node)?\ + __synctex_tree_set_##WHAT(node,new_value):NULL;\ +} +# define DEFINE_SYNCTEX_TREE__GETSETRESET(WHAT) \ +DEFINE_SYNCTEX_TREE__GET(WHAT) \ +DEFINE_SYNCTEX_TREE__SET(WHAT) \ +DEFINE_SYNCTEX_TREE__RESET(WHAT) + +# define DEFINE_SYNCTEX_TREE_GETSET(WHAT) \ +DEFINE_SYNCTEX_TREE_HAS(WHAT) \ +DEFINE_SYNCTEX_TREE_GET(WHAT) \ +DEFINE_SYNCTEX_TREE_SET(WHAT) + +# define DEFINE_SYNCTEX_TREE_GETRESET(WHAT) \ +DEFINE_SYNCTEX_TREE_HAS(WHAT) \ +DEFINE_SYNCTEX_TREE_GET(WHAT) \ +DEFINE_SYNCTEX_TREE_RESET(WHAT) + +# define DEFINE_SYNCTEX_TREE_GETSETRESET(WHAT) \ +DEFINE_SYNCTEX_TREE_HAS(WHAT) \ +DEFINE_SYNCTEX_TREE_GET(WHAT) \ +DEFINE_SYNCTEX_TREE_SET(WHAT) \ +DEFINE_SYNCTEX_TREE_RESET(WHAT) + +/* + * _synctex_tree_set_... methods return the old value. + * The return value of _synctex_tree_set_child and + * _synctex_tree_set_sibling must be released somehown. + */ +DEFINE_SYNCTEX_TREE__GETSETRESET(sibling) +DEFINE_SYNCTEX_TREE_GETSETRESET(parent) +DEFINE_SYNCTEX_TREE_GETSETRESET(child) +DEFINE_SYNCTEX_TREE_GETSETRESET(friend) +DEFINE_SYNCTEX_TREE_GETSET(last) +DEFINE_SYNCTEX_TREE_GETSET(next_hbox) +DEFINE_SYNCTEX_TREE_GETSET(arg_sibling) +DEFINE_SYNCTEX_TREE_GETSETRESET(target) + +#if SYNCTEX_DEBUG>1000 +# undef SYNCTEX_USE_NODE_COUNT +# define SYNCTEX_USE_NODE_COUNT 1 +#endif +#if SYNCTEX_USE_NODE_COUNT>0 +# define SYNCTEX_DECLARE_NODE_COUNT int node_count; +# define SYNCTEX_INIT_NODE_COUNT \ + do { node_count = 0; } while(synctex_NO) +#else +# define SYNCTEX_DECLARE_NODE_COUNT +# define SYNCTEX_INIT_NODE_COUNT +#endif + +#if SYNCTEX_USE_NODE_COUNT>10 +# define SYNCTEX_DID_NEW(N) _synctex_did_new(N) +# define SYNCTEX_WILL_FREE(N) _synctex_will_free(N) +#else +# define SYNCTEX_DID_NEW(N) +# define SYNCTEX_WILL_FREE(N) +#endif + +#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && _synctex_tree_child(NODE)) +# ifdef __SYNCTEX_WORK__ +# include "/usr/include/zlib.h" +# else +# include <zlib.h> +# endif + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark STATUS +# endif +/* When the end of the synctex file has been reached: */ +# define SYNCTEX_STATUS_EOF 0 +/* When the function could not return the value it was asked for: */ +# define SYNCTEX_STATUS_NOT_OK (SYNCTEX_STATUS_EOF+1) +/* When the function returns the value it was asked for: + It must be the biggest one */ +# define SYNCTEX_STATUS_OK (SYNCTEX_STATUS_NOT_OK+1) +/* Generic error: */ +# define SYNCTEX_STATUS_ERROR (SYNCTEX_STATUS_EOF-1) +/* Parameter error: */ +# define SYNCTEX_STATUS_BAD_ARGUMENT (SYNCTEX_STATUS_ERROR-1) + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark File reader +# endif + +/* We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */ +/* Actually, the minimum buffer size is driven by integer and float parsing, including the unit. + * ±0.123456789e123?? + */ +# define SYNCTEX_BUFFER_MIN_SIZE 32 +# define SYNCTEX_BUFFER_SIZE 32768 + +#if SYNCTEX_BUFFER_SIZE >= UINT_MAX +# error BAD BUFFER SIZE(1) +#endif +#if SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE +# error BAD BUFFER SIZE(2) +#endif + +typedef struct synctex_reader_t { + gzFile file; /* The (possibly compressed) file */ + char * output; + char * synctex; + char * current; /* current location in the buffer */ + char * start; /* start of the buffer */ + char * end; /* end of the buffer */ + size_t min_size; + size_t size; + int lastv; + int line_number; + SYNCTEX_DECLARE_CHAR_OFFSET +} synctex_reader_s; + +typedef synctex_reader_s * synctex_reader_p; + +typedef struct { + synctex_status_t status; + char * synctex; + gzFile file; + synctex_io_mode_t io_mode; +} synctex_open_s; + +/* This functions opens the file at the "output" given location. + * It manages the problem of quoted filenames that appear with pdftex and filenames containing the space character. + * In TeXLive 2008, the synctex file created with pdftex did contain unexpected quotes. + * This function will remove them if possible. + * All the reference arguments will take a value on return. They must be non NULL. + * - returns: an open structure which status is + * SYNCTEX_STATUS_OK on success, + * SYNCTEX_STATUS_ERROR on failure. + * - note: on success, the caller is the owner + * of the fields of the returned open structure. + */ +static synctex_open_s __synctex_open_v2(const char * output, synctex_io_mode_t io_mode, synctex_bool_t add_quotes) { + synctex_open_s open = {SYNCTEX_STATUS_ERROR, NULL, NULL, io_mode}; + char * quoteless_synctex_name = NULL; + const char * mode = _synctex_get_io_mode_name(open.io_mode); + size_t size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1; + if (NULL == (open.synctex = (char *)malloc(size))) { + _synctex_error("! __synctex_open_v2: Memory problem (1)\n"); + return open; + } + /* we have reserved for synctex enough memory to copy output (including its 2 eventual quotes), both suffices, + * including the terminating character. size is free now. */ + if (open.synctex != strcpy(open.synctex,output)) { + _synctex_error("! __synctex_open_v2: Copy problem\n"); + return_on_error: + free(open.synctex); + open.synctex = NULL; + free(quoteless_synctex_name);/* We MUST have quoteless_synctex_name<>synctex_name */ + return open; + } + /* remove the last path extension if any */ + _synctex_strip_last_path_extension(open.synctex); + if (!strlen(open.synctex)) { + goto return_on_error; + } + /* now insert quotes. */ + if (add_quotes) { + char * quoted = NULL; + if (_synctex_copy_with_quoting_last_path_component(open.synctex,&quoted,size) || quoted == NULL) { + /* There was an error or quoting does not make sense: */ + goto return_on_error; + } + quoteless_synctex_name = open.synctex; + open.synctex = quoted; + } + /* Now add to open.synctex the first path extension. */ + if (open.synctex != strcat(open.synctex,synctex_suffix)){ + _synctex_error("! __synctex_open_v2: Concatenation problem (can't add suffix '%s')\n",synctex_suffix); + goto return_on_error; + } + /* Add to quoteless_synctex_name as well, if relevant. */ + if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix))){ + free(quoteless_synctex_name); + quoteless_synctex_name = NULL; + } + if (NULL == (open.file = gzopen(open.synctex,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("could not open %s, error %i\n",open.synctex,errno); + goto return_on_error; + } + /* Apparently, there is no uncompressed synctex file. Try the compressed version */ + if (open.synctex != strcat(open.synctex,synctex_suffix_gz)){ + _synctex_error("! __synctex_open_v2: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz); + goto return_on_error; + } + open.io_mode |= synctex_io_gz_mask; + mode = _synctex_get_io_mode_name(open.io_mode); /* the file is a compressed and is a binary file, this caused errors on Windows */ + /* Add the suffix to the quoteless_synctex_name as well. */ + if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix_gz))){ + free(quoteless_synctex_name); + quoteless_synctex_name = NULL; + } + if (NULL == (open.file = gzopen(open.synctex,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("Could not open %s, error %i\n",open.synctex,errno); + } + goto return_on_error; + } + } + /* At this point, the file is properly open. + * If we are in the add_quotes mode, we change the file name by removing the quotes. */ + if (quoteless_synctex_name) { + gzclose(open.file); + if (rename(open.synctex,quoteless_synctex_name)) { + _synctex_error("Could not rename %s to %s, error %i\n",open.synctex,quoteless_synctex_name,errno); + /* We could not rename, reopen the file with the quoted name. */ + if (NULL == (open.file = gzopen(open.synctex,mode))) { + /* No luck, could not re open this file, something has happened meanwhile */ + if (errno != ENOENT) { + /* The file does not exist any more, it has certainly be removed somehow + * this is a lower level error, I can't do anything. */ + _synctex_error("Could not open again %s, error %i\n",open.synctex,errno); + } + goto return_on_error; + } + } else { + /* The file has been successfully renamed */ + if (NULL == (open.file = gzopen(quoteless_synctex_name,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("Could not open renamed %s, error %i\n",quoteless_synctex_name,errno); + } + goto return_on_error; + } + /* The quote free file name should replace the old one:*/ + free(open.synctex); + open.synctex = quoteless_synctex_name; + quoteless_synctex_name = NULL; + } + } + /* The operation is successful, return the arguments by value. */ + open.status = SYNCTEX_STATUS_OK; + return open; +} + +/* Opens the output file, taking into account the eventual build_directory. + * - returns: an open structure which status is + * SYNCTEX_STATUS_OK on success, + * SYNCTEX_STATUS_ERROR on failure. + * - note: on success, the caller is the owner + * of the fields of the returned open structure. + */ +static synctex_open_s _synctex_open_v2(const char * output, const char * build_directory, synctex_io_mode_t io_mode, synctex_bool_t add_quotes) { + synctex_open_s open = __synctex_open_v2(output,io_mode,add_quotes); + if (open.status == SYNCTEX_STATUS_OK) { + return open; + } + if (build_directory && strlen(build_directory)) { + char * build_output; + const char *lpc; + size_t size; + synctex_bool_t is_absolute; + build_output = NULL; + lpc = _synctex_last_path_component(output); + size = strlen(build_directory)+strlen(lpc)+2; /* One for the '/' and one for the '\0'. */ + is_absolute = _synctex_path_is_absolute(build_directory); + if (!is_absolute) { + size += strlen(output); + } + if ((build_output = (char *)_synctex_malloc(size))) { + if (is_absolute) { + build_output[0] = '\0'; + } else { + if (build_output != strcpy(build_output,output)) { + _synctex_free(build_output); + return open; + } + build_output[lpc-output]='\0'; + } + if (build_output == strcat(build_output,build_directory)) { + /* Append a path separator if necessary. */ + if (!SYNCTEX_IS_PATH_SEPARATOR(build_output[strlen(build_directory)-1])) { + if (build_output != strcat(build_output,"/")) { + _synctex_free(build_output); + return open; + } + } + /* Append the last path component of the output. */ + if (build_output != strcat(build_output,lpc)) { + _synctex_free(build_output); + return open; + } + open = __synctex_open_v2(build_output,io_mode,add_quotes); + } + _synctex_free(build_output); + } /* if ((build_output... */ + } /* if (build_directory...) */ + return open; +} +void synctex_reader_free(synctex_reader_p reader) { + if (reader) { + _synctex_free(reader->output); + _synctex_free(reader->synctex); + _synctex_free(reader->start); + gzclose(reader->file); + _synctex_free(reader); + } +} +/* + * Return reader on success. + * Deallocate reader and return NULL on failure. + */ +synctex_reader_p synctex_reader_init_with_output_file(synctex_reader_p reader, const char * output, const char * build_directory) { + if (reader) { + /* now open the synctex file */ + synctex_open_s open = _synctex_open_v2(output,build_directory,0,synctex_ADD_QUOTES); + if (open.status<SYNCTEX_STATUS_OK) { + open = _synctex_open_v2(output,build_directory,0,synctex_DONT_ADD_QUOTES); + if (open.status<SYNCTEX_STATUS_OK) { + return NULL; + } + } + reader->synctex = open.synctex; + reader->file = open.file; + /* make a private copy of output */ + if (NULL == (reader->output = (char *)_synctex_malloc(strlen(output)+1))){ + _synctex_error("! synctex_scanner_new_with_output_file: Memory problem (2), reader's output is not reliable."); + } else if (reader->output != strcpy(reader->output,output)) { + _synctex_free(reader->output); + reader->output = NULL; + _synctex_error("! synctex_scanner_new_with_output_file: Copy problem, reader's output is not reliable."); + } + reader->start = reader->end = reader->current = NULL; + reader->min_size = SYNCTEX_BUFFER_MIN_SIZE; + reader->size = SYNCTEX_BUFFER_SIZE; + reader->start = reader->current = + (char *)_synctex_malloc(reader->size+1); /* one more character for null termination */ + if (NULL == reader->start) { + _synctex_error("! malloc error in synctex_reader_init_with_output_file."); + bailey: +#ifdef SYNCTEX_DEBUG + return reader; +#else + synctex_reader_free(reader); + return NULL; +#endif + } + reader->end = reader->start+reader->size; + /* reader->end always points to a null terminating character. + * Maybe there is another null terminating character between reader->current and reader->end-1. + * At least, we are sure that reader->current points to a string covering a valid part of the memory. */ +# if defined(SYNCTEX_USE_CHARINDEX) + reader->charindex_offset = -reader->size; +# endif + } + return reader; +} + +# if defined(SYNCTEX_USE_HANDLE) +# define SYNCTEX_DECLARE_HANDLE synctex_node_p handle; +# else +# define SYNCTEX_DECLARE_HANDLE +# endif + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SCANNER +# endif +/** + * The synctex scanner is the root object. + * Is is initialized with the contents of a text file or a gzipped file. + * The buffer_.* are first used to parse the text. + */ +struct synctex_scanner_t { + synctex_reader_p reader; + SYNCTEX_DECLARE_NODE_COUNT + SYNCTEX_DECLARE_HANDLE + char * output_fmt; /* dvi or pdf, not yet used */ + synctex_iterator_p iterator;/* result iterator */ + int version; /* 1, not yet used */ + struct { + unsigned has_parsed:1; /* Whether the scanner has parsed its underlying synctex file. */ + unsigned postamble:1; /* Whether the scanner has parsed its underlying synctex file. */ + unsigned reserved:sizeof(unsigned)-2; /* alignment */ + } flags; + int pre_magnification; /* magnification from the synctex preamble */ + int pre_unit; /* unit from the synctex preamble */ + int pre_x_offset; /* X offset from the synctex preamble */ + int pre_y_offset; /* Y offset from the synctex preamble */ + int count; /* Number of records, from the synctex postamble */ + float unit; /* real unit, from synctex preamble or post scriptum */ + float x_offset; /* X offset, from synctex preamble or post scriptum */ + float y_offset; /* Y Offset, from synctex preamble or post scriptum */ + synctex_node_p input; /* The first input node, its siblings are the other input nodes */ + synctex_node_p sheet; /* The first sheet node, its siblings are the other sheet nodes */ + synctex_node_p form; /* The first form, its siblings are the other forms */ + synctex_node_p ref_in_sheet; /* The first form ref node in sheet, its friends are the other form ref nodes */ + synctex_node_p ref_in_form; /* The first form ref node, its friends are the other form ref nodes in sheet */ + int number_of_lists; /* The number of friend lists */ + synctex_node_r lists_of_friends;/* The friend lists */ + synctex_class_s class_[synctex_node_number_of_types]; /* The classes of the nodes of the scanner */ + int display_switcher; + char * display_prompt; +}; + +/** + * Create a new node of the given type. + * - parameter scanner: of type synctex_node_p + * - parameter type: a type, the client is responsible + * to ask for an acceptable type. + */ +synctex_node_p synctex_node_new(synctex_scanner_p scanner, synctex_node_type_t type) { + return scanner? scanner->class_[type].new(scanner):NULL; +} +# if defined(SYNCTEX_USE_HANDLE) +SYNCTEX_INLINE static void __synctex_scanner_free_handle(synctex_scanner_p scanner) { + synctex_node_free(scanner->handle); +} +SYNCTEX_INLINE static void __synctex_scanner_remove_handle_to(synctex_node_p node) { + synctex_node_p arg_sibling = NULL; + synctex_node_p handle = node->class_->scanner->handle; + while (handle) { + synctex_node_p sibling; + if (node == _synctex_tree_target(handle)) { + sibling = __synctex_tree_reset_sibling(handle); + if (arg_sibling) { + __synctex_tree_set_sibling(arg_sibling, sibling); + } else { + node->class_->scanner->handle = sibling; + } + synctex_node_free(handle); + break; + } else { + sibling = __synctex_tree_sibling(handle); + } + arg_sibling = handle; + handle = sibling; + } +} +SYNCTEX_INLINE static void __synctex_scanner_register_handle_to(synctex_node_p node) { + synctex_node_p NNN = _synctex_new_handle_with_target(node); + __synctex_tree_set_sibling(NNN,node->class_->scanner->handle); + node->class_->scanner->handle = NNN; +} +#endif +#if SYNCTEX_USE_NODE_COUNT>10 +SYNCTEX_INLINE static void _synctex_did_new(synctex_node_p node) { + printf("NODE CREATED # %i, %s, %p\n", + (node->class_->scanner->node_count)++, + synctex_node_isa(node), + node); +} +SYNCTEX_INLINE static void _synctex_will_free(synctex_node_p node) { + printf("NODE DELETED # %i, %s, %p\n", + --(node->class_->scanner->node_count), + synctex_node_isa(node), + node); +} +#endif + +/** + * Free the given node. + * - parameter node: of type synctex_node_p + * - note: a node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for all nodes with children. + */ +static void _synctex_free_node(synctex_node_p node) { + if (node) { + SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); + SYNCTEX_WILL_FREE(node); + synctex_node_free(__synctex_tree_sibling(node)); + synctex_node_free(_synctex_tree_child(node)); + _synctex_free(node); + } + return; +} +/** + * Free the given handle. + * - parameter node: of type synctex_node_p + * - note: a node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for all handles. + */ +static void _synctex_free_handle(synctex_node_p handle) { + if (handle) { + _synctex_free_handle(__synctex_tree_sibling(handle)); + _synctex_free_handle(_synctex_tree_child(handle)); + _synctex_free(handle); + } + return; +} + +/** + * Free the given leaf node. + * - parameter node: of type synctex_node_p, with no child nor sibling. + * - note: a node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for all nodes with no children. + */ +static void _synctex_free_leaf(synctex_node_p node) { + if (node) { + SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); + SYNCTEX_WILL_FREE(node); + synctex_node_free(__synctex_tree_sibling(node)); + _synctex_free(node); + } + return; +} + +/** + SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts + */ +# define SYNCTEX_CUR (scanner->reader->current) +# define SYNCTEX_START (scanner->reader->start) +# define SYNCTEX_END (scanner->reader->end) + +/* Here are gathered all the possible status that the next scanning functions will return. + * All these functions return a status, and pass their result through pointers. + * Negative values correspond to errors. + * The management of the buffer is causing some significant overhead. + * Every function that may access the buffer returns a status related to the buffer and file state. + * status >= SYNCTEX_STATUS_OK means the function worked as expected + * status < SYNCTEX_STATUS_OK means the function did not work as expected + * status == SYNCTEX_STATUS_NOT_OK means the function did not work as expected but there is still some material to parse. + * status == SYNCTEX_STATUS_EOF means the function did not work as expected and there is no more material. + * status<SYNCTEX_STATUS_EOF means an error + */ +#if defined(SYNCTEX_USE_CHARINDEX) +synctex_node_p synctex_scanner_handle(synctex_scanner_p scanner) { + return scanner? scanner->handle:NULL; +} +#endif + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Decoding prototypes +# endif + +typedef struct { + int integer; + synctex_status_t status; +} synctex_is_s; + +static synctex_is_s _synctex_decode_int(synctex_scanner_p scanner); +static synctex_is_s _synctex_decode_int_opt(synctex_scanner_p scanner, int default_value); +static synctex_is_s _synctex_decode_int_v(synctex_scanner_p scanner); + +typedef struct { + char * string; + synctex_status_t status; +} synctex_ss_s; + +static synctex_ss_s _synctex_decode_string(synctex_scanner_p scanner); + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Data SETGET +# endif + +/** + * The next macros are used to access the node data info + * through the class modelator integer fields. + * - parameter NODE: of type synctex_node_p + */ +# define SYNCTEX_DATA(NODE) ((*((((NODE)->class_))->info))(NODE)) +#if defined SYNCTEX_DEBUG > 1000 +# define DEFINE_SYNCTEX_DATA_HAS(WHAT) \ +SYNCTEX_INLINE static synctex_bool_t __synctex_data_has_##WHAT(synctex_node_p node) {\ + return (node && (node->class_->modelator->WHAT>=0));\ +}\ +SYNCTEX_INLINE static synctex_bool_t _synctex_data_has_##WHAT(synctex_node_p node) {\ + if (node && (node->class_->modelator->WHAT<0)) {\ + printf("WARNING: NO %s for %s\n", #WHAT, synctex_node_isa(node));\ + }\ + return __synctex_data_has_##WHAT(node);\ +} +#else +# define DEFINE_SYNCTEX_DATA_HAS(WHAT) \ +SYNCTEX_INLINE static synctex_bool_t __synctex_data_has_##WHAT(synctex_node_p node) {\ + return (node && (node->class_->modelator->WHAT>=0));\ +}\ +SYNCTEX_INLINE static synctex_bool_t _synctex_data_has_##WHAT(synctex_node_p node) {\ + return __synctex_data_has_##WHAT(node);\ +} +#endif + +SYNCTEX_INLINE static synctex_data_p __synctex_data(synctex_node_p node) { + return node->data+node->class_->navigator->size; +} +# define DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ +DEFINE_SYNCTEX_DATA_HAS(WHAT)\ +static int _synctex_data_##WHAT(synctex_node_p node) {\ + if (_synctex_data_has_##WHAT(node)) {\ + return __synctex_data(node)[node->class_->modelator->WHAT].as_integer;\ + }\ + return 0;\ +}\ +static int _synctex_data_set_##WHAT(synctex_node_p node, int new_value) {\ + int old = 0;\ + if (_synctex_data_has_##WHAT(node)) {\ + old = __synctex_data(node)[node->class_->modelator->WHAT].as_integer;\ + __synctex_data(node)[node->class_->modelator->WHAT].as_integer=new_value;\ + }\ + return old;\ +} +#define DEFINE_SYNCTEX_DATA_INT_DECODE(WHAT) \ +static synctex_status_t _synctex_data_decode_##WHAT(synctex_node_p node) {\ + if (_synctex_data_has_##WHAT(node)) {\ + synctex_is_s is = _synctex_decode_int(node->class_->scanner);\ + if (is.status == SYNCTEX_STATUS_OK) {\ + _synctex_data_set_##WHAT(node,is.integer);\ + } \ + return is.status;\ + }\ + return SYNCTEX_STATUS_BAD_ARGUMENT;\ +} +# define DEFINE_SYNCTEX_DATA_INT_DECODE_v(WHAT) \ +static synctex_status_t _synctex_data_decode_##WHAT##_v(synctex_node_p node) {\ + if (_synctex_data_has_##WHAT(node)) {\ + synctex_is_s is = _synctex_decode_int_v(node->class_->scanner);\ + if (is.status == SYNCTEX_STATUS_OK) {\ + _synctex_data_set_##WHAT(node,is.integer);\ + } \ + return is.status;\ + }\ + return SYNCTEX_STATUS_BAD_ARGUMENT;\ +} +#define DEFINE_SYNCTEX_DATA_STR_GETSET(WHAT) \ +DEFINE_SYNCTEX_DATA_HAS(WHAT)\ +static char * _synctex_data_##WHAT(synctex_node_p node) {\ + if (_synctex_data_has_##WHAT(node)) {\ + return node->data[node->class_->navigator->size+node->class_->modelator->WHAT].as_string;\ + }\ + return NULL;\ +}\ +static char * _synctex_data_set_##WHAT(synctex_node_p node, char * new_value) {\ + char * old = "";\ + if (_synctex_data_has_##WHAT(node)) {\ + old = node->data[node->class_->navigator->size+node->class_->modelator->WHAT].as_string;\ + node->data[node->class_->navigator->size+node->class_->modelator->WHAT].as_string =new_value;\ + }\ + return old;\ +} +#define DEFINE_SYNCTEX_DATA_STR_DECODE(WHAT) \ +static synctex_status_t _synctex_data_decode_##WHAT(synctex_node_p node) {\ + if (_synctex_data_has_##WHAT(node)) {\ + synctex_ss_s ss = _synctex_decode_string(node->class_->scanner);\ + if (ss.status == SYNCTEX_STATUS_OK) {\ + _synctex_data_set_##WHAT(node,ss.string);\ + } \ + return ss.status;\ + }\ + return SYNCTEX_STATUS_BAD_ARGUMENT;\ +} +#define DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(WHAT) \ +DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ +DEFINE_SYNCTEX_DATA_INT_DECODE(WHAT) +#define DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE_v(WHAT) \ +DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ +DEFINE_SYNCTEX_DATA_INT_DECODE_v(WHAT) +#define DEFINE_SYNCTEX_DATA_STR_GETSET_DECODE(WHAT) \ +DEFINE_SYNCTEX_DATA_STR_GETSET(WHAT) \ +DEFINE_SYNCTEX_DATA_STR_DECODE(WHAT) + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark OBJECTS, their creators and destructors. +# endif + +# ifdef SYNCTEX_NOTHING +# pragma mark input. +# endif + +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(tag) +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(line) +DEFINE_SYNCTEX_DATA_STR_GETSET_DECODE(name) + +/* Input nodes only know about their sibling, which is another input node. + * The synctex information is the _synctex_data_tag and _synctex_data_name + * note: the input owns its name. */ + +# define SYNCTEX_INPUT_MARK "Input:" + +static const synctex_tree_model_s synctex_tree_model_input = { + synctex_tree_sibling_idx, /* sibling */ + -1, /* parent */ + -1, /* child */ + -1, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + -1, /* target */ + synctex_tree_s_input_max +}; +static const synctex_data_model_s synctex_data_model_input = { + synctex_data_input_tag_idx, /* tag */ + synctex_data_input_line_idx,/* line */ + -1, /* column */ + -1, /* h */ + -1, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + synctex_data_input_name_idx, /* name */ + -1, /* page */ + synctex_data_input_tln_max +}; + +#define SYNCTEX_INSPECTOR_GETTER_F(WHAT)\ +&_synctex_data_##WHAT, &_synctex_data_set_##WHAT + +static synctex_node_p _synctex_new_input(synctex_scanner_p scanner); +static void _synctex_free_input(synctex_node_p node); +static void _synctex_log_input(synctex_node_p node); +static char * _synctex_abstract_input(synctex_node_p node); +static void _synctex_display_input(synctex_node_p node); + +static const synctex_tlcpector_s synctex_tlcpector_input = { + &_synctex_data_tag, /* tag */ + &_synctex_int_none, /* line */ + &_synctex_int_none, /* column */ +}; + +static synctex_class_s synctex_class_input = { + NULL, /* No scanner yet */ + synctex_node_type_input, /* Node type */ + &_synctex_new_input, /* creator */ + &_synctex_free_input, /* destructor */ + &_synctex_log_input, /* log */ + &_synctex_display_input, /* display */ + &_synctex_abstract_input, /* abstract */ + &synctex_tree_model_input, /* tree model */ + &synctex_data_model_input, /* data model */ + &synctex_tlcpector_input, /* inspector */ + &synctex_inspector_none, /* inspector */ + &synctex_vispector_none, /* vispector */ +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_s_input_max+synctex_data_input_tln_max]; +} synctex_input_s; + +static synctex_node_p _synctex_new_input(synctex_scanner_p scanner) { + if (scanner) { + synctex_node_p node = _synctex_malloc(sizeof(synctex_input_s)); + if (node) { + node->class_ = scanner->class_+synctex_node_type_input; + SYNCTEX_DID_NEW(node); + SYNCTEX_IMPLEMENT_CHARINDEX(node,0); + SYNCTEX_REGISTER_HANDLE_TO(node); + } + return node; + } + return NULL; +} + +static void _synctex_free_input(synctex_node_p node){ + if (node) { + SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); + SYNCTEX_WILL_FREE(node); + synctex_node_free(__synctex_tree_sibling(node)); + _synctex_free(_synctex_data_name(node)); + _synctex_free(node); + } +} + +/* The sheet is a first level node. + * It has no parent (the owner is the scanner itself) + * Its sibling points to another sheet. + * Its child points to its first child, in general a box. + * A sheet node contains only one synctex information: the page. + * This is the 1 based page index as given by TeX. + */ + +# ifdef SYNCTEX_NOTHING +# pragma mark sheet. +# endif +/** + * Every node has the same structure, but not the same size. + */ + +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(page) + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_scn_sheet_max+synctex_data_p_sheet_max]; +} synctex_node_sheet_s; + +/* sheet node creator */ + +#define DEFINE_synctex_new_scanned_NODE(NAME)\ +static synctex_node_p _synctex_new_##NAME(synctex_scanner_p scanner) {\ + if (scanner) {\ + ++SYNCTEX_CUR;\ + synctex_node_p node = _synctex_malloc(sizeof(synctex_node_##NAME##_s));\ + if (node) {\ + node->class_ = scanner->class_+synctex_node_type_##NAME;\ + SYNCTEX_DID_NEW(node); \ + SYNCTEX_IMPLEMENT_CHARINDEX(node,-1);\ + SYNCTEX_REGISTER_HANDLE_TO(node); \ + }\ + return node;\ + }\ + return NULL;\ +} +/* NB: -1 in SYNCTEX_IMPLEMENT_CHARINDEX above because + * the first char of the line has been scanned + */ +DEFINE_synctex_new_scanned_NODE(sheet) +static void _synctex_log_sheet(synctex_node_p node); +static char * _synctex_abstract_sheet(synctex_node_p node); +static void _synctex_display_sheet(synctex_node_p node); + +static const synctex_tree_model_s synctex_tree_model_sheet = { + synctex_tree_sibling_idx, /* sibling */ + -1, /* parent */ + synctex_tree_s_child_idx, /* child */ + -1, /* friend */ + -1, /* last */ + synctex_tree_sc_next_hbox_idx, /* next_hbox */ + -1, /* arg_sibling */ + -1, /* target */ + synctex_tree_scn_sheet_max +}; +static const synctex_data_model_s synctex_data_model_sheet = { + -1, /* tag */ + -1, /* line */ + -1, /* column */ + -1, /* h */ + -1, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + synctex_data_sheet_page_idx, /* page */ + synctex_data_p_sheet_max +}; +static synctex_class_s synctex_class_sheet = { + NULL, /* No scanner yet */ + synctex_node_type_sheet, /* Node type */ + &_synctex_new_sheet, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_sheet, /* log */ + &_synctex_display_sheet, /* display */ + &_synctex_abstract_sheet, /* abstract */ + &synctex_tree_model_sheet, /* tree model */ + &synctex_data_model_sheet, /* data model */ + &synctex_tlcpector_none, /* tlcpector */ + &synctex_inspector_none, /* inspector */ + &synctex_vispector_none, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark form. +# endif +/** + * Every node has the same structure, but not the same size. + */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_sct_form_max+synctex_data_t_form_max]; +} synctex_node_form_s; + +DEFINE_synctex_new_scanned_NODE(form) + +static char * _synctex_abstract_form(synctex_node_p node); +static void _synctex_display_form(synctex_node_p node); +static void _synctex_log_form(synctex_node_p node); + +static const synctex_tree_model_s synctex_tree_model_form = { + synctex_tree_sibling_idx, /* sibling */ + -1, /* parent */ + synctex_tree_s_child_idx, /* child */ + -1, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + synctex_tree_sc_target_idx, /* target */ + synctex_tree_sct_form_max +}; +static const synctex_data_model_s synctex_data_model_form = { + synctex_data_form_tag_idx, /* tag */ + -1, /* line */ + -1, /* column */ + -1, /* h */ + -1, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_t_form_max +}; +static synctex_class_s synctex_class_form = { + NULL, /* No scanner yet */ + synctex_node_type_form, /* Node type */ + &_synctex_new_form, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_form, /* log */ + &_synctex_display_form, /* display */ + &_synctex_abstract_form, /* abstract */ + &synctex_tree_model_form, /* tree model */ + &synctex_data_model_form, /* data model */ + &synctex_tlcpector_none, /* tlcpector */ + &synctex_inspector_none, /* inspector */ + &synctex_vispector_none, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark vbox. +# endif + +/* A box node contains navigation and synctex information + * There are different kinds of boxes. + * Only horizontal boxes are treated differently because of their visible size. + */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spcfl_vbox_max+synctex_data_box_max]; +} synctex_node_vbox_s; + +/* vertical box node creator */ +DEFINE_synctex_new_scanned_NODE(vbox) + +static char * _synctex_abstract_vbox(synctex_node_p node); +static void _synctex_display_vbox(synctex_node_p node); +static void _synctex_log_vbox(synctex_node_p node); + +static const synctex_tree_model_s synctex_tree_model_vbox = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + synctex_tree_sp_child_idx, /* child */ + synctex_tree_spc_friend_idx, /* friend */ + synctex_tree_spcf_last_idx, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + -1, /* target */ + synctex_tree_spcfl_vbox_max +}; + +#define SYNCTEX_DFLT_COLUMN -1 + +DEFINE_SYNCTEX_DATA_INT_GETSET(column) +static synctex_status_t _synctex_data_decode_column(synctex_node_p node) { + if (_synctex_data_has_column(node)) { + synctex_is_s is = _synctex_decode_int_opt(node->class_->scanner, + SYNCTEX_DFLT_COLUMN); + if (is.status == SYNCTEX_STATUS_OK) { + _synctex_data_set_column(node,is.integer); + } + return is.status; + } + return SYNCTEX_STATUS_BAD_ARGUMENT; +} +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(h) +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE_v(v) +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(width) +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(height) +DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(depth) + +SYNCTEX_INLINE static void _synctex_data_set_tlc(synctex_node_p node, synctex_node_p model) { + _synctex_data_set_tag(node, _synctex_data_tag(model)); + _synctex_data_set_line(node, _synctex_data_line(model)); + _synctex_data_set_column(node, _synctex_data_column(model)); +} +SYNCTEX_INLINE static void _synctex_data_set_tlchv(synctex_node_p node, synctex_node_p model) { + _synctex_data_set_tlc(node,model); + _synctex_data_set_h(node, _synctex_data_h(model)); + _synctex_data_set_v(node, _synctex_data_v(model)); +} + +static const synctex_data_model_s synctex_data_model_box = { + synctex_data_tag_idx, /* tag */ + synctex_data_line_idx, /* line */ + synctex_data_column_idx,/* column */ + synctex_data_h_idx, /* h */ + synctex_data_v_idx, /* v */ + synctex_data_width_idx, /* width */ + synctex_data_height_idx,/* height */ + synctex_data_depth_idx, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_box_max +}; +static const synctex_tlcpector_s synctex_tlcpector_default = { + &_synctex_data_tag, /* tag */ + &_synctex_data_line, /* line */ + &_synctex_data_column, /* column */ +}; +static const synctex_inspector_s synctex_inspector_box = { + &_synctex_data_h, + &_synctex_data_v, + &_synctex_data_width, + &_synctex_data_height, + &_synctex_data_depth, +}; +static float __synctex_node_visible_h(synctex_node_p node); +static float __synctex_node_visible_v(synctex_node_p node); +static float __synctex_node_visible_width(synctex_node_p node); +static float __synctex_node_visible_height(synctex_node_p node); +static float __synctex_node_visible_depth(synctex_node_p node); +static synctex_vispector_s synctex_vispector_box = { + &__synctex_node_visible_h, + &__synctex_node_visible_v, + &__synctex_node_visible_width, + &__synctex_node_visible_height, + &__synctex_node_visible_depth, +}; +/* These are static class objects, each scanner will make a copy of them and setup the scanner field. + */ +static synctex_class_s synctex_class_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_vbox, /* Node type */ + &_synctex_new_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_vbox, /* log */ + &_synctex_display_vbox, /* display */ + &_synctex_abstract_vbox, /* abstract */ + &synctex_tree_model_vbox, /* tree model */ + &synctex_data_model_box, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_box, /* inspector */ + &synctex_vispector_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark hbox. +# endif + +/* Horizontal boxes must contain visible size, because 0 width does not mean emptiness. + * They also contain an average of the line numbers of the containing nodes. */ + +static const synctex_tree_model_s synctex_tree_model_hbox = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + synctex_tree_sp_child_idx, /* child */ + synctex_tree_spc_friend_idx, /* friend */ + synctex_tree_spcf_last_idx, /* last */ + synctex_tree_spcfl_next_hbox_idx, /* next_hbox */ + -1, /* arg_sibling */ + -1, /* target */ + synctex_tree_spcfln_hbox_max +}; + +DEFINE_SYNCTEX_DATA_INT_GETSET(mean_line) +DEFINE_SYNCTEX_DATA_INT_GETSET(weight) +DEFINE_SYNCTEX_DATA_INT_GETSET(h_V) +DEFINE_SYNCTEX_DATA_INT_GETSET(v_V) +DEFINE_SYNCTEX_DATA_INT_GETSET(width_V) +DEFINE_SYNCTEX_DATA_INT_GETSET(height_V) +DEFINE_SYNCTEX_DATA_INT_GETSET(depth_V) + +/** + * The hbox model. + * It contains V variants of geometrical information. + * It happens that hboxes contain material that is not used to compute + * the bounding box. Some letters may appear out of the box given by TeX. + * In such a situation, the visible bounding box is bigger ence the V variant. + * Only hboxes have such variant. It does not make sense for void boxes + * and it is not used here for vboxes. + * - author: JL + */ + +static const synctex_data_model_s synctex_data_model_hbox = { + synctex_data_tag_idx, /* tag */ + synctex_data_line_idx, /* line */ + synctex_data_column_idx,/* column */ + synctex_data_h_idx, /* h */ + synctex_data_v_idx, /* v */ + synctex_data_width_idx, /* width */ + synctex_data_height_idx,/* height */ + synctex_data_depth_idx, /* depth */ + synctex_data_mean_line_idx, /* mean_line */ + synctex_data_weight_idx, /* weight */ + synctex_data_h_V_idx, /* h_V */ + synctex_data_v_V_idx, /* v_V */ + synctex_data_width_V_idx, /* width_V */ + synctex_data_height_V_idx, /* height_V */ + synctex_data_depth_V_idx, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_hbox_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spcfln_hbox_max+synctex_data_hbox_max]; +} synctex_node_hbox_s; + +/* horizontal box node creator */ +DEFINE_synctex_new_scanned_NODE(hbox) + +static void _synctex_log_hbox(synctex_node_p node); +static char * _synctex_abstract_hbox(synctex_node_p node); +static void _synctex_display_hbox(synctex_node_p node); + +static synctex_class_s synctex_class_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_hbox, /* Node type */ + &_synctex_new_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_hbox, /* log */ + &_synctex_display_hbox, /* display */ + &_synctex_abstract_hbox, /* abstract */ + &synctex_tree_model_hbox, /* tree model */ + &synctex_data_model_hbox, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_box, /* inspector */ + &synctex_vispector_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark void vbox. +# endif + +/* This void box node implementation is either horizontal or vertical + * It does not contain a child field. + */ +static const synctex_tree_model_s synctex_tree_model_spf = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + -1, /* child */ + synctex_tree_sp_friend_idx, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + -1, /* target */ + synctex_tree_spf_max +}; +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spf_max+synctex_data_box_max]; +} synctex_node_void_vbox_s; + +/* vertical void box node creator */ +DEFINE_synctex_new_scanned_NODE(void_vbox) + +static void _synctex_log_void_box(synctex_node_p node); +static char * _synctex_abstract_void_vbox(synctex_node_p node); +static void _synctex_display_void_vbox(synctex_node_p node); + +static synctex_class_s synctex_class_void_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_vbox,/* Node type */ + &_synctex_new_void_vbox, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_vbox,/* display */ + &_synctex_abstract_void_vbox,/* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_box, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_box, /* inspector */ + &synctex_vispector_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark void hbox. +# endif + +typedef synctex_node_void_vbox_s synctex_node_void_hbox_s; + +/* horizontal void box node creator */ +DEFINE_synctex_new_scanned_NODE(void_hbox) + +static char * _synctex_abstract_void_hbox(synctex_node_p node); +static void _synctex_display_void_hbox(synctex_node_p node); + +static synctex_class_s synctex_class_void_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_hbox,/* Node type */ + &_synctex_new_void_hbox, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_hbox,/* display */ + &_synctex_abstract_void_hbox,/* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_box, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_box, /* inspector */ + &synctex_vispector_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark form ref. +# endif + +/* The form ref node. */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spfa_max+synctex_data_ref_thv_max]; +} synctex_node_ref_s; + +/* form ref node creator */ +DEFINE_synctex_new_scanned_NODE(ref) + +static void _synctex_log_ref(synctex_node_p node); +static char * _synctex_abstract_ref(synctex_node_p node); +static void _synctex_display_ref(synctex_node_p node); + +static const synctex_tree_model_s synctex_tree_model_spfa = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + -1, /* child */ + synctex_tree_sp_friend_idx, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + synctex_tree_spf_arg_sibling_idx, /* arg_sibling */ + -1, /* target */ + synctex_tree_spfa_max +}; +static const synctex_data_model_s synctex_data_model_ref = { + synctex_data_tag_idx, /* tag */ + -1, /* line */ + -1, /* column */ + synctex_data_ref_h_idx, /* h */ + synctex_data_ref_v_idx, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_ref_thv_max /* size */ +}; +static synctex_class_s synctex_class_ref = { + NULL, /* No scanner yet */ + synctex_node_type_ref, /* Node type */ + &_synctex_new_ref, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_ref, /* log */ + &_synctex_display_ref, /* display */ + &_synctex_abstract_ref, /* abstract */ + &synctex_tree_model_spfa, /* navigator */ + &synctex_data_model_ref, /* data model */ + &synctex_tlcpector_none, /* tlcpector */ + &synctex_inspector_none, /* inspector */ + &synctex_vispector_none, /* vispector */ +}; +# ifdef SYNCTEX_NOTHING +# pragma mark small node. +# endif + +/* The small nodes correspond to glue, penalty, math and boundary nodes. */ +static const synctex_data_model_s synctex_data_model_tlchv = { + synctex_data_tag_idx, /* tag */ + synctex_data_line_idx, /* line */ + synctex_data_column_idx, /* column */ + synctex_data_h_idx, /* h */ + synctex_data_v_idx, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_tlchv_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spf_max+synctex_data_tlchv_max]; +} synctex_node_tlchv_s; + +static void _synctex_log_tlchv_node(synctex_node_p node); + +# ifdef SYNCTEX_NOTHING +# pragma mark math. +# endif + +typedef synctex_node_tlchv_s synctex_node_math_s; + +/* math node creator */ +DEFINE_synctex_new_scanned_NODE(math) + +static char * _synctex_abstract_math(synctex_node_p node); +static void _synctex_display_math(synctex_node_p node); +static synctex_inspector_s synctex_inspector_hv = { + &_synctex_data_h, + &_synctex_data_v, + &_synctex_int_none, + &_synctex_int_none, + &_synctex_int_none, +}; +static synctex_vispector_s synctex_vispector_hv = { + &__synctex_node_visible_h, + &__synctex_node_visible_v, + &_synctex_float_none, + &_synctex_float_none, + &_synctex_float_none, +}; + +static synctex_class_s synctex_class_math = { + NULL, /* No scanner yet */ + synctex_node_type_math, /* Node type */ + &_synctex_new_math, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_tlchv_node, /* log */ + &_synctex_display_math, /* display */ + &_synctex_abstract_math, /* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_tlchv, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_hv, /* inspector */ + &synctex_vispector_hv, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark kern node. +# endif + +static const synctex_data_model_s synctex_data_model_tlchvw = { + synctex_data_tag_idx, /* tag */ + synctex_data_line_idx, /* line */ + synctex_data_column_idx,/* column */ + synctex_data_h_idx, /* h */ + synctex_data_v_idx, /* v */ + synctex_data_width_idx, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_tlchvw_max +}; +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spf_max+synctex_data_tlchvw_max]; +} synctex_node_kern_s; + +/* kern node creator */ +DEFINE_synctex_new_scanned_NODE(kern) + +static void _synctex_log_kern_node(synctex_node_p node); +static char * _synctex_abstract_kern(synctex_node_p node); +static void _synctex_display_kern(synctex_node_p node); + +static synctex_inspector_s synctex_inspector_kern = { + &_synctex_data_h, + &_synctex_data_v, + &_synctex_data_width, + &_synctex_int_none, + &_synctex_int_none, +}; +static float __synctex_kern_visible_h(synctex_node_p node); +static float __synctex_kern_visible_width(synctex_node_p node); +static synctex_vispector_s synctex_vispector_kern = { + &__synctex_kern_visible_h, + &__synctex_node_visible_v, + &__synctex_kern_visible_width, + &_synctex_float_none, + &_synctex_float_none, +}; + +static synctex_class_s synctex_class_kern = { + NULL, /* No scanner yet */ + synctex_node_type_kern, /* Node type */ + &_synctex_new_kern, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_kern_node, /* log */ + &_synctex_display_kern, /* display */ + &_synctex_abstract_kern, /* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_tlchvw, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_kern, /* inspector */ + &synctex_vispector_kern, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark glue. +# endif + +/* glue node creator */ +typedef synctex_node_tlchv_s synctex_node_glue_s; +DEFINE_synctex_new_scanned_NODE(glue) + +static char * _synctex_abstract_glue(synctex_node_p node); +static void _synctex_display_glue(synctex_node_p node); + +static synctex_class_s synctex_class_glue = { + NULL, /* No scanner yet */ + synctex_node_type_glue, /* Node type */ + &_synctex_new_glue, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_tlchv_node, /* log */ + &_synctex_display_glue, /* display */ + &_synctex_abstract_glue, /* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_tlchv, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_hv, /* inspector */ + &synctex_vispector_hv, /* vispector */ +}; + +/* The small nodes correspond to glue and boundary nodes. */ + +# ifdef SYNCTEX_NOTHING +# pragma mark rule. +# endif + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spf_max+synctex_data_box_max]; +} synctex_node_rule_s; + +DEFINE_synctex_new_scanned_NODE(rule) + +static void _synctex_log_rule(synctex_node_p node); +static char * _synctex_abstract_rule(synctex_node_p node); +static void _synctex_display_rule(synctex_node_p node); + +static float __synctex_rule_visible_h(synctex_node_p node); +static float __synctex_rule_visible_v(synctex_node_p node); +static float __synctex_rule_visible_width(synctex_node_p node); +static float __synctex_rule_visible_height(synctex_node_p node); +static float __synctex_rule_visible_depth(synctex_node_p node); +static synctex_vispector_s synctex_vispector_rule = { + &__synctex_rule_visible_h, + &__synctex_rule_visible_v, + &__synctex_rule_visible_width, + &__synctex_rule_visible_height, + &__synctex_rule_visible_depth, +}; + +static synctex_class_s synctex_class_rule = { + NULL, /* No scanner yet */ + synctex_node_type_rule, /* Node type */ + &_synctex_new_rule, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_rule, /* log */ + &_synctex_display_rule, /* display */ + &_synctex_abstract_rule, /* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_box, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_box, /* inspector */ + &synctex_vispector_rule, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark boundary. +# endif + +/* boundary node creator */ +typedef synctex_node_tlchv_s synctex_node_boundary_s; +DEFINE_synctex_new_scanned_NODE(boundary) + +static char * _synctex_abstract_boundary(synctex_node_p node); +static void _synctex_display_boundary(synctex_node_p node); + +static synctex_class_s synctex_class_boundary = { + NULL, /* No scanner yet */ + synctex_node_type_boundary, /* Node type */ + &_synctex_new_boundary, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_tlchv_node, /* log */ + &_synctex_display_boundary, /* display */ + &_synctex_abstract_boundary,/* abstract */ + &synctex_tree_model_spf, /* tree model */ + &synctex_data_model_tlchv, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_hv, /* inspector */ + &synctex_vispector_hv, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark box boundary. +# endif + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spfa_max+synctex_data_tlchv_max]; +} synctex_node_box_bdry_s; + +#define DEFINE_synctex_new_unscanned_NODE(NAME)\ +SYNCTEX_INLINE static synctex_node_p _synctex_new_##NAME(synctex_scanner_p scanner) {\ + if (scanner) {\ + synctex_node_p node = _synctex_malloc(sizeof(synctex_node_##NAME##_s));\ + if (node) {\ + node->class_ = scanner->class_+synctex_node_type_##NAME;\ + SYNCTEX_DID_NEW(node); \ + }\ + return node;\ + }\ + return NULL;\ +} +DEFINE_synctex_new_unscanned_NODE(box_bdry) + +static char * _synctex_abstract_box_bdry(synctex_node_p node); +static void _synctex_display_box_bdry(synctex_node_p node); + +static synctex_class_s synctex_class_box_bdry = { + NULL, /* No scanner yet */ + synctex_node_type_box_bdry, /* Node type */ + &_synctex_new_box_bdry, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_tlchv_node, /* log */ + &_synctex_display_box_bdry, /* display */ + &_synctex_abstract_box_bdry,/* display */ + &synctex_tree_model_spfa, /* tree model */ + &synctex_data_model_tlchv, /* data model */ + &synctex_tlcpector_default, /* tlcpector */ + &synctex_inspector_hv, /* inspector */ + &synctex_vispector_hv, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark hbox proxy. +# endif + +/** + * Standard nodes refer to TeX nodes: math, kern, boxes... + * Proxy nodes are used to support forms. + * A form is parsed as a tree of standard nodes starting + * at the top left position. + * When a reference is used, the form is duplicated + * to the location specified by the reference. + * As the same form can be duplicated at different locations, + * the geometrical information is relative to its own top left point. + * As we need absolute locations, we use proxy nodes. + * A proxy node records an offset and the target node. + * The target partly acts as a delegate. + * The h and v position of the proxy node is the h and v + * position of the target shifted by the proxy's offset. + * The width, height and depth are not sensitive to offsets. + * When are proxies created ? + * 1) when the synctex file has been parsed, all the form refs + * are replaced by proxies to the content of a form. + * This content is a node with siblings (actually none). + * Those root proxies have the parent of the ref they replace, + * so their parents exist and are no proxy. + * Moreover, if they have no sibling, it means that their target have no + * sibling as well. + * Such nodes are called root proxies. + * 2) On the fly, when a proxy is asked for its child + * (or sibling) and has none, a proxy to its target's child + * (or sibling) is created if any. There are only 2 possible situations: + * either the newly created proxy is the child of a proxy, + * or it is the sibling of a proxy created on the fly. + * In both cases, the parent is a proxy with children. + * Such nodes are called child proxies. + * How to compute the offset of a proxy ? + * The offset of root proxy objects is exactly + * the offset of the ref they replace. + * The offset of other proxies is their owner's, + * except when pointing to a root proxy. + * What happens for cascading forms ? + * Here is an example diagram + * + * At parse time, the arrow means "owns": + * sheet0 -> ref_to1 + * + * target1 -> ref_to2 + * + * target2 -> child22 + * + * After replacing the refs: + * sheet0 -> proxy00 -> proxy01 -> proxy02 + * | | | + * target1 -> proxy11 -> proxy12 + * | | + * target2 -> proxy22 + * + * proxy00, proxy11 and proxy22 are root proxies. + * Their offset is the one of the ref they replace + * proxy01, proxy02 and proxy12 are child proxies. + * Their proxy is the one of their parent. + * Optimization. + * After all the refs are replaced, there are only root nodes + * targeting standard node. We make sure that each child proxy + * also targets a standard node. + * It is possible for a proxy to have a standard sibling + * whereas its target has no sibling at all. Root proxies + * are such nodes, and are the only ones. + * The consequence is that proxies created on the fly + * must take into account this situation. + */ + +/* A proxy to a hbox. + * A proxy do have a target, which can be a proxy + */ + +static const synctex_tree_model_s synctex_tree_model_proxy_hbox = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + synctex_tree_sp_child_idx, /* child */ + synctex_tree_spc_friend_idx, /* friend */ + synctex_tree_spcf_last_idx, /* last */ + synctex_tree_spcfl_next_hbox_idx, /* next_hbox */ + -1, /* arg_sibling */ + synctex_tree_spcfln_target_idx, /* target */ + synctex_tree_spcflnt_proxy_hbox_max +}; +static const synctex_data_model_s synctex_data_model_proxy = { + -1, /* tag */ + -1, /* line */ + -1, /* column */ + synctex_data_proxy_h_idx, /* h */ + synctex_data_proxy_v_idx, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + -1, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_proxy_hv_max +}; +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spcflnt_proxy_hbox_max+synctex_data_proxy_hv_max]; +} synctex_node_proxy_hbox_s; + +/* box proxy node creator */ +DEFINE_synctex_new_unscanned_NODE(proxy_hbox) + +static void _synctex_log_proxy(synctex_node_p node); +static char * _synctex_abstract_proxy_hbox(synctex_node_p node); +static void _synctex_display_proxy_hbox(synctex_node_p node); + +static int _synctex_proxy_tag(synctex_node_p); +static int _synctex_proxy_line(synctex_node_p); +static int _synctex_proxy_column(synctex_node_p); + +static synctex_tlcpector_s synctex_tlcpector_proxy = { + &_synctex_proxy_tag, + &_synctex_proxy_line, + &_synctex_proxy_column, +}; +static int _synctex_proxy_h(synctex_node_p); +static int _synctex_proxy_v(synctex_node_p); +static int _synctex_proxy_width(synctex_node_p); +static int _synctex_proxy_height(synctex_node_p); +static int _synctex_proxy_depth(synctex_node_p); +static synctex_inspector_s synctex_inspector_proxy_box = { + &_synctex_proxy_h, + &_synctex_proxy_v, + &_synctex_proxy_width, + &_synctex_proxy_height, + &_synctex_proxy_depth, +}; + +static float __synctex_proxy_visible_h(synctex_node_p); +static float __synctex_proxy_visible_v(synctex_node_p); +static float __synctex_proxy_visible_width(synctex_node_p); +static float __synctex_proxy_visible_height(synctex_node_p); +static float __synctex_proxy_visible_depth(synctex_node_p); + +static synctex_vispector_s synctex_vispector_proxy_box = { + &__synctex_proxy_visible_h, + &__synctex_proxy_visible_v, + &__synctex_proxy_visible_width, + &__synctex_proxy_visible_height, + &__synctex_proxy_visible_depth, +}; + +static synctex_class_s synctex_class_proxy_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_proxy_hbox, /* Node type */ + &_synctex_new_proxy_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_proxy, /* log */ + &_synctex_display_proxy_hbox, /* display */ + &_synctex_abstract_proxy_hbox, /* abstract */ + &synctex_tree_model_proxy_hbox, /* tree model */ + &synctex_data_model_proxy, /* data model */ + &synctex_tlcpector_proxy, /* tlcpector */ + &synctex_inspector_proxy_box, /* inspector */ + &synctex_vispector_proxy_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark vbox proxy. +# endif + +/* A proxy to a vbox. */ + +static const synctex_tree_model_s synctex_tree_model_proxy_vbox = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + synctex_tree_sp_child_idx, /* child */ + synctex_tree_spc_friend_idx, /* friend */ + synctex_tree_spcf_last_idx, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + synctex_tree_spcfl_target_idx, /* target */ + synctex_tree_spcflt_proxy_vbox_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spcflt_proxy_vbox_max+synctex_data_proxy_hv_max]; +} synctex_node_proxy_vbox_s; + +/* box proxy node creator */ +DEFINE_synctex_new_unscanned_NODE(proxy_vbox) + +static void _synctex_log_proxy(synctex_node_p node); +static char * _synctex_abstract_proxy_vbox(synctex_node_p node); +static void _synctex_display_proxy_vbox(synctex_node_p node); + +static synctex_class_s synctex_class_proxy_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_proxy_vbox, /* Node type */ + &_synctex_new_proxy_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_proxy, /* log */ + &_synctex_display_proxy_vbox, /* display */ + &_synctex_abstract_proxy_vbox, /* abstract */ + &synctex_tree_model_proxy_vbox, /* tree model */ + &synctex_data_model_proxy, /* data model */ + &synctex_tlcpector_proxy, /* tlcpector */ + &synctex_inspector_proxy_box, /* inspector */ + &synctex_vispector_proxy_box, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark proxy. +# endif + +/** + * A proxy to a node but a box. + */ + +static const synctex_tree_model_s synctex_tree_model_proxy = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + -1, /* child */ + synctex_tree_sp_friend_idx, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + synctex_tree_spf_target_idx,/* target */ + synctex_tree_spft_proxy_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spft_proxy_max+synctex_data_proxy_hv_max]; +} synctex_node_proxy_s; + +/* proxy node creator */ +DEFINE_synctex_new_unscanned_NODE(proxy) + +static void _synctex_log_proxy(synctex_node_p node); +static char * _synctex_abstract_proxy(synctex_node_p node); +static void _synctex_display_proxy(synctex_node_p node); + +static synctex_vispector_s synctex_vispector_proxy = { + &__synctex_proxy_visible_h, + &__synctex_proxy_visible_v, + &__synctex_proxy_visible_width, + &_synctex_float_none, + &_synctex_float_none, +}; + +static synctex_class_s synctex_class_proxy = { + NULL, /* No scanner yet */ + synctex_node_type_proxy, /* Node type */ + &_synctex_new_proxy, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_proxy, /* log */ + &_synctex_display_proxy, /* display */ + &_synctex_abstract_proxy, /* abstract */ + &synctex_tree_model_proxy, /* tree model */ + &synctex_data_model_proxy, /* data model */ + &synctex_tlcpector_proxy, /* tlcpector */ + &synctex_inspector_proxy_box, /* inspector */ + &synctex_vispector_proxy, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark last proxy. +# endif + +/** + * A proxy to the last proxy/box boundary. + */ + +static const synctex_tree_model_s synctex_tree_model_proxy_last = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + -1, /* child */ + synctex_tree_sp_friend_idx, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + synctex_tree_spf_arg_sibling_idx, /* arg_sibling */ + synctex_tree_spfa_target_idx, /* target */ + synctex_tree_spfat_proxy_last_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spfat_proxy_last_max+synctex_data_proxy_hv_max]; +} synctex_node_proxy_last_s; + +/* proxy node creator */ +DEFINE_synctex_new_unscanned_NODE(proxy_last) + +static void _synctex_log_proxy(synctex_node_p node); +static char * _synctex_abstract_proxy(synctex_node_p node); +static void _synctex_display_proxy(synctex_node_p node); + +static synctex_class_s synctex_class_proxy_last = { + NULL, /* No scanner yet */ + synctex_node_type_proxy_last, /* Node type */ + &_synctex_new_proxy, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_proxy, /* log */ + &_synctex_display_proxy, /* display */ + &_synctex_abstract_proxy, /* abstract */ + &synctex_tree_model_proxy_last, /* tree model */ + &synctex_data_model_proxy, /* data model */ + &synctex_tlcpector_proxy, /* tlcpector */ + &synctex_inspector_proxy_box, /* inspector */ + &synctex_vispector_proxy, /* vispector */ +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark handle. +# endif + +/** + * A handle node. + * A handle is never the target of a proxy + * or another handle. + * The child of a handle is always a handle if any. + * The sibling of a handle is always a handle if any. + * The parent of a handle is always a handle if any. + */ + +static const synctex_tree_model_s synctex_tree_model_handle = { + synctex_tree_sibling_idx, /* sibling */ + synctex_tree_s_parent_idx, /* parent */ + synctex_tree_sp_child_idx, /* child */ + -1, /* friend */ + -1, /* last */ + -1, /* next_hbox */ + -1, /* arg_sibling */ + synctex_tree_spc_target_idx,/* target */ + synctex_tree_spct_handle_max +}; + +static const synctex_data_model_s synctex_data_model_handle = { + -1, /* tag */ + -1, /* line */ + -1, /* column */ + -1, /* h */ + -1, /* v */ + -1, /* width */ + -1, /* height */ + -1, /* depth */ + -1, /* mean_line */ + synctex_data_handle_w_idx, /* weight */ + -1, /* h_V */ + -1, /* v_V */ + -1, /* width_V */ + -1, /* height_V */ + -1, /* depth_V */ + -1, /* name */ + -1, /* page */ + synctex_data_handle_w_max +}; + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_p class_; + synctex_data_u data[synctex_tree_spct_handle_max+synctex_data_handle_w_max]; +} synctex_node_handle_s; + +/* handle node creator */ +DEFINE_synctex_new_unscanned_NODE(handle) + +static void _synctex_log_handle(synctex_node_p node); +static char * _synctex_abstract_handle(synctex_node_p node); +static void _synctex_display_handle(synctex_node_p node); + +static synctex_class_s synctex_class_handle = { + NULL, /* No scanner yet */ + synctex_node_type_handle, /* Node type */ + &_synctex_new_handle, /* creator */ + &_synctex_free_handle, /* destructor */ + &_synctex_log_handle, /* log */ + &_synctex_display_handle, /* display */ + &_synctex_abstract_handle, /* abstract */ + &synctex_tree_model_handle, /* tree model */ + &synctex_data_model_handle, /* data model */ + &synctex_tlcpector_proxy, /* tlcpector */ + &synctex_inspector_proxy_box, /* inspector */ + &synctex_vispector_proxy_box, /* vispector */ +}; + +SYNCTEX_INLINE static synctex_node_p _synctex_new_handle_with_target(synctex_node_p target) { + if (target) { + synctex_node_p result = _synctex_new_handle(target->class_->scanner); + if (result) { + _synctex_tree_set_target(result,target); + return result; + } + } + return NULL; +} +SYNCTEX_INLINE static synctex_node_p _synctex_new_handle_with_child(synctex_node_p child) { + if (child) { + synctex_node_p result = _synctex_new_handle(child->class_->scanner); + if (result) { + _synctex_tree_set_child(result,child); + return result; + } + } + return NULL; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Navigation +# endif +synctex_node_p synctex_node_parent(synctex_node_p node) +{ + return _synctex_tree_parent(node); +} +synctex_node_p synctex_node_parent_sheet(synctex_node_p node) +{ + while(node && synctex_node_type(node) != synctex_node_type_sheet) { + node = _synctex_tree_parent(node); + } + /* exit the while loop either when node is NULL or node is a sheet */ + return node; +} +synctex_node_p synctex_node_parent_form(synctex_node_p node) +{ + while(node && synctex_node_type(node) != synctex_node_type_form) { + node = _synctex_tree_parent(node); + } + /* exit the while loop either when node is NULL or node is a form */ + return node; +} + +/** + * The returned proxy will be the child or a sibling of source. + * The returned proxy has no parent, child nor sibling. + * Used only by __synctex_replace_ref. + * argument to_node: a box, not a proxy nor anything else. + */ +SYNCTEX_INLINE static synctex_node_p __synctex_new_proxy_from_ref_to(synctex_node_p ref, synctex_node_p to_node) { + synctex_node_p proxy = NULL; + if (!ref || !to_node) { + return NULL; + } + switch(synctex_node_type(to_node)) { + case synctex_node_type_vbox: + proxy = _synctex_new_proxy_vbox(ref->class_->scanner); + break; + case synctex_node_type_hbox: + proxy = _synctex_new_proxy_hbox(ref->class_->scanner); + break; + default: + _synctex_error("! __synctex_new_proxy_from_ref_to. Unexpected form child (%s). Please report.", synctex_node_isa(to_node)); + return NULL; + } + if (!proxy) { + _synctex_error("! __synctex_new_proxy_from_ref_to. Internal error. Please report."); + return NULL; + } + _synctex_data_set_h(proxy, _synctex_data_h(ref)); + _synctex_data_set_v(proxy, _synctex_data_v(ref)-_synctex_data_height(to_node)); + _synctex_tree_set_target(proxy,to_node); +# if defined(SYNCTEX_USE_CHARINDEX) + proxy->line_index=to_node?to_node->line_index:0; + proxy->char_index=to_node?to_node->char_index:0; +# endif + return proxy; +} +/** + * The returned proxy will be the child or a sibling of owning_proxy. + * The returned proxy has no parent, nor child. + * Used only by synctex_node_child and synctex_node_sibling + * to create proxies on the fly. + * If the to_node has an already computed sibling, + * then the returned proxy has itself a sibling + * pointing to that already computed sibling. + */ +SYNCTEX_INLINE static synctex_node_p __synctex_new_child_proxy_to(synctex_node_p owner, synctex_node_p to_node) { + synctex_node_p proxy = NULL; + synctex_node_p target = to_node; + if (!owner) { + return NULL; + } + switch(synctex_node_type(target)) { + case synctex_node_type_vbox: + if ((proxy = _synctex_new_proxy_vbox(owner->class_->scanner))) { + exit_standard: + _synctex_data_set_h(proxy, _synctex_data_h(owner)); + _synctex_data_set_v(proxy, _synctex_data_v(owner)); + exit0: + _synctex_tree_set_target(proxy,target); +# if defined(SYNCTEX_USE_CHARINDEX) + proxy->line_index=to_node?to_node->line_index:0; + proxy->char_index=to_node?to_node->char_index:0; +# endif + return proxy; + }; + break; + case synctex_node_type_proxy_vbox: + if ((proxy = _synctex_new_proxy_vbox(owner->class_->scanner))) { + exit_proxy: + target = _synctex_tree_target(to_node); + _synctex_data_set_h(proxy, _synctex_data_h(owner)+_synctex_data_h(to_node)); + _synctex_data_set_v(proxy, _synctex_data_v(owner)+_synctex_data_v(to_node)); + goto exit0; + }; + break; + case synctex_node_type_hbox: + if ((proxy = _synctex_new_proxy_hbox(owner->class_->scanner))) { + goto exit_standard; + }; + break; + case synctex_node_type_proxy_hbox: + if ((proxy = _synctex_new_proxy_hbox(owner->class_->scanner))) { + goto exit_proxy; + }; + break; + case synctex_node_type_proxy: + case synctex_node_type_proxy_last: + if ((proxy = _synctex_new_proxy(owner->class_->scanner))) { + goto exit_proxy; + }; + break; + default: + if ((proxy = _synctex_new_proxy(owner->class_->scanner))) { + goto exit_standard; + }; + break; + } + _synctex_error("! __synctex_new_child_proxy_to. " + "Internal error. " + "Please report."); + return NULL; +} +SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_sibling(synctex_node_p node, synctex_node_p new_sibling); +typedef struct synctex_nns_t { + synctex_node_p first; + synctex_node_p last; + synctex_status_t status; +} synctex_nns_s; +/** + * Given a target node, create a list of proxies. + * The first proxy points to the target node, + * its sibling points to the target's sibling and so on. + * Returns the first created proxy, the last one and + * an error status. + */ +SYNCTEX_INLINE static synctex_nns_s _synctex_new_child_proxies_to(synctex_node_p owner, synctex_node_p to_node) { + synctex_nns_s nns = {NULL,NULL,SYNCTEX_STATUS_OK}; + if ((nns.first = nns.last = __synctex_new_child_proxy_to(owner,to_node))) { + synctex_node_p to_next_sibling = __synctex_tree_sibling(to_node); + synctex_node_p to_sibling; + while ((to_sibling = to_next_sibling)) { + synctex_node_p sibling; + if ((to_next_sibling = __synctex_tree_sibling(to_sibling))) { + /* This is not the last sibling */ + if((sibling = __synctex_new_child_proxy_to(owner,to_sibling))) { + _synctex_tree_set_sibling(nns.last,sibling); + nns.last = sibling; + continue; + } else { + _synctex_error("! _synctex_new_child_proxy_to. " + "Internal error (1). " + "Please report."); + nns.status = SYNCTEX_STATUS_ERROR; + } + } else if((sibling = _synctex_new_proxy_last(owner->class_->scanner))) { + _synctex_tree_set_sibling(nns.last,sibling); + nns.last = sibling; + _synctex_data_set_h(nns.last, _synctex_data_h(nns.first)); + _synctex_data_set_v(nns.last, _synctex_data_v(nns.first)); + _synctex_tree_set_target(nns.last,to_sibling); +# if defined(SYNCTEX_USE_CHARINDEX) + nns.last->line_index=to_sibling->line_index; + nns.last->char_index=to_sibling->char_index; +# endif + } else { + _synctex_error("! _synctex_new_child_proxy_to. " + "Internal error (2). " + "Please report."); + nns.status = SYNCTEX_STATUS_ERROR; + } + break; + } + } + return nns; +} +static char * _synctex_node_abstract(synctex_node_p node); +SYNCTEX_INLINE static synctex_node_p synctex_tree_set_friend(synctex_node_p node,synctex_node_p new_friend) { +#if SYNCTEX_DEBUG + synctex_node_p F = new_friend; + while (F) { + if (node == F) { + printf("THIS IS AN ERROR\n"); + F = new_friend; + while (F) { + printf("%s\n",_synctex_node_abstract(F)); + if (node == F) { + return NULL; + } + F = _synctex_tree_friend(F); + } + return NULL; + } + F = _synctex_tree_friend(F); + } +#endif + return new_friend?_synctex_tree_set_friend(node,new_friend):_synctex_tree_reset_friend(node); +} +/** + * + */ +SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend(synctex_node_p node, int i) { + synctex_node_p old = NULL; + if (i>=0) { + i = i%(node->class_->scanner->number_of_lists); + old = synctex_tree_set_friend(node,(node->class_->scanner->lists_of_friends)[i]); + (node->class_->scanner->lists_of_friends)[i] = node; +#if SYNCTEX_DEBUG>500 + printf("tl(%i)=>",i); + synctex_node_log(node); + if (synctex_node_parent_form(node)) { + printf("! ERROR. No registration expected!\n"); + } +#endif + } + return old; +} +/** + * All proxies have tlc attributes, on behalf of their target. + * The purpose is to register all af them. + * - argument node: is the proxy, must not be NULL + */ +SYNCTEX_INLINE static synctex_node_p __synctex_proxy_make_friend_and_next_hbox(synctex_node_p node) { + synctex_node_p old = NULL; + synctex_node_p target = _synctex_tree_target(node); + if (target) { + int i = _synctex_data_tag(target)+_synctex_data_line(target); + old = __synctex_node_make_friend(node,i); + } else { + old = __synctex_tree_reset_friend(node); + } + if (synctex_node_type(node) == synctex_node_type_proxy_hbox) { + synctex_node_p sheet = synctex_node_parent_sheet(node); + if (sheet) { + _synctex_tree_set_next_hbox(node,_synctex_tree_next_hbox(sheet)); + _synctex_tree_set_next_hbox(sheet,node); + } + } + return old; +} +/** + * Register a node which have tag, line and column. + * - argument node: the node + */ +SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend_tlc(synctex_node_p node) { + int i = synctex_node_tag(node)+synctex_node_line(node); + return __synctex_node_make_friend(node,i); +} +/** + * Register a node which have tag, line and column. + * Does nothing if the argument is NULL. + * Calls __synctex_node_make_friend_tlc. + * - argument node: the node + */ +SYNCTEX_INLINE static void _synctex_node_make_friend_tlc(synctex_node_p node) { + if (node) { + __synctex_node_make_friend_tlc(node); + } +} +static synctex_node_p _synctex_node_set_child(synctex_node_p node, synctex_node_p new_child); +/** + * The (first) child of the node, if any, NULL otherwise. + * At parse time, non void box nodes have children. + * All other nodes have no children. + * In order to support pdf forms, proxies are created + * to place form nodes at real locations. + * Ref nodes are replaced by root proxies targeting + * form contents. If root proxies have no children, + * they are created on the fly as proxies to the + * children of the targeted box. + * As such, proxies created here are targeting a + * node that belongs to a form. + * This is the only place where child proxies are created. + */ +synctex_node_p synctex_node_child(synctex_node_p node) { + synctex_node_p child = NULL; + synctex_node_p target = NULL; + if ((child = _synctex_tree_child(node))) { + return child; + } else if ((target = _synctex_tree_target(node))) { + if ((child = synctex_node_child(target))) { + /* This is a proxy with no child + * which target does have a child. */ + synctex_nns_s nns = _synctex_new_child_proxies_to(node, child); + if (nns.first) { + _synctex_node_set_child(node,nns.first); + return nns.first; + } else { + _synctex_error("! synctex_node_child. Internal inconsistency. Please report."); + } + } + } + return NULL; +} +/* + * Set the parent/child bound. + * Things get complicated when new_child has siblings. + * The caller is responsible for releasing the returned value. + */ +static synctex_node_p _synctex_node_set_child(synctex_node_p parent, synctex_node_p new_child) { + if (parent) { + synctex_node_p old = _synctex_tree_set_child(parent,new_child); + synctex_node_p last_child = NULL; + synctex_node_p child; + if ((child = old)) { + do { + _synctex_tree_reset_parent(child); + } while ((child = __synctex_tree_sibling(child))); + } + if ((child = new_child)) { + do { + _synctex_tree_set_parent(child,parent); + last_child = child; + } while ((child = __synctex_tree_sibling(child))); + } + _synctex_tree_set_last(parent,last_child); + return old; + } + return NULL; +} + +/* The last child of the given node, or NULL. + */ +synctex_node_p synctex_node_last_child(synctex_node_p node) { + return _synctex_tree_last(node); +} +/** + * All nodes siblings are properly set up at parse time + * except for non root proxies. + */ +synctex_node_p synctex_node_sibling(synctex_node_p node) { + return node? __synctex_tree_sibling(node): NULL; +} +/** + * All the _synctex_tree_... methods refer to the tree model. + * __synctex_tree_... methods are low level. + */ +/** + * Replace the sibling. + * Connect to the arg_sibling of the new_sibling if relevant. + * - returns the old sibling. + * The caller is responsible for releasing the old sibling. + * The bound to the parent is managed below. + */ +SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_sibling(synctex_node_p node, synctex_node_p new_sibling) { + if (node == new_sibling) { + printf("BOF\n"); + } + synctex_node_p old = node? __synctex_tree_set_sibling(node,new_sibling): NULL; + _synctex_tree_set_arg_sibling(new_sibling,node); + return old; +} +/** + * Replace the sibling. + * Set the parent of the new sibling (and further siblings) + * to the parent of the receiver. + * Also set the last sibling of parent. + * - argument new_sibling: must not be NULL. + * - returns the old sibling. + * The caller is responsible for releasing the old sibling. + */ +static synctex_node_p _synctex_node_set_sibling(synctex_node_p node, synctex_node_p new_sibling) { + if (node && new_sibling) { + synctex_node_p old = _synctex_tree_set_sibling(node,new_sibling); + if (_synctex_tree_has_parent(node)) { + synctex_node_p parent = __synctex_tree_parent(node); + if (parent) { + synctex_node_p N = new_sibling; + while (synctex_YES) { + if (_synctex_tree_has_parent(N)) { + __synctex_tree_set_parent(N,parent); + _synctex_tree_set_last(parent,N); + N = __synctex_tree_sibling(N); + continue; + } else if (N) { + _synctex_error("! synctex_node_sibling. " + "Internal inconsistency. " + "Please report."); + } + break; + } + } + } + return old; + } + return NULL; +} +/** + * The last sibling of the given node, or NULL with node. + */ +synctex_node_p synctex_node_last_sibling(synctex_node_p node) { + synctex_node_p sibling; + do { + sibling = node; + } while((node = synctex_node_sibling(node))); + return sibling; +} +/** + * The next nodes corresponds to a deep first tree traversal. + * Does not create child proxies as side effect contrary to + * the synctex_node_next method above. + * May loop infinitely many times if the tree + * is not properly built (contains loops). + */ +SYNCTEX_INLINE static synctex_node_p _synctex_node_sibling_or_parents(synctex_node_p node) { + while (node) { + synctex_node_p N; + if ((N = __synctex_tree_sibling(node))) { + return N; + } else if ((node = _synctex_tree_parent(node))) { + if (synctex_node_type(node) == synctex_node_type_sheet) {/* EXC_BAD_ACCESS? */ + return NULL; + } else if (synctex_node_type(node) == synctex_node_type_form) { + return NULL; + } + } else { + return NULL; + } + } + return NULL; +} +/** + * The next nodes corresponds to a deep first tree traversal. + * Creates child proxies as side effect. + * May loop infinitely many times if the tree + * is not properly built (contains loops). + */ +synctex_node_p synctex_node_next(synctex_node_p node) { + synctex_node_p N = synctex_node_child(node); + if (N) { + return N; + } + return _synctex_node_sibling_or_parents(node); +} +/** + * The next nodes corresponds to a deep first tree traversal. + * Does not create child proxies as side effect contrary to + * the synctex_node_next method above. + * May loop infinitely many times if the tree + * is not properly built (contains loops). + */ +synctex_node_p _synctex_node_next(synctex_node_p node) { + synctex_node_p N = _synctex_tree_child(node); + if (N) { + return N; + } + return _synctex_node_sibling_or_parents(node); +} +/** + * The node which argument is the sibling. + * - return: NULL if the argument has no parent or + * is the first child of its parent. + * - Input nodes have no arg siblings + */ +synctex_node_p synctex_node_arg_sibling(synctex_node_p node) { +#if 1 + return _synctex_tree_arg_sibling(node); +#else + synctex_node_p N = _synctex_tree_parent(node); + if ((N = _synctex_tree_child(N))) { + do { + synctex_node_p NN = __synctex_tree_sibling(N); + if (NN == node) { + return N; + } + N = NN; + } while (N); + } + return N; +#endif +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark CLASS +# endif + +/* Public node accessor: the type */ +synctex_node_type_t synctex_node_type(synctex_node_p node) { + return node? node->class_->type: synctex_node_type_none; +} + +/* Public node accessor: the type */ +synctex_node_type_t synctex_node_target_type(synctex_node_p node) { + synctex_node_p target = _synctex_tree_target(node); + if (target) { + return (((target)->class_))->type; + } else if (node) { + return (((node)->class_))->type; + } + return synctex_node_type_none; +} + +/* Public node accessor: the human readable type */ +const char * synctex_node_isa(synctex_node_p node) { + static const char * isa[synctex_node_number_of_types] = + {"Not a node", + "input", + "sheet", + "form", + "ref", + "vbox", + "void vbox", + "hbox", + "void hbox", + "kern", + "glue", + "rule", + "math", + "boundary", + "box_bdry", + "proxy", + "last proxy", + "vbox proxy", + "hbox proxy", + "handle"}; + return isa[synctex_node_type(node)]; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark LOG +# endif + +/* Public node logger */ +void synctex_node_log(synctex_node_p node) { + SYNCTEX_MSG_SEND(node,log); +} + +static void _synctex_log_input(synctex_node_p node) { + if (node) { + printf("%s:%i,%s(%i)\n",synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_name(node), + _synctex_data_line(node)); + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n", + (void *)__synctex_tree_sibling(node)); + } +} + +static void _synctex_log_sheet(synctex_node_p node) { + if (node) { + printf("%s:%i",synctex_node_isa(node),_synctex_data_page(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); + } +} + +static void _synctex_log_form(synctex_node_p node) { + if (node) { + printf("%s:%i",synctex_node_isa(node),_synctex_data_tag(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + } +} + +static void _synctex_log_ref(synctex_node_p node) { + if (node) { + printf("%s:%i:%i,%i", + synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_h(node), + _synctex_data_v(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + } +} + +static void _synctex_log_tlchv_node(synctex_node_p node) { + if (node) { + printf("%s:%i,%i,%i:%i,%i", + synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_column(node), + _synctex_data_h(node), + _synctex_data_v(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + } +} + +static void _synctex_log_kern_node(synctex_node_p node) { + if (node) { + printf("%s:%i,%i,%i:%i,%i:%i", + synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_column(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + } +} + +static void _synctex_log_rule(synctex_node_p node) { + if (node) { + printf("%s:%i,%i,%i:%i,%i", + synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_column(node), + _synctex_data_h(node), + _synctex_data_v(node)); + printf(":%i",_synctex_data_width(node)); + printf(",%i",_synctex_data_height(node)); + printf(",%i",_synctex_data_depth(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + } +} + +static void _synctex_log_void_box(synctex_node_p node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",_synctex_data_tag(node)); + printf(",%i",_synctex_data_line(node)); + printf(",%i",_synctex_data_column(node)); + printf(":%i",_synctex_data_h(node)); + printf(",%i",_synctex_data_v(node)); + printf(":%i",_synctex_data_width(node)); + printf(",%i",_synctex_data_height(node)); + printf(",%i",_synctex_data_depth(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + } +} + +static void _synctex_log_vbox(synctex_node_p node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",_synctex_data_tag(node)); + printf(",%i",_synctex_data_line(node)); + printf(",%i",_synctex_data_column(node)); + printf(":%i",_synctex_data_h(node)); + printf(",%i",_synctex_data_v(node)); + printf(":%i",_synctex_data_width(node)); + printf(",%i",_synctex_data_height(node)); + printf(",%i",_synctex_data_depth(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); + } +} + +static void _synctex_log_hbox(synctex_node_p node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",_synctex_data_tag(node)); + printf(",%i~%i*%i",_synctex_data_line(node),_synctex_data_mean_line(node),_synctex_data_weight(node)); + printf(",%i",_synctex_data_column(node)); + printf(":%i",_synctex_data_h(node)); + printf(",%i",_synctex_data_v(node)); + printf(":%i",_synctex_data_width(node)); + printf(",%i",_synctex_data_height(node)); + printf(",%i",_synctex_data_depth(node)); + printf("/%i",_synctex_data_h_V(node)); + printf(",%i",_synctex_data_v_V(node)); + printf(":%i",_synctex_data_width_V(node)); + printf(",%i",_synctex_data_height_V(node)); + printf(",%i",_synctex_data_depth_V(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); + printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); + } +} +static void _synctex_log_proxy(synctex_node_p node) { + if (node) { + synctex_node_p N = _synctex_tree_target(node); + printf("%s",synctex_node_isa(node)); + printf(":%i",_synctex_data_h(node)); + printf(",%i",_synctex_data_v(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); + printf(" ->%s\n",_synctex_node_abstract(N)); + } +} +static void _synctex_log_handle(synctex_node_p node) { + if (node) { + synctex_node_p N = _synctex_tree_target(node); + printf("%s",synctex_node_isa(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + printf("SELF:%p\n",(void *)node); + printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); + printf(" ->%s\n",_synctex_node_abstract(N)); + } +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SYNCTEX_DISPLAY +# endif + +int synctex_scanner_display_switcher(synctex_scanner_p scanR) { + return scanR->display_switcher; +} +void synctex_scanner_set_display_switcher(synctex_scanner_p scanR, int switcher) { + scanR->display_switcher = switcher; +} +static const char * const _synctex_display_prompt = "................................"; + +static char * _synctex_scanner_display_prompt_down(synctex_scanner_p scanR) { + if (scanR->display_prompt>_synctex_display_prompt) { + --scanR->display_prompt; + } + return scanR->display_prompt; +} +static char * _synctex_scanner_display_prompt_up(synctex_scanner_p scanR) { + if (scanR->display_prompt+1<_synctex_display_prompt+strlen(_synctex_display_prompt)) { + ++scanR->display_prompt; + } + return scanR->display_prompt; +} + +void synctex_node_display(synctex_node_p node) { + if (node) { + synctex_scanner_p scanR = node->class_->scanner; + if (scanR) { + if (scanR->display_switcher<0) { + SYNCTEX_MSG_SEND(node, display); + } else if (scanR->display_switcher>0 && --scanR->display_switcher>0) { + SYNCTEX_MSG_SEND(node, display); + } else if (scanR->display_switcher-->=0) { + printf("%s Next display skipped. Reset display switcher.\n",node->class_->scanner->display_prompt); + } + } else { + SYNCTEX_MSG_SEND(node, display); + } + } +} +static char * _synctex_node_abstract(synctex_node_p node) { + SYNCTEX_PARAMETER_ASSERT(node || node->class_); + return (node && node->class_->abstract)? node->class_->abstract(node):"none"; +} + +SYNCTEX_INLINE static void _synctex_display_child(synctex_node_p node) { + synctex_node_p N = _synctex_tree_child(node); + if (N) { + _synctex_scanner_display_prompt_down(N->class_->scanner); + synctex_node_display(N); + _synctex_scanner_display_prompt_up(N->class_->scanner); + } +} + +SYNCTEX_INLINE static void _synctex_display_sibling(synctex_node_p node) { + synctex_node_display(__synctex_tree_sibling(node)); +} +#define SYNCTEX_ABSTRACT_MAX 128 +static char * _synctex_abstract_input(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"Input:%i:%s(%i)" SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_name(node), + _synctex_data_line(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_input(synctex_node_p node) { + if (node) { + printf("Input:%i:%s(%i)" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + _synctex_data_tag(node), + _synctex_data_name(node), + _synctex_data_line(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + synctex_node_display(__synctex_tree_sibling(node)); + } +} + +static char * _synctex_abstract_sheet(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"{%i...}" SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_page(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_sheet(synctex_node_p node) { + if (node) { + printf("%s{%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_page(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s}\n",node->class_->scanner->display_prompt); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_form(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"<%i...>" SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + SYNCTEX_PRINT_CHARINDEX; + } + return abstract; +} + +static void _synctex_display_form(synctex_node_p node) { + if (node) { + printf("%s<%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s>\n",node->class_->scanner->display_prompt); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_vbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"[%i,%i:%i,%i:%i,%i,%i...]" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_vbox(synctex_node_p node) { + if (node) { + printf("%s[%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s]\n%slast:%s\n", + node->class_->scanner->display_prompt, + node->class_->scanner->display_prompt, + _synctex_node_abstract(_synctex_tree_last(node))); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_hbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"(%i,%i~%i*%i:%i,%i:%i,%i,%i...)" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_mean_line(node), + _synctex_data_weight(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_hbox(synctex_node_p node) { + if (node) { + printf("%s(%i,%i~%i*%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_mean_line(node), + _synctex_data_weight(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s)\n%slast:%s\n", + node->class_->scanner->display_prompt, + node->class_->scanner->display_prompt, + _synctex_node_abstract(_synctex_tree_last(node))); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_void_vbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"v%i,%i;%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_void_vbox(synctex_node_p node) { + if (node) { + printf("%sv%i,%i;%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_void_hbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"h%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_void_hbox(synctex_node_p node) { + if (node) { + printf("%sh%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_glue(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"glue:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_glue(synctex_node_p node) { + if (node) { + printf("%sglue:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_rule(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"rule:%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_rule(synctex_node_p node) { + if (node) { + printf("%srule:%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node), + _synctex_data_height(node), + _synctex_data_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_math(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"math:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_math(synctex_node_p node) { + if (node) { + printf("%smath:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_kern(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"kern:%i,%i:%i,%i:%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_kern(synctex_node_p node) { + if (node) { + printf("%skern:%i,%i:%i,%i:%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + _synctex_data_width(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_boundary(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"boundary:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_boundary(synctex_node_p node) { + if (node) { + printf("%sboundary:%i,%i:%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_box_bdry(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"box bdry:%i,%i:%i,%i" SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_box_bdry(synctex_node_p node) { + if (node) { + printf("%sbox bdry:%i,%i:%i,%i", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_line(node), + _synctex_data_h(node), + _synctex_data_v(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_ref(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"form ref:%i:%i,%i" SYNCTEX_PRINT_CHARINDEX_FMT, + _synctex_data_tag(node), + _synctex_data_h(node), + _synctex_data_v(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_ref(synctex_node_p node) { + if (node) { + printf("%sform ref:%i:%i,%i", + node->class_->scanner->display_prompt, + _synctex_data_tag(node), + _synctex_data_h(node), + _synctex_data_v(node)); + SYNCTEX_PRINT_CHARINDEX_NL; + _synctex_display_sibling(node); + } +} +static char * _synctex_abstract_proxy(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + synctex_node_p N = _synctex_tree_target(node); + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"%s:%i,%i:%i,%i/%p%s", + synctex_node_isa(node), + synctex_node_tag(node), + synctex_node_line(node), + _synctex_data_h(node), + _synctex_data_v(node), + node, + _synctex_node_abstract(N)); + } + return abstract; +} +static void _synctex_display_proxy(synctex_node_p node) { + if (node) { + synctex_node_p N = _synctex_tree_target(node); + printf("%s%s:%i,%i:%i,%i", + node->class_->scanner->display_prompt, + synctex_node_isa(node), + synctex_node_tag(node), + synctex_node_line(node), + _synctex_data_h(node), + _synctex_data_v(node)); + if (N) { + printf("=%i,%i:%i,%i,%i->%s", + synctex_node_h(node), + synctex_node_v(node), + synctex_node_width(node), + synctex_node_height(node), + synctex_node_depth(node), + _synctex_node_abstract(N)); + } + printf("\n"); + _synctex_display_child(node); + _synctex_display_sibling(node); + } +} +static char * _synctex_abstract_proxy_vbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX, + "[*%i,%i:%i,%i:%i,%i,%i...*]" + SYNCTEX_PRINT_CHARINDEX_FMT, + synctex_node_tag(node), + synctex_node_line(node), + synctex_node_h(node), + synctex_node_v(node), + synctex_node_width(node), + synctex_node_height(node), + synctex_node_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_proxy_vbox(synctex_node_p node) { + if (node) { + printf("%s[*%i,%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + synctex_node_tag(node), + synctex_node_line(node), + synctex_node_h(node), + synctex_node_v(node), + synctex_node_width(node), + synctex_node_height(node), + synctex_node_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s*]\n%slast:%s\n", + node->class_->scanner->display_prompt, + node->class_->scanner->display_prompt, + _synctex_node_abstract(_synctex_tree_last(node))); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_proxy_hbox(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"(*%i,%i~%i*%i:%i,%i:%i,%i,%i...*)/%p" + SYNCTEX_PRINT_CHARINDEX_FMT, + synctex_node_tag(node), + synctex_node_line(node), + synctex_node_mean_line(node), + synctex_node_weight(node), + synctex_node_h(node), + synctex_node_v(node), + synctex_node_width(node), + synctex_node_height(node), + synctex_node_depth(node), + node + SYNCTEX_PRINT_CHARINDEX_WHAT); + } + return abstract; +} + +static void _synctex_display_proxy_hbox(synctex_node_p node) { + if (node) { + printf("%s(*%i,%i~%i*%i:%i,%i:%i,%i,%i" + SYNCTEX_PRINT_CHARINDEX_FMT + "\n", + node->class_->scanner->display_prompt, + synctex_node_tag(node), + synctex_node_line(node), + synctex_node_mean_line(node), + synctex_node_weight(node), + synctex_node_h(node), + synctex_node_v(node), + synctex_node_width(node), + synctex_node_height(node), + synctex_node_depth(node) + SYNCTEX_PRINT_CHARINDEX_WHAT); + _synctex_display_child(node); + printf("%s*)\n%slast:%s\n", + node->class_->scanner->display_prompt, + node->class_->scanner->display_prompt, + _synctex_node_abstract(_synctex_tree_last(node))); + _synctex_display_sibling(node); + } +} + +static char * _synctex_abstract_handle(synctex_node_p node) { + static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; + if (node) { + synctex_node_p N = _synctex_tree_target(node); + if (N && !N->class_) { + exit(1); + } + snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"%s:%s", + synctex_node_isa(node), + (N?_synctex_node_abstract(N):"")); + } + return abstract; +} +static void _synctex_display_handle(synctex_node_p node) { + if (node) { + synctex_node_p N = _synctex_tree_target(node); + printf("%s%s(%i):->%s\n", + node->class_->scanner->display_prompt, + synctex_node_isa(node), + _synctex_data_weight(N), + _synctex_node_abstract(N)); + _synctex_display_child(node); + _synctex_display_sibling(node); + } +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark STATUS +# endif + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Prototypes +# endif +typedef struct { + size_t size; + synctex_status_t status; +} synctex_zs_s; +static synctex_zs_s _synctex_buffer_get_available_size(synctex_scanner_p scanner, size_t size); +static synctex_status_t _synctex_next_line(synctex_scanner_p scanner); +static synctex_status_t _synctex_match_string(synctex_scanner_p scanner, const char * the_string); + +typedef struct synctex_ns_t { + synctex_node_p node; + synctex_status_t status; +} synctex_ns_s; +static synctex_ns_s __synctex_parse_new_input(synctex_scanner_p scanner); +static synctex_status_t _synctex_scan_preamble(synctex_scanner_p scanner); +typedef struct { + float value; + synctex_status_t status; +} synctex_fs_s; +static synctex_fs_s _synctex_scan_float_and_dimension(synctex_scanner_p scanner); +static synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_p scanner); +static synctex_status_t _synctex_scan_postamble(synctex_scanner_p scanner); +static synctex_status_t _synctex_setup_visible_hbox(synctex_node_p box); +static synctex_status_t _synctex_scan_content(synctex_scanner_p scanner); +int synctex_scanner_pre_x_offset(synctex_scanner_p scanner); +int synctex_scanner_pre_y_offset(synctex_scanner_p scanner); +const char * synctex_scanner_get_output_fmt(synctex_scanner_p scanner); + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SCANNER UTILITIES +# endif + +# define SYNCTEX_FILE (scanner->reader->file) + +/** + * Try to ensure that the buffer contains at least size bytes. + * Passing a huge size argument means the whole buffer length. + * Passing a 0 size argument means return the available buffer length, without reading the file. + * In that case, the return status is always SYNCTEX_STATUS_OK unless the given scanner is NULL. + * The size_t value returned is the number of bytes now available in the buffer. This is a nonnegative integer, it may take the value 0. + * It is the responsibility of the caller to test whether this size is conforming to its needs. + * Negative values may return in case of error, actually + * when there was an error reading the synctex file. + * - parameter scanner: The owning scanner. When NULL, returns SYNCTEX_STATUS_BAD_ARGUMENT. + * - parameter expected: expected number of bytes. + * - returns: a size and a status. + */ +static synctex_zs_s _synctex_buffer_get_available_size(synctex_scanner_p scanner, size_t expected) { + size_t size = 0; + if (NULL == scanner) { + return (synctex_zs_s){0,SYNCTEX_STATUS_BAD_ARGUMENT}; + } + if (expected>scanner->reader->size){ + expected = scanner->reader->size; + } + size = SYNCTEX_END - SYNCTEX_CUR; /* available is the number of unparsed chars in the buffer */ + if (expected<=size) { + /* There are already sufficiently many characters in the buffer */ + return (synctex_zs_s){size,SYNCTEX_STATUS_OK}; + } + if (SYNCTEX_FILE) { + /* Copy the remaining part of the buffer to the beginning, + * then read the next part of the file */ + int already_read = 0; +# if defined(SYNCTEX_USE_CHARINDEX) + scanner->reader->charindex_offset += SYNCTEX_CUR - SYNCTEX_START; +# endif + if (size) { + memmove(SYNCTEX_START, SYNCTEX_CUR, size); + } + SYNCTEX_CUR = SYNCTEX_START + size; /* the next character after the move, will change. */ + /* Fill the buffer up to its end */ + already_read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,(int)(SYNCTEX_BUFFER_SIZE - size)); + if (already_read>0) { + /* We assume that 0<already_read<=SYNCTEX_BUFFER_SIZE - size, such that + * SYNCTEX_CUR + already_read = SYNCTEX_START + size + already_read <= SYNCTEX_START + SYNCTEX_BUFFER_SIZE */ + SYNCTEX_END = SYNCTEX_CUR + already_read; + /* If the end of the file was reached, all the required SYNCTEX_BUFFER_SIZE - available + * may not be filled with values from the file. + * In that case, the buffer should stop properly after already_read characters. */ + * SYNCTEX_END = '\0'; /* there is enough room */ + SYNCTEX_CUR = SYNCTEX_START; + /* May be available is less than size, the caller will have to test. */ + return (synctex_zs_s){SYNCTEX_END - SYNCTEX_CUR,SYNCTEX_STATUS_OK}; + } else if (0>already_read) { + /* There is a possible error in reading the file */ + int errnum = 0; + const char * error_string = gzerror(SYNCTEX_FILE, &errnum); + if (Z_ERRNO == errnum) { + /* There is an error in zlib caused by the file system */ + _synctex_error("gzread error from the file system (%i)",errno); + return (synctex_zs_s){0,SYNCTEX_STATUS_ERROR}; + } else if (errnum) { + _synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string); + return (synctex_zs_s){0,SYNCTEX_STATUS_ERROR}; + } + } + /* Nothing was read, we are at the end of the file. */ + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + SYNCTEX_END = SYNCTEX_CUR; + SYNCTEX_CUR = SYNCTEX_START; + * SYNCTEX_END = '\0';/* Terminate the string properly.*/ + /* there might be a bit of text left */ + return (synctex_zs_s){SYNCTEX_END - SYNCTEX_CUR,SYNCTEX_STATUS_EOF}; + } + /* We cannot enlarge the buffer because the end of the file was reached. */ + return (synctex_zs_s){size,SYNCTEX_STATUS_EOF}; +} + +/* Used when parsing the synctex file. + * Advance to the next character starting a line. + * Actually, only '\n' is recognized as end of line marker. + * On normal completion, the returned value is the number of unparsed characters available in the buffer. + * In general, it is a positive value, 0 meaning that the end of file was reached. + * -1 is returned in case of error, actually because there was an error while feeding the buffer. + * When the function returns with no error, SYNCTEX_CUR points to the first character of the next line, if any. + * J. Laurens: Sat May 10 07:52:31 UTC 2008 + */ +static synctex_status_t _synctex_next_line(synctex_scanner_p scanner) { + synctex_status_t status = SYNCTEX_STATUS_OK; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +infinite_loop: + while(SYNCTEX_CUR<SYNCTEX_END) { + if (*SYNCTEX_CUR == '\n') { + ++SYNCTEX_CUR; + ++scanner->reader->line_number; + return _synctex_buffer_get_available_size(scanner, 1).status; + } + ++SYNCTEX_CUR; + } + /* Here, we have SYNCTEX_CUR == SYNCTEX_END, such that the next call to _synctex_buffer_get_available_size + * will read another bunch of synctex file. Little by little, we advance to the end of the file. */ + status = _synctex_buffer_get_available_size(scanner, 1).status; + if (status<=SYNCTEX_STATUS_EOF) { + return status; + } + goto infinite_loop; +} + +/* Scan the given string. + * Both scanner and the_string must not be NULL, and the_string must not be 0 length. + * SYNCTEX_STATUS_OK is returned if the string is found, + * SYNCTEX_STATUS_EOF is returned when the EOF is reached, + * SYNCTEX_STATUS_NOT_OK is returned is the string is not found, + * an error status is returned otherwise. + * This is a critical method because buffering renders things more difficult. + * The given string might be as long as the maximum size_t value. + * As side effect, the buffer state may have changed if the given argument string can't fit into the buffer. + */ +static synctex_status_t _synctex_match_string(synctex_scanner_p scanner, const char * the_string) { + size_t tested_len = 0; /* the number of characters at the beginning of the_string that match */ + size_t remaining_len = 0; /* the number of remaining characters of the_string that should match */ + size_t available = 0; + synctex_zs_s zs = {0,0}; + if (NULL == scanner || NULL == the_string) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + remaining_len = strlen(the_string); /* All the_string should match */ + if (0 == remaining_len) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* How many characters available in the buffer? */ + zs = _synctex_buffer_get_available_size(scanner,remaining_len); + if (zs.status<SYNCTEX_STATUS_EOF) { + return zs.status; + } + /* Maybe we have less characters than expected because the buffer is too small. */ + if (zs.size>=remaining_len) { + /* The buffer is sufficiently big to hold the expected number of characters. */ + if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) { + return SYNCTEX_STATUS_NOT_OK; + } + return_OK: + /* Advance SYNCTEX_CUR to the next character after the_string. */ + SYNCTEX_CUR += remaining_len; + return SYNCTEX_STATUS_OK; + } else if (strncmp((char *)SYNCTEX_CUR,the_string,zs.size)) { + /* No need to go further, this is not the expected string in the buffer. */ + return SYNCTEX_STATUS_NOT_OK; + } else if (SYNCTEX_FILE) { + /* The buffer was too small to contain remaining_len characters. + * We have to cut the string into pieces. */ + z_off_t offset = 0L; + /* the first part of the string is found, advance the_string to the next untested character. */ + the_string += zs.size; + /* update the remaining length and the parsed length. */ + remaining_len -= zs.size; + tested_len += zs.size; + SYNCTEX_CUR += zs.size; /* We validate the tested characters. */ + if (0 == remaining_len) { + /* Nothing left to test, we have found the given string. */ + return SYNCTEX_STATUS_OK; + } + /* We also have to record the current state of the file cursor because + * if the_string does not match, all this should be a totally blank operation, + * for which the file and buffer states should not be modified at all. + * In fact, the states of the buffer before and after this function are in general different + * but they are totally equivalent as long as the values of the buffer before SYNCTEX_CUR + * can be safely discarded. */ + offset = gztell(SYNCTEX_FILE); + /* offset now corresponds to the first character of the file that was not buffered. */ + /* SYNCTEX_CUR - SYNCTEX_START is the number of chars that where already buffered and + * that match the head of the_string. If in fine the_string does not match, all these chars must be recovered + * because the whole buffer contents is replaced in _synctex_buffer_get_available_size. + * They were buffered from offset-len location in the file. */ + offset -= SYNCTEX_CUR - SYNCTEX_START; + more_characters: + /* There is still some work to be done, so read another bunch of file. + * This is the second call to _synctex_buffer_get_available_size, + * which means that the actual contents of the buffer will be discarded. + * We will definitely have to recover the previous state in case we do not find the expected string. */ + zs = _synctex_buffer_get_available_size(scanner,remaining_len); + if (zs.status<SYNCTEX_STATUS_EOF) { + return zs.status; /* This is an error, no need to go further. */ + } + if (zs.size==0) { + /* Missing characters: recover the initial state of the file and return. */ + return_NOT_OK: + if (offset != gzseek(SYNCTEX_FILE,offset,SEEK_SET)) { + /* This is a critical error, we could not recover the previous state. */ + _synctex_error("Can't seek file"); + return SYNCTEX_STATUS_ERROR; + } + /* Next time we are asked to fill the buffer, + * we will read a complete bunch of text from the file. */ + SYNCTEX_CUR = SYNCTEX_END; + return SYNCTEX_STATUS_NOT_OK; + } + if (zs.size<remaining_len) { + /* We'll have to loop one more time. */ + if (strncmp((char *)SYNCTEX_CUR,the_string,zs.size)) { + /* This is not the expected string, recover the previous state and return. */ + goto return_NOT_OK; + } + /* Advance the_string to the first untested character. */ + the_string += available; + /* update the remaining length and the parsed length. */ + remaining_len -= zs.size; + tested_len += zs.size; + SYNCTEX_CUR += zs.size; /* We validate the tested characters. */ + goto more_characters; + } + /* This is the last step. */ + if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) { + /* This is not the expected string, recover the previous state and return. */ + goto return_NOT_OK; + } + goto return_OK; + } else { + /* The buffer can't contain the given string argument, and the EOF was reached */ + return SYNCTEX_STATUS_EOF; + } +} + +/* Used when parsing the synctex file. + * Decode an integer. + * First, field separators, namely ':' and ',' characters are skipped + * The returned value is negative if there is an unrecoverable error. + * It is SYNCTEX_STATUS_NOT_OK if an integer could not be parsed, for example + * if the characters at the current cursor position are not digits or + * if the end of the file has been reached. + * It is SYNCTEX_STATUS_OK if an int has been successfully parsed. + * The given scanner argument must not be NULL, on the contrary, value_ref may be NULL. + */ +static synctex_is_s _synctex_decode_int(synctex_scanner_p scanner) { + char * ptr = NULL; + char * end = NULL; + synctex_zs_s zs = {0,0}; + int result; + if (NULL == scanner) { + return (synctex_is_s){0, SYNCTEX_STATUS_BAD_ARGUMENT}; + } + zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); + if (zs.status<SYNCTEX_STATUS_EOF) { + return (synctex_is_s){0,zs.status}; + } + if (zs.size==0) { + return (synctex_is_s){0,SYNCTEX_STATUS_NOT_OK}; + } + ptr = SYNCTEX_CUR; + /* Optionally parse the separator */ + if (*ptr==':' || *ptr==',') { + ++ptr; + --zs.size; + if (zs.size==0) { + return (synctex_is_s){0,SYNCTEX_STATUS_NOT_OK}; + } + } + result = (int)strtol(ptr, &end, 10); + if (end>ptr) { + SYNCTEX_CUR = end; + return (synctex_is_s){result,SYNCTEX_STATUS_OK}; + } + return (synctex_is_s){result,SYNCTEX_STATUS_NOT_OK}; +} +static synctex_is_s _synctex_decode_int_opt(synctex_scanner_p scanner, int default_value) { + char * ptr = NULL; + char * end = NULL; + synctex_zs_s zs = {0, 0}; + if (NULL == scanner) { + return (synctex_is_s){default_value, SYNCTEX_STATUS_BAD_ARGUMENT}; + } + zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); + if (zs.status<SYNCTEX_STATUS_EOF) { + return (synctex_is_s){default_value,zs.status}; + } + if (zs.size==0) { + return (synctex_is_s){default_value,SYNCTEX_STATUS_OK}; + } + ptr = SYNCTEX_CUR; + /* Comma separator required */ + if (*ptr==',') { + int result; + ++ptr; + --zs.size; + if (zs.size==0) { + return (synctex_is_s){default_value,SYNCTEX_STATUS_NOT_OK}; + } + result = (int)strtol(ptr, &end, 10); + if (end>ptr) { + SYNCTEX_CUR = end; + return (synctex_is_s){result,SYNCTEX_STATUS_OK}; + } + return (synctex_is_s){default_value,SYNCTEX_STATUS_NOT_OK}; + } + return (synctex_is_s){default_value,SYNCTEX_STATUS_OK}; +} +/* Used when parsing the synctex file. + * Decode an integer for a v field. + * Try the _synctex_decode_int version and set the last v field scanned. + * If it does not succeed, tries to match an '=' sign, + * which is a shortcut for the last v field scanned. + */ +# define SYNCTEX_INPUT_COMEQUALS ",=" +static synctex_is_s _synctex_decode_int_v(synctex_scanner_p scanner) { + synctex_is_s is = _synctex_decode_int(scanner); + if (SYNCTEX_STATUS_OK == is.status) { + scanner->reader->lastv = is.integer; + return is; + } + is.status = _synctex_match_string(scanner,SYNCTEX_INPUT_COMEQUALS); + if (is.status<SYNCTEX_STATUS_OK) { + return is; + } + is.integer = scanner->reader->lastv; + return is; +} + +/* The purpose of this function is to read a string. + * A string is an array of characters from the current parser location + * and before the next '\n' character. + * If a string was properly decoded, it is returned in value_ref and + * the cursor points to the new line marker. + * The returned string was allocated on the heap, the caller is the owner and + * is responsible to free it in due time, + * unless it transfers the ownership to another object. + * If no string is parsed, * value_ref is undefined. + * The maximum length of a string that a scanner can decode is platform dependent, namely UINT_MAX. + * If you just want to blindly parse the file up to the end of the current line, + * use _synctex_next_line instead. + * On return, the scanner cursor is unchanged if a string could not be scanned or + * points to the terminating '\n' character otherwise. As a consequence, + * _synctex_next_line is necessary after. + * If either scanner or value_ref is NULL, it is considered as an error and + * SYNCTEX_STATUS_BAD_ARGUMENT is returned. + */ +static synctex_ss_s _synctex_decode_string(synctex_scanner_p scanner) { + char * end = NULL; + size_t len = 0;/* The number of bytes to copy */ + size_t already_len = 0; + synctex_zs_s zs = {0,0}; + char * string = NULL; + if (NULL == scanner) { + return (synctex_ss_s){NULL,SYNCTEX_STATUS_BAD_ARGUMENT}; + } + /* The buffer must at least contain one character: the '\n' end of line marker */ + if (SYNCTEX_CUR>=SYNCTEX_END) { +more_characters: + zs = _synctex_buffer_get_available_size(scanner,1); + if (zs.status < SYNCTEX_STATUS_EOF) { + return (synctex_ss_s){NULL,zs.status}; + } else if (0 == zs.size) { + return (synctex_ss_s){NULL,SYNCTEX_STATUS_EOF}; + } + } + /* Now we are sure that there is at least one available character, either because + * SYNCTEX_CUR was already < SYNCTEX_END, or because the buffer has been properly filled. */ + /* end will point to the next unparsed '\n' character in the file, when mapped to the buffer. */ + end = SYNCTEX_CUR; + /* We scan all the characters up to the next '\n' */ + while (end<SYNCTEX_END && *end != '\n') { + ++end; + } + /* OK, we found where to stop: + * either end == SYNCTEX_END + * or *end == '\n' */ + len = end - SYNCTEX_CUR; + if (len<UINT_MAX-already_len) { + if ((string = realloc(string,len+already_len+1)) != NULL) { + if (memcpy(string+already_len,SYNCTEX_CUR,len)) { + already_len += len; + string[already_len]='\0'; /* Terminate the string */ + SYNCTEX_CUR += len;/* Eventually advance to the terminating '\n' */ + if (SYNCTEX_CUR==SYNCTEX_END) { + /* No \n found*/ + goto more_characters; + } + /* trim the trailing whites */ + len = already_len; + while (len>0) { + already_len = len--; + if (string[len]!=' ') { + break; + } + } + string[already_len] = '\0'; + return (synctex_ss_s){string,SYNCTEX_STATUS_OK}; + } + free(string); + _synctex_error("could not copy memory (1)."); + return (synctex_ss_s){NULL,SYNCTEX_STATUS_ERROR}; + } + } + _synctex_error("could not (re)allocate memory (1)."); + return (synctex_ss_s){NULL,SYNCTEX_STATUS_ERROR}; +} + +/* Used when parsing the synctex file. + * Read an Input record. + * - parameter scanner: non NULL scanner + * - returns SYNCTEX_STATUS_OK on successful completions, others values otherwise. + */ +static synctex_ns_s __synctex_parse_new_input(synctex_scanner_p scanner) { + synctex_node_p input = NULL; + synctex_status_t status = SYNCTEX_STATUS_BAD_ARGUMENT; + synctex_zs_s zs = {0,0}; + if (NULL == scanner) { + return (synctex_ns_s){NULL,status}; + } + if ((status=_synctex_match_string(scanner,SYNCTEX_INPUT_MARK))<SYNCTEX_STATUS_OK) { + return (synctex_ns_s){NULL,status}; + } + /* Create a node */ + if (NULL == (input = _synctex_new_input(scanner))) { + _synctex_error("Could not create an input node."); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + /* Decode the tag */ + if ((status=_synctex_data_decode_tag(input))<SYNCTEX_STATUS_OK) { + _synctex_error("Bad format of input node."); + synctex_node_free(input); + return (synctex_ns_s){NULL,status}; + } + /* The next character is a field separator, we expect one character in the buffer. */ + zs = _synctex_buffer_get_available_size(scanner, 1); + if (zs.status<=SYNCTEX_STATUS_ERROR) { + return (synctex_ns_s){NULL,status}; + } + if (0 == zs.size) { + return (synctex_ns_s){NULL,SYNCTEX_STATUS_EOF}; + } + /* We can now safely advance to the next character, stepping over the field separator. */ + ++SYNCTEX_CUR; + --zs.size; + /* Then we scan the file name */ + if ((status=_synctex_data_decode_name(input))<SYNCTEX_STATUS_OK) { + synctex_node_free(input); + _synctex_next_line(scanner);/* Ignore this whole line */ + return (synctex_ns_s){NULL,status}; + } + /* Prepend this input node to the input linked list of the scanner */ + __synctex_tree_set_sibling(input,scanner->input);/* input has no parent */ + scanner->input = input; +# if SYNCTEX_VERBOSE + synctex_node_log(input); +# endif + return (synctex_ns_s){input,_synctex_next_line(scanner)};/* read the line termination character, if any */ +} + +typedef synctex_is_s (*synctex_decoder_t)(synctex_scanner_p); + +/* Used when parsing the synctex file. + * Read one of the settings. + * On normal completion, returns SYNCTEX_STATUS_OK. + * On error, returns SYNCTEX_STATUS_ERROR. + * Both arguments must not be NULL. + * On return, the scanner points to the next character after the decoded object whatever it is. + * It is the responsibility of the caller to prepare the scanner for the next line. + */ +static synctex_status_t _synctex_scan_named(synctex_scanner_p scanner,const char * name) { + synctex_status_t status = 0; + if (NULL == scanner || NULL == name) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +not_found: + status = _synctex_match_string(scanner,name); + if (status<SYNCTEX_STATUS_NOT_OK) { + return status; + } else if (status == SYNCTEX_STATUS_NOT_OK) { + status = _synctex_next_line(scanner); + if (status<SYNCTEX_STATUS_OK) { + return status; + } + goto not_found; + } + return SYNCTEX_STATUS_OK; +} + +/* Used when parsing the synctex file. + * Read the preamble. + */ +static synctex_status_t _synctex_scan_preamble(synctex_scanner_p scanner) { + synctex_status_t status = 0; + synctex_is_s is = {0,0}; + synctex_ss_s ss = {NULL,0}; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + status = _synctex_scan_named(scanner,"SyncTeX Version:"); + if (status<SYNCTEX_STATUS_OK) { + return status; + } + is = _synctex_decode_int(scanner); + if (is.status<SYNCTEX_STATUS_OK) { + return is.status; + } + status = _synctex_next_line(scanner); + if (status<SYNCTEX_STATUS_OK) { + return status; + } + scanner->version = is.integer; + /* Read all the input records */ + do { + status = __synctex_parse_new_input(scanner).status; + if (status<SYNCTEX_STATUS_NOT_OK) { + return status; + } + } while(status == SYNCTEX_STATUS_OK); + /* the loop exits when status == SYNCTEX_STATUS_NOT_OK */ + /* Now read all the required settings. */ + if ((status=_synctex_scan_named(scanner,"Output:"))<SYNCTEX_STATUS_OK) { + return status; + } + if ((ss=_synctex_decode_string(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->output_fmt = ss.string; + if ((status=_synctex_scan_named(scanner,"Magnification:"))<SYNCTEX_STATUS_OK) { + return status; + } + if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->pre_magnification = is.integer; + if ((status=_synctex_scan_named(scanner,"Unit:"))<SYNCTEX_STATUS_OK) { + return status; + } + if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->pre_unit = is.integer; + if ((status=_synctex_scan_named(scanner,"X Offset:"))<SYNCTEX_STATUS_OK) { + return status; + } + if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->pre_x_offset = is.integer; + if ((status=_synctex_scan_named(scanner,"Y Offset:"))<SYNCTEX_STATUS_OK) { + return status; + } + if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->pre_y_offset = is.integer; + return SYNCTEX_STATUS_OK; +} + +/* parse a float with a dimension */ +static synctex_fs_s _synctex_scan_float_and_dimension(synctex_scanner_p scanner) { + synctex_fs_s fs = {0,0}; + synctex_zs_s zs = {0,0}; + char * endptr = NULL; +#ifdef HAVE_SETLOCALE + char * loc = setlocale(LC_NUMERIC, NULL); +#endif + if (NULL == scanner) { + return (synctex_fs_s){0,SYNCTEX_STATUS_BAD_ARGUMENT}; + } + zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); + if (zs.status<SYNCTEX_STATUS_EOF) { + _synctex_error("Problem with float."); + return (synctex_fs_s){0,zs.status}; + } +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, "C"); +#endif + fs.value = strtod(SYNCTEX_CUR,&endptr); +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, loc); +#endif + if (endptr == SYNCTEX_CUR) { + _synctex_error("A float was expected."); + return (synctex_fs_s){0,SYNCTEX_STATUS_ERROR}; + } + SYNCTEX_CUR = endptr; + if ((fs.status = _synctex_match_string(scanner,"in")) >= SYNCTEX_STATUS_OK) { + fs.value *= 72.27f*65536; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + report_unit_error: + _synctex_error("problem with unit."); + return fs; + } else if ((fs.status = _synctex_match_string(scanner,"cm")) >= SYNCTEX_STATUS_OK) { + fs.value *= 72.27f*65536/2.54f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) { + fs.value *= 72.27f*65536/25.4f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) { + fs.value *= 65536.0f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) { + fs.value *= 72.27f/72*65536.0f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) { + fs.value *= 12.0*65536.0f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) { + fs.value *= 1.0f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) { + fs.value *= 1238.0f/1157*65536.0f; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) { + fs.value *= 14856.0f/1157*65536; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) { + fs.value *= 685.0f/642*65536; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } else if ((fs.status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) { + fs.value *= 1370.0f/107*65536; + } else if (fs.status<SYNCTEX_STATUS_EOF) { + goto report_unit_error; + } + return fs; +} + +/* parse the post scriptum + * SYNCTEX_STATUS_OK is returned on completion + * a negative error is returned otherwise */ +static synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_p scanner) { + synctex_status_t status = 0; + synctex_fs_s fs = {0,0}; + char * endptr = NULL; +#ifdef HAVE_SETLOCALE + char * loc = setlocale(LC_NUMERIC, NULL); +#endif + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* Scan the file until a post scriptum line is found */ +post_scriptum_not_found: + status = _synctex_match_string(scanner,"Post scriptum:"); + if (status<SYNCTEX_STATUS_NOT_OK) { + return status; + } + if (status == SYNCTEX_STATUS_NOT_OK) { + status = _synctex_next_line(scanner); + if (status<SYNCTEX_STATUS_EOF) { + return status; + } else if (status<SYNCTEX_STATUS_OK) { + return SYNCTEX_STATUS_OK;/* The EOF is found, we have properly scanned the file */ + } + goto post_scriptum_not_found; + } + /* We found the name, advance to the next line. */ +next_line: + status = _synctex_next_line(scanner); + if (status<SYNCTEX_STATUS_EOF) { + return status; + } else if (status<SYNCTEX_STATUS_OK) { + return SYNCTEX_STATUS_OK;/* The EOF is found, we have properly scanned the file */ + } + /* Scanning the information */ + status = _synctex_match_string(scanner,"Magnification:"); + if (status == SYNCTEX_STATUS_OK ) { +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, "C"); +#endif + scanner->unit = strtod(SYNCTEX_CUR,&endptr); +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, loc); +#endif + if (endptr == SYNCTEX_CUR) { + _synctex_error("bad magnification in the post scriptum, a float was expected."); + return SYNCTEX_STATUS_ERROR; + } + if (scanner->unit<=0) { + _synctex_error("bad magnification in the post scriptum, a positive float was expected."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_CUR = endptr; + goto next_line; + } + if (status<SYNCTEX_STATUS_EOF){ + report_record_problem: + _synctex_error("Problem reading the Post Scriptum records"); + return status; /* echo the error. */ + } + status = _synctex_match_string(scanner,"X Offset:"); + if (status == SYNCTEX_STATUS_OK) { + fs = _synctex_scan_float_and_dimension(scanner); + if (fs.status<SYNCTEX_STATUS_OK) { + _synctex_error("Problem with X offset in the Post Scriptum."); + return fs.status; + } + scanner->x_offset = fs.value; + goto next_line; + } else if (status<SYNCTEX_STATUS_EOF){ + goto report_record_problem; + } + status = _synctex_match_string(scanner,"Y Offset:"); + if (status==SYNCTEX_STATUS_OK) { + fs = _synctex_scan_float_and_dimension(scanner); + if (fs.status<SYNCTEX_STATUS_OK) { + _synctex_error("Problem with Y offset in the Post Scriptum."); + return fs.status; + } + scanner->x_offset = fs.value; + goto next_line; + } else if (status<SYNCTEX_STATUS_EOF){ + goto report_record_problem; + } + goto next_line; +} + +/* SYNCTEX_STATUS_OK is returned if the postamble is read + * SYNCTEX_STATUS_NOT_OK is returned if the postamble is not at the current location + * a negative error otherwise + * The postamble comprises the post scriptum section. + */ +static synctex_status_t _synctex_scan_postamble(synctex_scanner_p scanner) { + synctex_status_t status = 0; + synctex_is_s is = {0,0}; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if (!scanner->flags.postamble && (status=_synctex_match_string(scanner,"Postamble:"))<SYNCTEX_STATUS_OK) { + return status; + } +count_again: + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + if ((status=_synctex_scan_named(scanner,"Count:"))< SYNCTEX_STATUS_EOF) { + return status; /* forward the error */ + } else if (status < SYNCTEX_STATUS_OK) { /* No Count record found */ + goto count_again; + } + if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { + return is.status; + } + if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { + return status; + } + scanner->count = is.integer; + /* Now we scan the last part of the SyncTeX file: the Post Scriptum section. */ + return _synctex_scan_post_scriptum(scanner); +} + +/* Horizontal boxes also have visible size. + * Visible size are bigger than real size. + * For example 0 width boxes may contain text. + * At creation time, the visible size is set to the values of the real size. + */ +static synctex_status_t _synctex_setup_visible_hbox(synctex_node_p box) { + if (box) { + switch(synctex_node_type(box)) { + case synctex_node_type_hbox: + _synctex_data_set_h_V(box,_synctex_data_h(box)); + _synctex_data_set_v_V(box,_synctex_data_v(box)); + _synctex_data_set_width_V(box,_synctex_data_width(box)); + _synctex_data_set_height_V(box,_synctex_data_height(box)); + _synctex_data_set_depth_V(box,_synctex_data_depth(box)); + return SYNCTEX_STATUS_OK; + default: + break; + } + } + return SYNCTEX_STATUS_BAD_ARGUMENT; +} + +/* This method is sent to an horizontal box to setup the visible size + * Some box have 0 width but do contain text material. + * With this method, one can enlarge the box to contain the given point (h,v). + */ +static synctex_status_t _synctex_make_hbox_contain_point(synctex_node_p node,synctex_point_s point) { + int min, max, n; + if (NULL == node || synctex_node_type(node) != synctex_node_type_hbox) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if ((n = _synctex_data_width_V(node))<0) { + max = _synctex_data_h_V(node); + min = max+n; + if (point.h<min) { + _synctex_data_set_width_V(node,point.h-max); + } else if (point.h>max) { + _synctex_data_set_h_V(node,point.h); + _synctex_data_set_width_V(node,min-point.h); + } + } else { + min = _synctex_data_h_V(node); + max = min+n; + if (point.h<min) { + _synctex_data_set_h_V(node,point.h); + _synctex_data_set_width_V(node,max - point.h); + } else if (point.h>max) { + _synctex_data_set_width_V(node,point.h - min); + } + } + n = _synctex_data_v_V(node); + min = n - _synctex_data_height_V(node); + max = n + _synctex_data_depth_V(node); + if (point.v<min) { + _synctex_data_set_height_V(node,n-point.v); + } else if (point.v>max) { + _synctex_data_set_depth_V(node,point.v-n); + } + return SYNCTEX_STATUS_OK; +} +static synctex_status_t _synctex_make_hbox_contain_box(synctex_node_p node,synctex_box_s box) { + int min, max, n; + if (NULL == node || synctex_node_type(node) != synctex_node_type_hbox) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if ((n = _synctex_data_width_V(node))<0) { + max = _synctex_data_h_V(node); + min = max+n; + if (box.min.h <min) { + _synctex_data_set_width_V(node,box.min.h-max); + } else if (box.max.h>max) { + _synctex_data_set_h_V(node,box.max.h); + _synctex_data_set_width_V(node,min-box.max.h); + } + } else { + min = _synctex_data_h_V(node); + max = min+n; + if (box.min.h<min) { + _synctex_data_set_h_V(node,box.min.h); + _synctex_data_set_width_V(node,max - box.min.h); + } else if (box.max.h>max) { + _synctex_data_set_width_V(node,box.max.h - min); + } + } + n = _synctex_data_v_V(node); + min = n - _synctex_data_height_V(node); + max = n + _synctex_data_depth_V(node); + if (box.min.v<min) { + _synctex_data_set_height_V(node,n-box.min.v); + } else if (box.max.v>max) { + _synctex_data_set_depth_V(node,box.max.v-n); + } + return SYNCTEX_STATUS_OK; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SPECIAL CHARACTERS +# endif + + +/* Here are the control characters that start each line of the synctex output file. + * Their values define the meaning of the line. + */ +# define SYNCTEX_CHAR_BEGIN_SHEET '{' +# define SYNCTEX_CHAR_END_SHEET '}' +# define SYNCTEX_CHAR_BEGIN_FORM '<' +# define SYNCTEX_CHAR_END_FORM '>' +# define SYNCTEX_CHAR_BEGIN_VBOX '[' +# define SYNCTEX_CHAR_END_VBOX ']' +# define SYNCTEX_CHAR_BEGIN_HBOX '(' +# define SYNCTEX_CHAR_END_HBOX ')' +# define SYNCTEX_CHAR_ANCHOR '!' +# define SYNCTEX_CHAR_VOID_VBOX 'v' +# define SYNCTEX_CHAR_VOID_HBOX 'h' +# define SYNCTEX_CHAR_KERN 'k' +# define SYNCTEX_CHAR_GLUE 'g' +# define SYNCTEX_CHAR_RULE 'r' +# define SYNCTEX_CHAR_MATH '$' +# define SYNCTEX_CHAR_FORM_REF 'f' +# define SYNCTEX_CHAR_BOUNDARY 'x' +# define SYNCTEX_CHAR_CHARACTER 'c' +# define SYNCTEX_CHAR_COMMENT '%' + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SCANNERS & PARSERS +# endif + +# define SYNCTEX_DECODE_FAILED(NODE,WHAT) \ +(_synctex_data_decode_##WHAT(NODE)<SYNCTEX_STATUS_OK) +# define SYNCTEX_DECODE_FAILED_V(NODE,WHAT) \ +(_synctex_data_decode_##WHAT##_v(NODE)<SYNCTEX_STATUS_OK) + +#define SYNCTEX_NS_NULL (synctex_ns_s){NULL,SYNCTEX_STATUS_NOT_OK} +static synctex_ns_s _synctex_parse_new_sheet(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_sheet(scanner))) { + if ( + SYNCTEX_DECODE_FAILED(node,page)) { + _synctex_error("Bad sheet record."); + } else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of sheet."); + } else { + /* Now set the owner */ + if (scanner->sheet) { + synctex_node_p last_sheet = scanner->sheet; + synctex_node_p next_sheet = NULL; + while ((next_sheet = __synctex_tree_sibling(last_sheet))) { + last_sheet = next_sheet; + } + /* sheets have no parent */ + __synctex_tree_set_sibling(last_sheet,node); + } else { + scanner->sheet = node; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_free_node(node); + } + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +/** + * - requirement: scanner != NULL + */ +static synctex_ns_s _synctex_parse_new_form(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_form(scanner))) { + if ( + SYNCTEX_DECODE_FAILED(node,tag)) { + _synctex_error("Bad sheet record."); + } else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of form."); + } else { + /* Now set the owner */ + if (scanner->form) { + synctex_node_p last_form = scanner->form; + synctex_node_p next_form = NULL; + while ((next_form = __synctex_tree_sibling(last_form))) { + last_form = next_form; + } + __synctex_tree_set_sibling(last_form,node); + } else { + scanner->form = node; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_free_node(node); + } + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +# define SYNCTEX_SHOULD_DECODE_FAILED(NODE,WHAT) \ +(_synctex_data_has_##WHAT(NODE) &&(_synctex_data_decode_##WHAT(NODE)<SYNCTEX_STATUS_OK)) +# define SYNCTEX_SHOULD_DECODE_FAILED_V(NODE,WHAT) \ +(_synctex_data_has_##WHAT(NODE) &&(_synctex_data_decode_##WHAT##_v(NODE)<SYNCTEX_STATUS_OK)) + +static synctex_status_t _synctex_data_decode_tlchvwhd(synctex_node_p node) { + return SYNCTEX_SHOULD_DECODE_FAILED(node,tag) + || SYNCTEX_SHOULD_DECODE_FAILED(node,line) + || SYNCTEX_SHOULD_DECODE_FAILED(node,column) + || SYNCTEX_SHOULD_DECODE_FAILED(node,h) + || SYNCTEX_SHOULD_DECODE_FAILED_V(node,v) + || SYNCTEX_SHOULD_DECODE_FAILED(node,width) + || SYNCTEX_SHOULD_DECODE_FAILED(node,height) + || SYNCTEX_SHOULD_DECODE_FAILED(node,depth); +} +static synctex_ns_s _synctex_parse_new_vbox(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_vbox(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad vbox record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of vbox."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend_tlc(synctex_node_p node); +static synctex_ns_s _synctex_parse_new_hbox(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_hbox(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad hbox record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of hbox."); + goto out; + } + if (_synctex_setup_visible_hbox(node)<SYNCTEX_STATUS_OK) { + _synctex_error("Unexpected error (_synctex_parse_new_hbox)."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_void_vbox(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_void_vbox(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad void vbox record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_void_hbox(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_void_hbox(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad void hbox record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_kern(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_kern(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad kern record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_glue(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_glue(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad glue record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_rule(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_rule(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad rule record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_math(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_math(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad math record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +static synctex_ns_s _synctex_parse_new_boundary(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_boundary(scanner))) { + if (_synctex_data_decode_tlchvwhd(node)) { + _synctex_error("Bad boundary record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +SYNCTEX_INLINE static synctex_ns_s _synctex_parse_new_ref(synctex_scanner_p scanner) { + synctex_node_p node; + if ((node = _synctex_new_ref(scanner))) { + if (SYNCTEX_DECODE_FAILED(node,tag) + || SYNCTEX_DECODE_FAILED(node,h) + || SYNCTEX_DECODE_FAILED_V(node,v)) { + _synctex_error("Bad form ref record."); + _synctex_next_line(scanner); + out: + _synctex_free_node(node); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + goto out; + } + return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; + } + _synctex_next_line(scanner); + return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; +} +# undef SYNCTEX_DECODE_FAILED +# undef SYNCTEX_DECODE_FAILED_V + +SYNCTEX_INLINE static synctex_point_s _synctex_data_point(synctex_node_p node); +SYNCTEX_INLINE static synctex_point_s _synctex_data_point_V(synctex_node_p node); +SYNCTEX_INLINE static synctex_point_s _synctex_data_set_point(synctex_node_p node, synctex_point_s point); +SYNCTEX_INLINE static synctex_box_s _synctex_data_box(synctex_node_p node); +SYNCTEX_INLINE static synctex_box_s _synctex_data_xob(synctex_node_p node); +SYNCTEX_INLINE static synctex_box_s _synctex_data_box_V(synctex_node_p node); + +SYNCTEX_INLINE static synctex_node_p _synctex_input_register_line(synctex_node_p input,synctex_node_p node) { + if (node && _synctex_data_tag(input) != _synctex_data_tag(node)) { + input = synctex_scanner_input_with_tag(node->class_->scanner,_synctex_data_tag(node)); + } + if (_synctex_data_line(node)>_synctex_data_line(input)) { + _synctex_data_set_line(input,_synctex_data_line(node)); + } + return input; +} +/** + * Free node and its siblings and return its detached child. + */ +SYNCTEX_INLINE static synctex_node_p _synctex_handle_pop_child(synctex_node_p handle) { + synctex_node_p child = _synctex_tree_reset_child(handle); + synctex_node_free(handle); + return child; +} +/** + * Set the tlc of all the x nodes that are targets of + * x_handle and its sibling. + * Reset the target of x_handle and deletes its siblings. + * child is a node that has just been parsed and is not a boundary node. + */ +SYNCTEX_INLINE static void _synctex_handle_set_tlc(synctex_node_p x_handle, synctex_node_p child, synctex_bool_t make_friend) { + if (x_handle) { + synctex_node_p sibling = x_handle; + if (child) { + synctex_node_p target; + while ((target = synctex_node_target(sibling))) { + _synctex_data_set_tlc(target,child); + if (make_friend) { + _synctex_node_make_friend_tlc(target); + } + if ((sibling = __synctex_tree_sibling(sibling))) { + continue; + } else { + break; + } + } + } + _synctex_tree_reset_target(x_handle); + sibling = __synctex_tree_reset_sibling(x_handle); + synctex_node_free(sibling); + } +} +/** + * When we have parsed a box, we must register + * all the contained heading boundary nodes + * that have not yet been registered. + * Those handles will be deleted when popping. + */ +SYNCTEX_INLINE static void _synctex_handle_make_friend_tlc(synctex_node_p node) { + while (node) { + synctex_node_p target = _synctex_tree_reset_target(node); + _synctex_node_make_friend_tlc(target); + node = __synctex_tree_sibling(node); + } +} +/** + * Scan sheets, forms and input records. + * - parameter scanner: owning scanner + * - returns: status + */ +static synctex_status_t __synctex_parse_sfi(synctex_scanner_p scanner) { + synctex_status_t status = SYNCTEX_STATUS_OK; + synctex_zs_s zs = {0,0}; + synctex_ns_s input = SYNCTEX_NS_NULL; + synctex_node_p sheet = NULL; + synctex_node_p form = NULL; + synctex_node_p parent = NULL; + synctex_node_p child = NULL; + /* + * Experimentations lead to the forthcoming conclusion: + * Sometimes, the first nodes of a box have the wrong line number. + * These are only boundary (x) nodes. + * We observed that boundary nodes do have the proper line number + * if they follow a node with a different type. + * We keep track of these leading x nodes in a handle tree. + */ + synctex_node_p x_handle = NULL; +# define SYNCTEX_RETURN(STATUS) \ + synctex_node_free(x_handle);\ + return STATUS + synctex_node_p last_k = NULL; + synctex_node_p last_g = NULL; + synctex_ns_s ns = SYNCTEX_NS_NULL; + int form_depth = 0; + int ignored_form_depth = 0; + synctex_bool_t try_input = synctex_YES; + if (!(x_handle = _synctex_new_handle(scanner))) { + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } +# ifdef SYNCTEX_NOTHING +# pragma mark MAIN LOOP +# endif +main_loop: + status = SYNCTEX_STATUS_OK; + sheet = form = parent = child = NULL; +# define SYNCTEX_START_SCAN(WHAT)\ +(*SYNCTEX_CUR == SYNCTEX_CHAR_##WHAT) + if (SYNCTEX_CUR<SYNCTEX_END) { + if (SYNCTEX_START_SCAN(BEGIN_FORM)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN FORM +# endif + scan_form: + ns = _synctex_parse_new_form(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + ++form_depth; + if (_synctex_tree_parent(form)) { + /* This form is already being parsed */ + ++ignored_form_depth; + goto ignore_loop; + } + _synctex_tree_set_parent(ns.node,form); + form = ns.node; + parent = form; + child = NULL; + last_k = last_g = NULL; + goto content_loop; + } + if (form || sheet) { + last_k = last_g = NULL; + goto content_loop; + } + try_input = synctex_YES; + goto main_loop; + } else if (SYNCTEX_START_SCAN(BEGIN_SHEET)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN SHEET +# endif + try_input = synctex_YES; + ns = _synctex_parse_new_sheet(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + sheet = ns.node; + parent = sheet; + last_k = last_g = NULL; + goto content_loop; + } + goto main_loop; + } else if (SYNCTEX_START_SCAN(ANCHOR)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN ANCHOR +# endif + scan_anchor: + ++SYNCTEX_CUR; + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing anchor."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + if (form || sheet) { + last_k = last_g = NULL; + goto content_loop; + } + try_input = synctex_YES; + goto main_loop; + } else if (SYNCTEX_START_SCAN(ANCHOR)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN COMMENT +# endif + ++SYNCTEX_CUR; + _synctex_next_line(scanner); + try_input = synctex_YES; + goto main_loop; + } else if (try_input) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN INPUT +# endif + try_input = synctex_NO; + do { + input = __synctex_parse_new_input(scanner); + } while (input.status == SYNCTEX_STATUS_OK); + goto main_loop; + } + status = _synctex_match_string(scanner,"Postamble:"); + if (status==SYNCTEX_STATUS_OK) { + scanner->flags.postamble = 1; + SYNCTEX_RETURN(status); + } + status = _synctex_next_line(scanner); + if (status<SYNCTEX_STATUS_OK) { + SYNCTEX_RETURN(status); + } + } + /* At least 1 more character */ + zs = _synctex_buffer_get_available_size(scanner,1); + if (zs.size == 0){ + _synctex_error("Incomplete synctex file, postamble missing."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + goto main_loop; + /* Unreachable. */ +# ifdef SYNCTEX_NOTHING +# pragma mark IGNORE LOOP +# endif +ignore_loop: + ns = SYNCTEX_NS_NULL; + if (SYNCTEX_CUR<SYNCTEX_END) { + if (SYNCTEX_START_SCAN(BEGIN_FORM)) { + ++ignored_form_depth; + } else if (SYNCTEX_START_SCAN(END_FORM)) { + --ignored_form_depth; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Incomplete container."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + } else { + zs = _synctex_buffer_get_available_size(scanner,1); + if (zs.size == 0){ + _synctex_error("Incomplete synctex file, postamble missing."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + } + if (ignored_form_depth) { + goto ignore_loop; + } else { + last_k = last_g = NULL; + goto content_loop; + } + +# ifdef SYNCTEX_NOTHING +# pragma mark CONTENT LOOP +# endif +content_loop: + /* Either in a form, a sheet or a box. + * - in a sheet, "{" is not possible, only boxes and "}" at top level. + * - in a form, "{" is not possible, only boxes, "<" and ">" at top level. + * - in a box, the unique possibility is '<', '[', '(' or ">". + * We still keep the '(' for a sheet, because that dos not cost too much. + * We must also consider void boxes as children. + */ + /* forms are everywhere */ + ns = SYNCTEX_NS_NULL; +#if SYNCTEX_VERBOSE + synctex_scanner_set_display_switcher(scanner,-1); + printf("NEW CONTENT LOOP\n"); +#if SYNCTEX_DEBUG>500 + synctex_node_display(sheet); +#endif +#endif + if (SYNCTEX_CUR<SYNCTEX_END) { + if (SYNCTEX_START_SCAN(BEGIN_FORM)) { + goto scan_form; + } else if (SYNCTEX_START_SCAN(BEGIN_VBOX)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN VBOX +# endif + ns = _synctex_parse_new_vbox(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + x_handle = _synctex_new_handle_with_child(x_handle); + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + parent = ns.node; + child = _synctex_tree_last(parent); +# if SYNCTEX_VERBOSE + synctex_node_log(parent); +# endif + input.node = _synctex_input_register_line(input.node,parent); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(END_VBOX)) { + if (synctex_node_type(parent) == synctex_node_type_vbox) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN XOBV +# endif + ++SYNCTEX_CUR; + if (NULL == _synctex_tree_child(parent) && !form) { + /* only void v boxes are friends */ + _synctex_node_make_friend_tlc(parent); + } + child = parent; + parent = _synctex_tree_parent(child); + if (!form) { + _synctex_handle_make_friend_tlc(x_handle); + } + x_handle = _synctex_handle_pop_child(x_handle); + _synctex_handle_set_tlc(x_handle,child,!form); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Incomplete container."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(BEGIN_HBOX)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN HBOX +# endif +# if defined(SYNCTEX_USE_CHARINDEX) + synctex_charindex_t char_index = (synctex_charindex_t)(scanner->reader->charindex_offset+SYNCTEX_CUR-SYNCTEX_START); + synctex_lineindex_t line_index = scanner->reader->line_number; +# endif + ns = _synctex_parse_new_hbox(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + x_handle = _synctex_new_handle_with_child(x_handle); + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + parent = ns.node; + /* add a box boundary node at the start */ + if ((child = _synctex_new_box_bdry(scanner))) { +# if defined(SYNCTEX_USE_CHARINDEX) + child->line_index=line_index; + child->char_index=char_index; +# endif + _synctex_node_set_child(parent,child); + _synctex_data_set_tlchv(child,parent); + if (!form) { + __synctex_node_make_friend_tlc(child); + } + } else { + _synctex_error("Can't create box bdry record."); + } +# if SYNCTEX_VERBOSE + synctex_node_log(parent); +# endif + input.node = _synctex_input_register_line(input.node,parent); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(END_HBOX)) { + if (synctex_node_type(parent) == synctex_node_type_hbox) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN XOBH +# endif + ++SYNCTEX_CUR; + /* setting the next horizontal box at the end ensures + * that a child is recorded before any of its ancestors. + */ + if (form == NULL /* && sheet != NULL*/ ) { + _synctex_tree_set_next_hbox(parent,_synctex_tree_next_hbox(sheet)); + _synctex_tree_set_next_hbox(sheet,parent); + } + { + /* Update the mean line number */ + synctex_node_p node = _synctex_tree_child(parent); + synctex_node_p sibling = NULL; + /* Ignore the first node (a box_bdry) */ + if (node && (sibling = __synctex_tree_sibling(node))) { + unsigned int node_weight = 0; + unsigned int cumulated_line_numbers = 0; + _synctex_data_set_line(node, _synctex_data_line(sibling)); + node = sibling; + do { + if (synctex_node_type(node)==synctex_node_type_hbox) { + if (_synctex_data_weight(node)) { + node_weight += _synctex_data_weight(node); + cumulated_line_numbers += _synctex_data_mean_line(node)*_synctex_data_weight(node); + } else { + ++node_weight; + cumulated_line_numbers += _synctex_data_mean_line(node); + } + } else { + ++node_weight; + cumulated_line_numbers += synctex_node_line(node); + } + } while ((node = __synctex_tree_sibling(node))); + _synctex_data_set_mean_line(parent,(cumulated_line_numbers + node_weight/2)/node_weight); + _synctex_data_set_weight(parent,node_weight); + } else { + _synctex_data_set_mean_line(parent,_synctex_data_line(parent)); + _synctex_data_set_weight(parent,1); + } + if ((sibling = _synctex_new_box_bdry(scanner))) { +# if defined(SYNCTEX_USE_CHARINDEX) + sibling->line_index=child->line_index; + sibling->char_index=child->char_index; +# endif + _synctex_node_set_sibling(child,sibling); + { + synctex_node_p N = child; + while (synctex_node_type(N) == synctex_node_type_ref) { + N = _synctex_tree_arg_sibling(N); + } + _synctex_data_set_tlc(sibling,N); + } + _synctex_data_set_h(sibling,_synctex_data_h_V(parent)+_synctex_data_width_V(parent)); + _synctex_data_set_v(sibling,_synctex_data_v_V(parent)); + child = sibling; + } else { + _synctex_error("Can't create box bdry record."); + } + sibling = _synctex_tree_child(parent); + _synctex_data_set_point(sibling,_synctex_data_point_V(parent)); + if (last_k && last_g && (child = synctex_node_child(parent))) { + /* Find the node preceding last_k */ + synctex_node_p next; + while ((next = __synctex_tree_sibling(child))) { + if (next == last_k) { + _synctex_data_set_tlc(last_k,child); + _synctex_data_set_tlc(last_g,child); + break; + } + child = next; + } + } + child = parent; + parent = _synctex_tree_parent(child); + if (!form) { + _synctex_handle_make_friend_tlc(x_handle); + } + x_handle = _synctex_handle_pop_child(x_handle); + _synctex_handle_set_tlc(x_handle,child,!form); + _synctex_make_hbox_contain_box(parent, _synctex_data_box_V(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Incomplete container."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(VOID_VBOX)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN VOID VBOX +# endif + ns = _synctex_parse_new_void_vbox(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + _synctex_handle_set_tlc(x_handle, child,!form); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(VOID_HBOX)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN VOID HBOX +# endif + ns = _synctex_parse_new_void_hbox(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (_synctex_data_width(ns.node)<0) { + printf("Negative width\n"); + } + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + _synctex_handle_set_tlc(x_handle, child,!form); + _synctex_make_hbox_contain_box(parent,_synctex_data_box(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(KERN)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN KERN +# endif + ns = _synctex_parse_new_kern(scanner); + continue_scan: + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + if (!form) { + __synctex_node_make_friend_tlc(child); + } + _synctex_handle_set_tlc(x_handle, child,!form); + _synctex_make_hbox_contain_box(parent,_synctex_data_xob(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = child; + last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(GLUE)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN GLUE +# endif + ns = _synctex_parse_new_glue(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + if (!form) { + __synctex_node_make_friend_tlc(child); + } + _synctex_handle_set_tlc(x_handle, child,!form); + _synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + if (last_k) { + last_g = child; + } else { + last_k = last_g = NULL; + } + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(RULE)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN RULE +# endif + ns = _synctex_parse_new_rule(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + if (!form) { + __synctex_node_make_friend_tlc(child); + } + _synctex_handle_set_tlc(x_handle, child,!form); + /* Rules are sometimes far too big +_synctex_make_hbox_contain_box(parent,_synctex_data_box(child)); + */ +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(MATH)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN MATH +# endif + ns = _synctex_parse_new_math(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + if (!form) { + __synctex_node_make_friend_tlc(child); + } + _synctex_handle_set_tlc(x_handle, child,!form); + _synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(FORM_REF)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN FORM REF +# endif +#if SYNCTEX_DEBUG>500 + synctex_node_display(parent); + synctex_node_display(child); +#endif + ns = _synctex_parse_new_ref(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + child = ns.node; + if (form) { + if (scanner->ref_in_form) { + synctex_tree_set_friend(child,scanner->ref_in_form); + } + scanner->ref_in_form = child; + } else { + if (scanner->ref_in_sheet) { + synctex_tree_set_friend(child,scanner->ref_in_sheet); + } + scanner->ref_in_sheet = child; + } +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(BOUNDARY)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN BOUNDARY +# endif + ns = _synctex_parse_new_boundary(scanner); + if (ns.status == SYNCTEX_STATUS_OK) { + if (child) { + _synctex_node_set_sibling(child,ns.node); + } else { + _synctex_node_set_child(parent,ns.node); + } + if (synctex_node_type(child)==synctex_node_type_box_bdry + || _synctex_tree_target(x_handle)) { + child = _synctex_tree_reset_child(x_handle); + child = _synctex_new_handle_with_child(child); + __synctex_tree_set_sibling(child, x_handle); + x_handle = child; + _synctex_tree_set_target(x_handle,ns.node); + } else if (!form) { + __synctex_node_make_friend_tlc(ns.node); + } + child = ns.node; + _synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + input.node = _synctex_input_register_line(input.node,child); + last_k = last_g = NULL; + goto content_loop; + } + } else if (SYNCTEX_START_SCAN(CHARACTER)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN CHARACTER +# endif + ++SYNCTEX_CUR; + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of container."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + last_k = last_g = NULL; + goto content_loop; + } else if (SYNCTEX_START_SCAN(ANCHOR)) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN ANCHOR +# endif + goto scan_anchor; + } else if (SYNCTEX_START_SCAN(END_SHEET)) { + if (sheet && parent == sheet) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN TEEHS +# endif + ++SYNCTEX_CUR; + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing anchor."); + } + parent = sheet = NULL; + goto main_loop; + } + } else if (SYNCTEX_START_SCAN(END_FORM)) { + if (parent == form && form_depth > 0) { +# ifdef SYNCTEX_NOTHING +# pragma mark + SCAN MROF +# endif + ++SYNCTEX_CUR; + --form_depth; + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK + && (form_depth || sheet)) { + _synctex_error("Missing end of container."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + if ((parent = _synctex_tree_parent(form))) { + _synctex_tree_reset_parent(form); + child = form; + form = parent; + goto content_loop; + } else if (sheet) { + form = NULL; + parent = sheet; + child = synctex_node_last_sibling(child); + goto content_loop; + } + goto main_loop; + } + } + _synctex_error("Ignored record <%.20s...>(line %i)\n",SYNCTEX_CUR, scanner->reader->line_number+1); + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing end of sheet/form."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + last_k = last_g = NULL; + goto content_loop; + } + zs = _synctex_buffer_get_available_size(scanner,1); + if (zs.size == 0){ + _synctex_error("Incomplete synctex file, postamble missing."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + last_k = last_g = NULL; + goto content_loop; +} +#undef SYNCTEX_RETURN +/** + * Replace ref in its tree hierarchy by a single box + * proxy to the contents of the associated form. + * - argument ref: a ref node with no friend + * - return the proxy created. + * - note: Does nothing if ref is not owned. + * - note: On return, ref will have no parent nor sibling. + * The caller is responsible for releasing ref. + * - note: this is where root proxies are created. + * - note: the target of the root proxy is the content + * of a form. + */ +SYNCTEX_INLINE static synctex_ns_s __synctex_replace_ref(synctex_node_p ref) { + synctex_ns_s ns = {NULL,SYNCTEX_STATUS_OK}; + synctex_node_p parent; + if ((parent = _synctex_tree_parent(ref))) { + synctex_node_p sibling = __synctex_tree_reset_sibling(ref); + synctex_node_p arg_sibling = synctex_node_arg_sibling(ref); + /* arg_sibling != NULL because the child of a box + * is always a box boundary, not a ref. */ + synctex_node_p target = synctex_form_content(ref->class_->scanner, _synctex_data_tag(ref)); + /* The target is a single node (box) + * with children and no siblings. */ + if ((ns.node = __synctex_new_proxy_from_ref_to(ref, target))) { + /* Insert this proxy instead of ref. */ + _synctex_node_set_sibling(arg_sibling,ns.node); + /* Then append the original sibling of ref. */ + _synctex_node_set_sibling(ns.node,sibling); +# if defined(SYNCTEX_USE_CHARINDEX) + if (synctex_node_type(sibling) == synctex_node_type_box_bdry) { + /* The sibling is the last box boundary + * which may have a less accurate information */ + sibling->char_index = arg_sibling->char_index; + sibling->line_index = arg_sibling->line_index; + } +#endif +#if SYNCTEX_DEBUG>500 + printf("! Ref replacement:\n"); + synctex_node_log(ref); + synctex_node_display(synctex_node_sibling(ref)); +#endif + } else /* simply remove ref */ { + _synctex_tree_set_sibling(arg_sibling,sibling); + } + __synctex_tree_reset_parent(ref); + } else { + _synctex_error("! Missing parent in __synctex_replace_ref. " + "Please report."); + ns.status = SYNCTEX_STATUS_BAD_ARGUMENT; + } + return ns; +} +/** + * - argument ref: is the starting point of a linked list + * of refs. The link is made through the friend field. + * - returns: the status and the list of all the proxies + * created. The link is made through the friend field. + * - note: All refs are freed + */ +SYNCTEX_INLINE static synctex_ns_s _synctex_post_process_ref(synctex_node_p ref) { + synctex_ns_s ns = {NULL, SYNCTEX_STATUS_OK}; + while (ref) { + synctex_node_p next_ref = _synctex_tree_reset_friend(ref); + synctex_ns_s sub_ns = __synctex_replace_ref(ref); + if (sub_ns.status < ns.status) { + ns.status = sub_ns.status; + } else { + /* Insert all the created proxies in the list + * sub_ns.node is the last friend, + */ + synctex_tree_set_friend(sub_ns.node,ns.node); + ns.node = sub_ns.node; + } + synctex_node_free(ref); + ref = next_ref; + } + return ns; +} +typedef synctex_node_p (* synctex_processor_f)(synctex_node_p node); +/** + * Apply the processor f to the tree hierarchy rooted at proxy. + * proxy has replaced a form ref, no children yet. + * As a side effect all the hierarchy of nodes will be created. + */ +SYNCTEX_INLINE static synctex_status_t _synctex_post_process_proxy(synctex_node_p proxy, synctex_processor_f f) { + while(proxy) { + synctex_node_p next_proxy = _synctex_tree_friend(proxy); + synctex_node_p halt = __synctex_tree_sibling(proxy); + /* if proxy is the last sibling, halt is NULL. + * Find what should be a next node, + * without creating new nodes. */ + if (!halt) { + synctex_node_p parent = _synctex_tree_parent(proxy); + halt = __synctex_tree_sibling(parent); + while (!halt && parent) { + parent = _synctex_tree_parent(parent); + halt = __synctex_tree_sibling(parent); + } + } + do { +#if SYNCTEX_DEBUG>500 + printf("POST PROCESSING %s\n",_synctex_node_abstract(proxy)); + { + int i,j = 0; + for (i=0;i<proxy->class_->scanner->number_of_lists;++i) { + synctex_node_p N = proxy->class_->scanner->lists_of_friends[i]; + do { + if (N==proxy) { + ++j; + printf("%s",_synctex_node_abstract(N)); + } + } while ((N = _synctex_tree_friend(N))); + } + if (j) { + printf("\nBeforehand %i match\n",j); + } + } +#endif + f(proxy); +#if SYNCTEX_DEBUG>500 + { + int i,j = 0; + for (i=0;i<proxy->class_->scanner->number_of_lists;++i) { + synctex_node_p N = proxy->class_->scanner->lists_of_friends[i]; + do { + if (N==proxy) { + ++j; + printf("%s",_synctex_node_abstract(N)); + } + } while ((N = _synctex_tree_friend(N))); + } + if (j) { + printf("\n%i match\n",j); + } + } +#endif + /* Side effect: create the hierarchy on the fly */ + proxy = synctex_node_next(proxy); /* Change is here */ +#if SYNCTEX_DEBUG>500 + if (proxy) { + int i,j = 0; + for (i=0;i<proxy->class_->scanner->number_of_lists;++i) { + synctex_node_p N = proxy->class_->scanner->lists_of_friends[i]; + do { + if (N==proxy) { + ++j; + printf("%s",_synctex_node_abstract(N)); + } + } while ((N = _synctex_tree_friend(N))); + } + if (j) { + printf("\nnext %i match\n",j); + } + } +#endif + } while (proxy && proxy != halt); + proxy = next_proxy; + } + return SYNCTEX_STATUS_OK; +} +/** + * Replace all the form refs by root box proxies. + * Create the node hierarchy and update the friends. + * On entry, the refs are collected as a friend list + * in either a form or a sheet + * - parameter: the owning scanner + */ +SYNCTEX_INLINE static synctex_status_t _synctex_post_process(synctex_scanner_p scanner) { + synctex_status_t status = SYNCTEX_STATUS_OK; + synctex_ns_s ns = {NULL,SYNCTEX_STATUS_NOT_OK}; +#if SYNCTEX_DEBUG>500 + printf("! entering _synctex_post_process.\n"); + synctex_node_display(scanner->sheet); + synctex_node_display(scanner->form); +#endif + /* replace form refs inside forms by box proxies */ + ns = _synctex_post_process_ref(scanner->ref_in_form); + scanner->ref_in_form = NULL;/* it was just released */ + if (ns.status<status) { + status = ns.status; + } +#if SYNCTEX_DEBUG>500 + printf("! ref replaced in form _synctex_post_process.\n"); + synctex_node_display(scanner->form); +#endif + /* Create all the form proxy nodes on the fly. + * ns.node is the root of the list of + * newly created proxies. + * There might be a problem with cascading proxies. + * In order to be properly managed, the data must + * be organized in the right way. + * The inserted form must be defined before + * the inserting one. *TeX will take care of that. */ + ns.status = _synctex_post_process_proxy(ns.node,&_synctex_tree_reset_friend); + if (ns.status<status) { + status = ns.status; + } + /* replace form refs inside sheets by box proxies */ + ns = _synctex_post_process_ref(scanner->ref_in_sheet); + if (ns.status<status) { + status = ns.status; + } + scanner->ref_in_sheet = NULL; +#if SYNCTEX_DEBUG>500 + printf("! ref replaced in sheet _synctex_post_process.\n"); + synctex_node_display(scanner->sheet); +#endif +#if 0 + { + int i; + for (i=0;i<scanner->number_of_lists;++i) { + synctex_node_p P = ns.node; + do { + synctex_node_p N = scanner->lists_of_friends[i]; + do { + if (P == N) { + printf("Already registered.\n"); + synctex_node_display(N); + break; + } + } while ((N = _synctex_tree_friend(N))); + } while((P = _synctex_tree_friend(P))); + } + } +#endif +#if SYNCTEX_DEBUG>10000 + { + int i; + for (i=0;i<scanner->number_of_lists;++i) { + synctex_node_p P = scanner->lists_of_friends[i]; + int j = 0; + while (P) { + ++j; + synctex_node_log(P); + P = _synctex_tree_friend(P); + } + if (j) { + printf("friends %i -> # %i\n",i,j); + } + } + } +#endif + ns.status = _synctex_post_process_proxy(ns.node,&__synctex_proxy_make_friend_and_next_hbox); + if (ns.status<status) { + status = ns.status; + } +#if SYNCTEX_DEBUG>500 + printf("! exiting _synctex_post_process.\n"); + synctex_node_display(scanner->sheet); + synctex_node_display(scanner->form); + printf("! display all.\n"); + synctex_node_display(scanner->sheet); + synctex_node_display(scanner->form); +#endif + return status; +} +/* Used when parsing the synctex file + */ +static synctex_status_t _synctex_scan_content(synctex_scanner_p scanner) { + scanner->reader->lastv = -1; + synctex_status_t status = 0; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* Find where this section starts */ +content_not_found: + status = _synctex_match_string(scanner,"Content:"); + if (status<SYNCTEX_STATUS_EOF) { + return status; + } + if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Incomplete Content."); + return SYNCTEX_STATUS_ERROR; + } + if (status == SYNCTEX_STATUS_NOT_OK) { + goto content_not_found; + } + status = __synctex_parse_sfi(scanner); + if (status == SYNCTEX_STATUS_OK) { + status = _synctex_post_process(scanner); + } + return status; +} +synctex_scanner_p synctex_scanner_new() { + synctex_scanner_p scanner =(synctex_scanner_p)_synctex_malloc(sizeof(synctex_scanner_s)); + if (scanner) { + if (!(scanner->reader = _synctex_malloc(sizeof(synctex_reader_s)))) { + _synctex_free(scanner); + return NULL; + } +# ifdef SYNCTEX_NOTHING +# pragma mark - +# endif +# define DEFINE_synctex_scanner_class(NAME)\ + scanner->class_[synctex_node_type_##NAME] = synctex_class_##NAME;\ +(scanner->class_[synctex_node_type_##NAME]).scanner = scanner + DEFINE_synctex_scanner_class(input); + DEFINE_synctex_scanner_class(sheet); + DEFINE_synctex_scanner_class(form); + DEFINE_synctex_scanner_class(hbox); + DEFINE_synctex_scanner_class(void_hbox); + DEFINE_synctex_scanner_class(vbox); + DEFINE_synctex_scanner_class(void_vbox); + DEFINE_synctex_scanner_class(kern); + DEFINE_synctex_scanner_class(glue); + DEFINE_synctex_scanner_class(rule); + DEFINE_synctex_scanner_class(math); + DEFINE_synctex_scanner_class(boundary); + DEFINE_synctex_scanner_class(box_bdry); + DEFINE_synctex_scanner_class(ref); + DEFINE_synctex_scanner_class(proxy_hbox); + DEFINE_synctex_scanner_class(proxy_vbox); + DEFINE_synctex_scanner_class(proxy); + DEFINE_synctex_scanner_class(proxy_last); + DEFINE_synctex_scanner_class(handle); + /* set up the lists of friends */ + scanner->number_of_lists = 1024; + scanner->lists_of_friends = (synctex_node_r)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_p)); + if (NULL == scanner->lists_of_friends) { + synctex_scanner_free(scanner); + _synctex_error("malloc:2"); + return NULL; + } + scanner->display_switcher = 100; + scanner->display_prompt = (char *)_synctex_display_prompt+strlen(_synctex_display_prompt)-1; + } + return scanner; +} +/* Where the synctex scanner is created. */ +synctex_scanner_p synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) { + synctex_scanner_p scanner = synctex_scanner_new(); + if (NULL == scanner) { + _synctex_error("malloc problem"); + return NULL; + } + if ((scanner->reader = synctex_reader_init_with_output_file(scanner->reader, output, build_directory))) { + return parse? synctex_scanner_parse(scanner):scanner; + } + _synctex_error("No file?"); + return NULL; +} + +/* The scanner destructor + */ +int synctex_scanner_free(synctex_scanner_p scanner) { + int node_count = 0; + if (scanner) { + if (SYNCTEX_FILE) { + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + } + synctex_node_free(scanner->sheet); + synctex_node_free(scanner->form); + synctex_node_free(scanner->input); + synctex_reader_free(scanner->reader); + SYNCTEX_SCANNER_FREE_HANDLE(scanner); + synctex_iterator_free(scanner->iterator); + free(scanner->output_fmt); + free(scanner->lists_of_friends); +#if SYNCTEX_USE_NODE_COUNT>0 + node_count = scanner->node_count; +#endif + free(scanner); + } + return node_count; +} + +/* Where the synctex scanner parses the contents of the file. */ +synctex_scanner_p synctex_scanner_parse(synctex_scanner_p scanner) { + synctex_status_t status = 0; + if (!scanner || scanner->flags.has_parsed) { + return scanner; + } + scanner->flags.has_parsed=1; + scanner->pre_magnification = 1000; + scanner->pre_unit = 8192; + scanner->pre_x_offset = scanner->pre_y_offset = 578; + /* initialize the offset with a fake unprobable value, + * If there is a post scriptum section, this value will be overridden by the real life value */ + scanner->x_offset = scanner->y_offset = 6.027e23f; + scanner->reader->line_number = 1; + + SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /* one more character for null termination */ + if (NULL == SYNCTEX_START) { + _synctex_error("! malloc error in synctex_scanner_parse."); + bailey: +#ifdef SYNCTEX_DEBUG + return scanner; +#else + synctex_scanner_free(scanner); + return NULL; +#endif + } + synctex_scanner_set_display_switcher(scanner, 1000); + SYNCTEX_END = SYNCTEX_START+SYNCTEX_BUFFER_SIZE; + /* SYNCTEX_END always points to a null terminating character. + * Maybe there is another null terminating character between SYNCTEX_CUR and SYNCTEX_END-1. + * At least, we are sure that SYNCTEX_CUR points to a string covering a valid part of the memory. */ + *SYNCTEX_END = '\0'; + SYNCTEX_CUR = SYNCTEX_END; +# if defined(SYNCTEX_USE_CHARINDEX) + scanner->reader->charindex_offset = -SYNCTEX_BUFFER_SIZE; +# endif + status = _synctex_scan_preamble(scanner); + if (status<SYNCTEX_STATUS_OK) { + _synctex_error("Bad preamble\n"); + goto bailey; + } + status = _synctex_scan_content(scanner); + if (status<SYNCTEX_STATUS_OK) { + _synctex_error("Bad content\n"); + goto bailey; + } + status = _synctex_scan_postamble(scanner); + if (status<SYNCTEX_STATUS_OK) { + _synctex_error("Bad postamble. Ignored\n"); + } +#if SYNCTEX_DEBUG>500 + synctex_scanner_set_display_switcher(scanner, 100); + synctex_node_display(scanner->sheet); + synctex_node_display(scanner->form); +#endif + synctex_scanner_set_display_switcher(scanner, 1000); + /* Everything is finished, free the buffer, close the file */ + free((void *)SYNCTEX_START); + SYNCTEX_START = SYNCTEX_CUR = SYNCTEX_END = NULL; + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + /* Final tuning: set the default values for various parameters */ + /* 1 pre_unit = (scanner->pre_unit)/65536 pt = (scanner->pre_unit)/65781.76 bp + * 1 pt = 65536 sp */ + if (scanner->pre_unit<=0) { + scanner->pre_unit = 8192; + } + if (scanner->pre_magnification<=0) { + scanner->pre_magnification = 1000; + } + if (scanner->unit <= 0) { + /* no post magnification */ + scanner->unit = scanner->pre_unit / 65781.76;/* 65781.76 or 65536.0*/ + } else { + /* post magnification */ + scanner->unit *= scanner->pre_unit / 65781.76; + } + scanner->unit *= scanner->pre_magnification / 1000.0; + if (scanner->x_offset > 6e23) { + /* no post offset */ + scanner->x_offset = scanner->pre_x_offset * (scanner->pre_unit / 65781.76); + scanner->y_offset = scanner->pre_y_offset * (scanner->pre_unit / 65781.76); + } else { + /* post offset */ + scanner->x_offset /= 65781.76f; + scanner->y_offset /= 65781.76f; + } + return scanner; +#undef SYNCTEX_FILE +} + +/* Scanner accessors. + */ +int synctex_scanner_pre_x_offset(synctex_scanner_p scanner){ + return scanner?scanner->pre_x_offset:0; +} +int synctex_scanner_pre_y_offset(synctex_scanner_p scanner){ + return scanner?scanner->pre_y_offset:0; +} +int synctex_scanner_x_offset(synctex_scanner_p scanner){ + return scanner?scanner->x_offset:0; +} +int synctex_scanner_y_offset(synctex_scanner_p scanner){ + return scanner?scanner->y_offset:0; +} +float synctex_scanner_magnification(synctex_scanner_p scanner){ + return scanner?scanner->unit:1; +} +void synctex_scanner_display(synctex_scanner_p scanner) { + if (NULL == scanner) { + return; + } + printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->reader->output,scanner->output_fmt,scanner->version); + printf("pre_unit:%i\nx_offset:%i\ny_offset:%i\n",scanner->pre_unit,scanner->pre_x_offset,scanner->pre_y_offset); + printf("count:%i\npost_magnification:%f\npost_x_offset:%f\npost_y_offset:%f\n", + scanner->count,scanner->unit,scanner->x_offset,scanner->y_offset); + printf("The input:\n"); + synctex_node_display(scanner->input); + if (scanner->count<1000) { + printf("The sheets:\n"); + synctex_node_display(scanner->sheet); + printf("The friends:\n"); + if (scanner->lists_of_friends) { + int i = scanner->number_of_lists; + synctex_node_p node; + while(i--) { + printf("Friend index:%i\n",i); + node = (scanner->lists_of_friends)[i]; + while(node) { + printf("%s:%i,%i\n", + synctex_node_isa(node), + _synctex_data_tag(node), + _synctex_data_line(node) + ); + node = _synctex_tree_friend(node); + } + } + } + } else { + printf("SyncTeX Warning: Too many objects\n"); + } +} +/* Public */ +const char * synctex_scanner_get_name(synctex_scanner_p scanner,int tag) { + synctex_node_p input = NULL; + if (NULL == scanner) { + return NULL; + } + if ((input = scanner->input)) {; + do { + if (tag == _synctex_data_tag(input)) { + return (_synctex_data_name(input)); + } + } while((input = __synctex_tree_sibling(input))); + } + return NULL; +} +const char * synctex_node_get_name(synctex_node_p node) { + if (node) { + return synctex_scanner_get_name(node->class_->scanner,_synctex_data_tag(node)); + } + return NULL; +} + +static int _synctex_scanner_get_tag(synctex_scanner_p scanner,const char * name); +static int _synctex_scanner_get_tag(synctex_scanner_p scanner,const char * name) { + synctex_node_p input = NULL; + if (NULL == scanner) { + return 0; + } + if ((input = scanner->input)) { + do { + if (_synctex_is_equivalent_file_name(name,(_synctex_data_name(input)))) { + return _synctex_data_tag(input); + } + } while((input = __synctex_tree_sibling(input))); + } + // 2011 version + name = _synctex_base_name(name); + if ((input = scanner->input)) { + do { + if (_synctex_is_equivalent_file_name(name,_synctex_base_name(_synctex_data_name(input)))) { + synctex_node_p other_input = input; + while((other_input = __synctex_tree_sibling(other_input))) { + if (_synctex_is_equivalent_file_name(name,_synctex_base_name(_synctex_data_name(other_input))) + && (strlen(_synctex_data_name(input))!=strlen(_synctex_data_name(other_input)) + || strncmp(_synctex_data_name(other_input),_synctex_data_name(input),strlen(_synctex_data_name(input))))) { + // There is a second possible candidate + return 0; + } + } + return _synctex_data_tag(input); + } + } while((input = __synctex_tree_sibling(input))); + } + return 0; +} + +int synctex_scanner_get_tag(synctex_scanner_p scanner,const char * name) { + size_t char_index = strlen(name); + if ((scanner = synctex_scanner_parse(scanner)) && (0 < char_index)) { + /* the name is not void */ + char_index -= 1; + if (!SYNCTEX_IS_PATH_SEPARATOR(name[char_index])) { + /* the last character of name is not a path separator */ + int result = _synctex_scanner_get_tag(scanner,name); + if (result) { + return result; + } else { + /* the given name was not the one known by TeX + * try a name relative to the enclosing directory of the scanner->output file */ + const char * relative = name; + const char * ptr = scanner->reader->output; + while((strlen(relative) > 0) && (strlen(ptr) > 0) && (*relative == *ptr)) + { + relative += 1; + ptr += 1; + } + /* Find the last path separator before relative */ + while(relative > name) { + if (SYNCTEX_IS_PATH_SEPARATOR(*(relative-1))) { + break; + } + relative -= 1; + } + if ((relative > name) && (result = _synctex_scanner_get_tag(scanner,relative))) { + return result; + } + if (SYNCTEX_IS_PATH_SEPARATOR(name[0])) { + /* No tag found for the given absolute name, + * Try each relative path starting from the shortest one */ + while(0<char_index) { + char_index -= 1; + if (SYNCTEX_IS_PATH_SEPARATOR(name[char_index]) + && (result = _synctex_scanner_get_tag(scanner,name+char_index+1))) { + return result; + } + } + } + } + return result; + } + } + return 0; +} +synctex_node_p synctex_scanner_input(synctex_scanner_p scanner) { + return scanner?scanner->input:NULL; +} +synctex_node_p synctex_scanner_input_with_tag(synctex_scanner_p scanner, int tag) { + synctex_node_p input = scanner?scanner->input:NULL; + while (_synctex_data_tag(input)!=tag) { + if ((input = __synctex_tree_sibling(input))) { + continue; + } + break; + } + return input; +} +const char * synctex_scanner_get_output_fmt(synctex_scanner_p scanner) { + return NULL != scanner && scanner->output_fmt?scanner->output_fmt:""; +} +const char * synctex_scanner_get_output(synctex_scanner_p scanner) { + return NULL != scanner && scanner->reader->output?scanner->reader->output:""; +} +const char * synctex_scanner_get_synctex(synctex_scanner_p scanner) { + return NULL != scanner && scanner->reader->synctex?scanner->reader->synctex:""; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Public node attributes +# endif + +# define SYNCTEX_DEFINE_NODE_HVWHD(WHAT) \ +int synctex_node_##WHAT(synctex_node_p node) { \ + return (node && node->class_->inspector->WHAT)? \ + node->class_->inspector->WHAT(node): 0; \ +} +# define SYNCTEX_DEFINE_PROXY_HV(WHAT) \ +static int _synctex_proxy_##WHAT(synctex_proxy_p proxy) { \ + synctex_node_p target = _synctex_tree_target(proxy); \ + if (target) { \ + return _synctex_data_##WHAT(proxy)+synctex_node_##WHAT(target); \ + } else { \ + return proxy? _synctex_data_##WHAT(proxy): 0; \ + } \ +} +#define SYNCTEX_DEFINE_PROXY_TLCWVD(WHAT) \ +static int _synctex_proxy_##WHAT(synctex_proxy_p proxy) { \ + synctex_node_p target = _synctex_tree_target(proxy); \ + return target? synctex_node_##WHAT(target): 0; \ +} + +/** + * The horizontal location of the node. + * Idem for v, width, height and depth. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - requires: every proxy node has a target. + * - note: recursive call if the parameter has a proxy. + * - author: JL + */ +SYNCTEX_DEFINE_NODE_HVWHD(h) +SYNCTEX_DEFINE_NODE_HVWHD(v) +SYNCTEX_DEFINE_NODE_HVWHD(width) +SYNCTEX_DEFINE_NODE_HVWHD(height) +SYNCTEX_DEFINE_NODE_HVWHD(depth) +SYNCTEX_DEFINE_PROXY_TLCWVD(tag) +SYNCTEX_DEFINE_PROXY_TLCWVD(line) +SYNCTEX_DEFINE_PROXY_TLCWVD(column) +SYNCTEX_DEFINE_PROXY_HV(h) +SYNCTEX_DEFINE_PROXY_HV(v) +SYNCTEX_DEFINE_PROXY_TLCWVD(width) +SYNCTEX_DEFINE_PROXY_TLCWVD(height) +SYNCTEX_DEFINE_PROXY_TLCWVD(depth) + +/** + * Whether the argument is a box, + * either vertical or horizontal, + * either void or not, + * or a proxy to such a box. + * - parameter NODE: of type synctex_node_p + * - returns: yorn + */ + +SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_box(synctex_node_p node) { + return node && + (node->class_->type == synctex_node_type_hbox + || node->class_->type == synctex_node_type_void_hbox + || node->class_->type == synctex_node_type_vbox + || node->class_->type == synctex_node_type_void_vbox + || _synctex_node_is_box(_synctex_tree_target(node))); +} + +/** + * Whether the argument is a handle. + * Handles are similar to proxies because they have a target. + * They are used for query results. + * - parameter NODE: of type synctex_node_p + * - returns: yorn + */ + +SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_handle(synctex_node_p node) { + return node && + (node->class_->type == synctex_node_type_handle); +} + +/** + * Resolves handle indirection. + * - parameter node: of type synctex_node_p + * - returns: node if it is not a handle, + * its target otherwise. + */ + +SYNCTEX_INLINE static synctex_node_p _synctex_node_or_handle_target(synctex_node_p node) { + return _synctex_node_is_handle(node)? + _synctex_tree_target(node):node; +} + +/** + * Whether the argument is an hbox. + * - parameter NODE: of type synctex_node_p + * - returns: yorn + */ + +SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_hbox(synctex_node_p node) { + return node && + (node->class_->type == synctex_node_type_hbox + || node->class_->type == synctex_node_type_void_hbox + || _synctex_node_is_hbox(_synctex_tree_target(node))); +} + +/** + * The horizontal location of the first box enclosing node. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - author: JL + */ +int synctex_node_box_h(synctex_node_p node) { + if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { + return synctex_node_h(node); + } + return 0; +} +/** + * The vertical location of the first box enclosing node. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - author: JL + */ +int synctex_node_box_v(synctex_node_p node) { + if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { + return synctex_node_v(node); + } + return 0; +} +/** + * The width of the first box enclosing node. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - author: JL + */ +int synctex_node_box_width(synctex_node_p node) { + if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { + return synctex_node_width(node); + } + return 0; +} +/** + * The height of the first box enclosing node. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - author: JL + */ +int synctex_node_box_height(synctex_node_p node) { + if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { + return synctex_node_height(node); + } + return 0; +} +/** + * The depth of the first box enclosing node. + * - parameter node: a node with geometrical information. + * - returns: an integer. + * - author: JL + */ +int synctex_node_box_depth(synctex_node_p node) { + if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { + return synctex_node_depth(node); + } + return 0; +} +/** + * The horizontal location of an hbox, corrected with contents. + * - parameter node: an hbox node. + * - returns: an integer, 0 if node is not an hbox or an hbox proxy. + * - note: recursive call when node is an hbox proxy. + * - author: JL + */ +int synctex_node_hbox_h(synctex_node_p node) { + switch(synctex_node_type(node)) { + case synctex_node_type_hbox: + return _synctex_data_h_V(node); + case synctex_node_type_proxy_hbox: + return _synctex_data_h(node)+synctex_node_hbox_