pax_global_header00006660000000000000000000000064127007030310014503gustar00rootroot0000000000000052 comment=ee8fd757249a26ace3070e0b0d578b086ad5fc12 dspdfviewer-1.15/000077500000000000000000000000001270070303100137535ustar00rootroot00000000000000dspdfviewer-1.15/CHANGELOG.md000066400000000000000000000117621270070303100155730ustar00rootroot00000000000000dspdfviewer changelog ===================== This file aims to record all user relevant changes to dspdfviewer. If you're intrested in the specific source-level changes between versions, or non user-relevant changes (like build system or packaging related), please inspect the output of commands like `git diff -w v1.11..v1.12` directly. v1.15 - 2016-04-05 ------------------ Changes in behaviour: * When compiling dspdfviewer yourself: * You must now execute the testsuite within a running X Server (Linux) or within a graphical environment (Windows/OSX). It will no longer try to set up one on its own. * The testsuite expects two screens to be connected by default. If your environment only has one screen, you can pass -DRunDualScreenTests=OFF at CMake time to avoid a test failure. * Instead of offering pre-rendered PDFs for download, they are now included in the source tree. The option `DownloadTestPDF` has been replaced with `UsePrerenderedPDF` accordingly. * Qt5 is now the default. If you want to build against Qt4, you will have to pass -DUseQtFive=OFF to CMake. Note that Qt4 support is now considered deprecated and will be removed in one of the next versions. Please file a bug if your system does not work correctly with Qt5. New features: * Predictable memory usage and configurable cache size limit * Previously, dspdfviewer allowed to cache 100 images. It did not matter whether these where thumbnails or full pages, resulting in unpredictable memory usage. * Now you can specify memory limit for the cache in MiB, using the command-line parameter --cache-size. The default value 1024 is normally fine, setting is below 250 will result in severe performance crippling. * This only affects the *cache* memory. The total memory usage of dspdfviewer can be up to 300M higher, mainly depending on whether the shared libraries (poppler/qt) are also used by other programs, or are specifically loaded for dspdfviewer. Other: * A lot of work has gone into the Windows port, and compiling on Windows with MSVC is now part of the automated testing. * The testsuite is currently disabled on big-endian machines. It keeps failing with what appears to be an endian issue. If you want to help out, you can force the testsuite to run by passing -DRunTestsOnBigEndian=ON at cmake time. v1.14 - 2015-12-01 ------------------ New features: * Translation support * For now, a german translation is included, thanks to projekter. * Experimental i3 support * If you specify the --i3-workaround=true option, dspdfviewer will try to use i3-cmd to move the audience window one screen to the right. * This code is not well tested, please leave feedback and suggestions. * Add support for Qt5 * Add support for Windows, thanks to projekter. v1.13.1 - 2015-08-06 -------------------- This is a bugfix and maintenance release for v1.13. Bugfixes: * Regression: Changing slide while blanked crashed the program Other: * A `dspdfviewer.desktop` file has been added, helping desktop enviroments to include the program in their menus * `dspdfviewer --help` informs about the manpage and the F1 help box * This changelog has been started, listing changes since v1.8. v1.13 - 2015-07-30 ------------------ New features: * dspdfviewer will open a file chooser dialog when started without a command-line file parameter (instead of aborting with error message) * Hyperlinks between pages are now clickable * The height of the bottom pane (thumbnails and clocks) on the second screen can now be configured v1.12 - 2015-07-23 ------------------ New features: * Added support for the Logitech R400 presenter remote control. * Secondary screen can be toggled between notes and main presentation. * `F1` or `?` key show a quick help box. * A configuration file at `~/.config/dspdfviewer.ini` can now be used in addition to command-line parameters. v1.11 - 2014-07-12 ------------------ New features: * The window for the presenter now spawns on the primary screen (see `man xrandr` on how to define this) which is assumed to be the notebook you carry to the presenation site. * The windows now have different window titles and window roles. This enables scripting, especially useful on tiling window managers. * dspdfviewer will now watch the file on disk and re-read when it changes. This is useful if you're using dspdfviewer as a previewer while writing a presentation. v1.10.1 - 2013-10-22 -------------------- This is a bugfix and maintenance release for 1.10. Bugfixes: * Regression: Primary and secondary window could not be swapped on Kubuntu 13.10. 1.10 - 2013-03-25 ----------------- Improvement: * Speed up initial rendering 1.9 - 2013-01-23 ---------------- New feature: * The "go to" command (`G`) now displays the range of valid page numbers 1.8 - 2012-12-11 ---------------- Initial *free and open source software* release: * dspdfviewer is now distributed under the GNU GPL, version 2, or (at your option) any later version. dspdfviewer-1.15/CMakeLists.txt000066400000000000000000000075421270070303100165230ustar00rootroot00000000000000# /* # dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer # Copyright (C) 2012 Danny Edel # # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # */ cmake_minimum_required(VERSION 2.8.4 FATAL_ERROR) if( NOT CMAKE_VERSION VERSION_LESS 2.8.11) cmake_policy(SET CMP0020 NEW) # qtmain.lib linkage on windows endif() if( NOT CMAKE_VERSION VERSION_LESS 3.1) cmake_policy(SET CMP0054 NEW) # dont expand quoted strings in if()s endif() project(dspdfviewer) include(FindPkgConfig) set(CMAKE_AUTOMOC ON)# set(CMAKE_AUTORCC OFF) set(CMAKE_INCLUDE_CURRENT_DIR OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) option(UseQtFive "Build with Qt5 and libpoppler-qt5" ON) option(UpdateTranslations "Do you want to update the .ts files (WARNING: running make clean will delete them!)" OFF) option(BuildTests "Build unit tests (this requires pdflatex or internet access and DownloadTestPDFs=ON)" ON) option(RunDualScreenTests "Also run tests that require two screens to be connected" ON) option(RunTestsOnBigEndian "Run tests on a big-endian system" OFF) option(BoostStaticLink "Link statically against the boost libraries" OFF) option(WindowsStaticLink "Windows/MSVC only: Link statically against the dependencies and set /MT instead of /MD" ON) option(UsePrerenderedPDF "Use prerendered PDFs included in the source for testing, instead of building with pdflatex" OFF) option(CodeCoverage "Add coverage analysis code to the program. Will slow it down. A lot. Only supported on GCC right now." OFF) include(cmake/platforms.cmake) include(cmake/filelists.cmake) include(cmake/external_libraries.cmake) include(cmake/qrc_embedding.cmake) include(cmake/compilers.cmake) include(cmake/version_number.cmake) if( I3WORKAROUND_SHELLCODE ) add_definitions(-DI3WORKAROUND_SHELLCODE="${I3WORKAROUND_SHELLCODE}") endif() include_directories(SYSTEM ${LIST_INCLUDE_DIRS}) ### Compile all the source code into one .a file add_library(libdspdfviewer ${libdspdfviewer_SRCS} ${dspdfviewer_UIS_H} ${TRANSLATIONS}) # Set target name to "libdspdfviewer" # without a prefix (normally unix would auto-add lib...) set_target_properties(libdspdfviewer PROPERTIES PREFIX "" ) target_link_libraries(libdspdfviewer ${LIST_LIBRARIES}) ### And link it together with main.cpp to produce an executable add_executable(dspdfviewer ${dspdfviewer_SRCS} ${EMBEDDED_QRC}) target_link_libraries(dspdfviewer libdspdfviewer) ### ... and link it with the tests to produce a testing executable. if(BuildTests) # Run unit tests regardless of debug/release # Set a default timeout to 60 seconds set(DART_TESTING_TIMEOUT 60) set(CTEST_TEST_TIMEOUT 60) # Check for big endian include(TestBigEndian) TEST_BIG_ENDIAN(BigEndian) if( NOT BigEndian OR RunTestsOnBigEndian) include(CTest) add_subdirectory(testing) else() message(WARNING "The unit tests have been temporarily disabled on big-endian " "systems. If you want to help in debugging this, please pass " "-DRunTestsOnBigEndian=ON to cmake to force their execution.") endif() endif() #### Installation install(TARGETS dspdfviewer RUNTIME DESTINATION bin) install(FILES docs/dspdfviewer.1 DESTINATION share/man/man1) install(FILES dspdfviewer.desktop DESTINATION share/applications) dspdfviewer-1.15/COPYING000066400000000000000000000432541270070303100150160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. dspdfviewer-1.15/INSTALL000066400000000000000000000023171270070303100150070ustar00rootroot00000000000000How to install the dspdfviewer ============================== System-specific installation instructions ----------------------------------------- Please refer to the project's website for installation instructions, broken down by operating system. At least for Debian, Ubuntu, Gentoo, Arch, MacOS and Windows the website contains specific installation instructions. For some systems, ready-made binaries are provided. http://dspdfviewer.danny-edel.de Installation from source (generic cmake) ---------------------------------------- dspdfviewer requires the following build-time dependencies: * cmake * boost * qt4 * poppler-qt4 From inside the source directory, execute the following steps: mkdir build cd build cmake .. make (as root) make install Note that there is no make uninstall, so please redirect the make install step through your system's package management utility. The "cmake" step will respect --prefix= parameters, and "make install" will respect DESTDIR. Compiling against qt5 and poppler-qt5 ------------------------------------- There is now experimental support for compiling against qt5/libpoppler-qt5. To try it out, substitute the "cmake" step above against cmake -DUseQtFive=ON .. dspdfviewer-1.15/README.md000066400000000000000000000023651270070303100152400ustar00rootroot00000000000000# Dual-Screen PDF Viewer for latex-beamer [![Build Status](https://travis-ci.org/dannyedel/dspdfviewer.svg?branch=master)](https://travis-ci.org/dannyedel/dspdfviewer) This is a simple viewer for latex-beamer presentations that are built with the **show notes on second screen** option of latex-beamer. It will take your PDF file, split it in a left and right half and render the two halves individually to the screens. ## Usage There is a manpage included that explains all the options and keybindings. Most important ones are probably: Command line: dspdfviewer [options] *pdf-file* where the most useful option is probably -f, because it enables you to use the software with a standard pdf (i.e. not specifically built for latex-beamer). In-Program-Controls: Left/Right, Mouse Buttons or Mouse Wheel: Back/Forward S or F12: Swap screens (if you see the wall clock on the projector) B or . (period): blank/unblank audience screen Q/Esc: Quit ## Installation Please read the [installation section] on the project's website for detailed instructions, broken down by operating system. For generic from-source installation instructions, you can also consult the INSTALL file. [installation section]: http://dspdfviewer.danny-edel.de/#how-do-i-install-it dspdfviewer-1.15/adjustedlink.cpp000066400000000000000000000051471270070303100171470ustar00rootroot00000000000000/* Copyright (C) 2012 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "adjustedlink.h" #include #include using boost::numeric_cast; QSharedPointer< Poppler::Link > AdjustedLink::link() const { return m_link; } QRectF AdjustedLink::linkArea() const { QRectF const& orig( link()->linkArea() ); QRectF retval( link()->linkArea() ); switch(ri.pagePart()) { case PagePart::FullPage: break; case PagePart::LeftHalf: if ( retval.left() > 0.5 ) { return QRectF(); } retval.setLeft( orig.left() * 2.0 ); retval.setWidth( orig.width() * 2.0 ); break; case PagePart::RightHalf: if ( retval.right() < 0.5 ) { // no part of the rectangle is in our page return QRectF(); } retval.setLeft( (orig.left() - 0.5) * 2.0 ); retval.setWidth( orig.width() * 2.0 ); break; } if ( retval.height() < 0 ) { retval.setHeight( -retval.height() ); retval.moveTop( retval.top() - retval.height()); } return retval; } Poppler::Link::LinkType AdjustedLink::linkType() const { return link()->linkType(); } AdjustedLink::AdjustedLink(const RenderingIdentifier& renderIdent, QSharedPointer< Poppler::Link > link) : m_link(link), ri(renderIdent) { if ( linkArea().isNull() ) throw OutsidePage(); } AdjustedLink::OutsidePage::OutsidePage(): runtime_error("This link is not inside the current page part") { } uint AdjustedLink::targetPageNumber() const { /* Although page numbers in a PDF are non-negative, poppler's pageNumber * is a signed integer. * * numeric_cast checks that the number actually *is* non-negative. */ return numeric_cast(lgt().destination().pageNumber()) -1u; } Poppler::LinkGoto const& AdjustedLink::lgt() const { return dynamic_cast( *m_link ); } dspdfviewer-1.15/adjustedlink.h000066400000000000000000000040141270070303100166040ustar00rootroot00000000000000/* Copyright (C) 2012 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ADJUSTEDLINK_H #define ADJUSTEDLINK_H #include "renderingidentifier.h" #include #include "poppler-qt.h" #include /** Link that is adjusted to a rendered page. * This includes failing to construct if it is completely outside the scope. */ class AdjustedLink { public: /** Exception class */ struct OutsidePage: public std::runtime_error { OutsidePage(); }; /** Exception class */ struct UnsupportedLinkType{}; AdjustedLink(const RenderingIdentifier& renderIdent, QSharedPointer link); /** Returns the link area as floating point in the range 0..1 **/ QRectF linkArea() const; /** Returns the link area, but scaled to the given QRectangle **/ QRect linkArea(const QRect& rect) const; /** Target page number */ uint targetPageNumber() const; /** Returns a shared pointer to the Poppler link */ QSharedPointer link() const; /** Forwarding function for Poppler::Link::LinkType **/ Poppler::Link::LinkType linkType() const; private: QSharedPointer m_link; RenderingIdentifier ri; Poppler::LinkGoto const& lgt() const; }; #endif // ADJUSTEDLINK_H dspdfviewer-1.15/cmake/000077500000000000000000000000001270070303100150335ustar00rootroot00000000000000dspdfviewer-1.15/cmake/compiler_clang.cmake000066400000000000000000000030521270070303100210130ustar00rootroot00000000000000# CMake fragment # Clang add_definitions("-std=c++11") # Turn on a lot of warnings, hopefully helping with code quality. add_definitions(-Weverything) # And turn them into errors. add_definitions(-Werror) # Disable warnings irrelevant to the project: # c++98 compatibility warnings add_definitions(-Wno-c++98-compat -Wno-c++98-compat-pedantic) # Currently this warning triggers in exception classes add_definitions(-Wno-error=weak-vtables) # We don't care about padding right now. add_definitions(-Wno-padded) # in release mode: Unrechable code / Macro expansion (these stem from QDEBUG) # trigger a lot (QDEBUG compiles into the equivalent of while(false) { ... } ) # So these are safe to ignore IFF we're not in Debug mode. if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" ) add_definitions(-Wno-unreachable-code -Wno-disabled-macro-expansion) endif() # Qt's moc (Meta Object Compiler) generates code that triggers warnings # about undefined behaviours. # So don't set Werror for it. # TODO: Set this only for the automoc files add_definitions(-Wno-error=undefined-reinterpret-cast) #message(FATAL_ERROR "Version: ${CMAKE_CXX_COMPILER_VERSION}") # clang will complain about -isystem passed but not used. # This adds unnecessary noise add_definitions(-Wno-unused-command-line-argument) # qrc system generates code that triggers a lot of the # clang warnings. set_source_files_properties( ${EMBEDDED_QRC} PROPERTIES COMPILE_FLAGS "-Wno-error") if(CodeCoverage) add_definitions(--coverage -O0) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") endif() dspdfviewer-1.15/cmake/compiler_gnu_gcc.cmake000066400000000000000000000022521270070303100213350ustar00rootroot00000000000000# GNU GCC include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" C11FLAG) if(C11FLAG) message(STATUS "Compiler supports -std=c++11, using it.") add_definitions(-std=c++11) else() message(WARNING "Compiler does not support -std=c++11, trying c++0x") CHECK_CXX_COMPILER_FLAG("-std=c++0x" C0XFLAG) if(C0XFLAG) message(STATUS "Using -std=c++0x") add_definitions(-std=c++0x) else() message(FATAL_ERROR "Compiler does not support -std=c++0x either. " "Please upgrade your compiler." ) endif() endif() # Set as much warnings as possible. add_definitions( -Wall -Wextra -pedantic -Wold-style-cast -Woverloaded-virtual -Weffc++ ) # Turn all warnings into errors add_definitions(-Werror -pedantic-errors) # These warnings produce false positives on some older qt4 libraries # (this failed debian s390x compilation), therefore tune them back # to warning when building against qt4. if( NOT UseQtFive ) add_definitions(-Wno-error=old-style-cast) add_definitions(-Wno-error=effc++) endif() if(CodeCoverage) message(STATUS "Adding gcov as test coverage helper") add_definitions(-fprofile-arcs -ftest-coverage -O0) LIST(APPEND LIST_LIBRARIES gcov ) endif() dspdfviewer-1.15/cmake/compiler_msvc.cmake000066400000000000000000000006711270070303100207030ustar00rootroot00000000000000# MSVC foreach(var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO ) if(WindowsStaticLink) if(${var} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}") endif() add_definitions(-DWINDOWS_STATIC_LINK) endif() endforeach() dspdfviewer-1.15/cmake/compiler_unknown.cmake000066400000000000000000000002041270070303100214220ustar00rootroot00000000000000# CMake framgent: unknown compiler # Let's hops this works. add_definitions(-std=c++11) include_directories(${LIST_INCLUDE_DIRS}) dspdfviewer-1.15/cmake/compilers.cmake000066400000000000000000000020331270070303100200300ustar00rootroot00000000000000# cmake fragment # Compiler support if(CMAKE_COMPILER_IS_GNUCXX) message(STATUS "GNU G++ detected.") include(cmake/compiler_gnu_gcc.cmake) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(STATUS "Clang++ detected.") include(cmake/compiler_clang.cmake) elseif(MSVC) message(STATUS "MSVC detected.") include(cmake/compiler_msvc.cmake) else() # Unknown Compiler message(WARNING "You are using an unidentified and therefore unsupported compiler. " " If this works for you, please report that to upstream and - if possible -" " please send patches activating sensible compile flags on it.") include(cmake/compiler_unknown.cmake) endif() # ### Common for all compilers # # Add -DQT_NO_DEBUG_OUTPUT to Release # and Release with Debug Info builds set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DQT_NO_DEBUG_OUTPUT") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_NO_DEBUG_OUTPUT") # This helps with translation, since it disables QString(const char*) add_definitions(-DQT_NO_CAST_FROM_ASCII) dspdfviewer-1.15/cmake/external_libraries.cmake000066400000000000000000000071241270070303100217170ustar00rootroot00000000000000# cmake frament # External libraries ### Boost Libraries ### if( BoostStaticLink ) set(Boost_USE_STATIC_LIBS ON) if(WindowsStaticLink) set(Boost_USE_STATIC_RUNTIME ON) endif() elseif(BuildTests) add_definitions(-DBOOST_TEST_DYN_LINK) endif() if(BuildTests) # If we're building tests, we need program_options # AND unit_test_framework find_package(Boost COMPONENTS program_options unit_test_framework REQUIRED) else() # No unit tests: Only program_options needed find_package(Boost COMPONENTS program_options REQUIRED) endif() ### Qt if(UseQtFive) # Qt5 message(STATUS "Using Qt5 and libpoppler-qt5") if(MSVC AND WindowsStaticLink) # If linking statically, # Try to find Qt in /qt/static/qtbase # or plain /qt/static set(CMAKE_PREFIX_PATH "/Qt/static/qtbase;/Qt/static;/Qt/static/qttools;${CMAKE_PREFIX_PATH}") endif() find_package(Qt5Core 5.1.1 REQUIRED) # On lower versions, QTBUG-32100 prevents compilation at least on clang # https://bugreports.qt.io/browse/QTBUG-32100 find_package(Qt5Gui REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5LinguistTools REQUIRED) find_package(Qt5Xml REQUIRED) message(STATUS "Found Qt5 at ${Qt5Core_DIR}") if(MSVC) if (WindowsStaticLink) set(POPPLER_LIBRARIES ${WINDOWS_PRECOMPILED_STATIC_LIBRARIES} ) set(POPPLER_INCLUDE_DIRS "C:/dspdf/poppler/poppler/include/poppler/qt5" ) else() # MSVC, but not statically linking set(POPPLER_LIBRARIES ${WINDOWS_PRECOMPILED_DYNAMIC_LIBRARIES} ) set(POPPLER_INCLUDE_DIRS "C:/dspdf/popplerDyn/poppler/include/poppler/qt5" ) endif() else() # Non-MSVC Platforms: # Find poppler using pkg-config pkg_search_module(POPPLER REQUIRED poppler-qt5) endif() # add their link flags list(APPEND LIST_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5LinguistTools_LIBRARIES} ${Qt5Xml_LIBRARIES} ) # add their include directories list(APPEND LIST_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5LinguistTools_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ) # Set conditional compilation to Qt5 mode add_definitions(-DPOPPLER_QT5) qt5_wrap_ui(dspdfviewer_UIS_H ${UIFILES}) if( UpdateTranslations ) qt5_create_translation(TRANSLATIONS ${libdspdfviewer_SRCS} ${dspdfviewer_SRCS} ${UIFILES} ${TRANSLATIONFILES} OPTIONS -no-obsolete ) else() qt5_add_translation(TRANSLATIONS ${TRANSLATIONFILES}) endif() else() #qt4 message(STATUS "Using Qt4 and libpoppler-qt4") message(WARNING "Qt4 support is deprecated and will be removed in the next version. " "Please build dspdfviewer using Qt5. " "If that does not work correctly on your platform, please file a bug!") find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui) pkg_search_module(POPPLER REQUIRED poppler-qt4) list(APPEND LIST_INCLUDE_DIRS ${QT_INCLUDES}) list(APPEND LIST_LIBRARIES Qt4::QtGui) qt4_wrap_ui(dspdfviewer_UIS_H ${UIFILES}) if( UpdateTranslations ) qt4_create_translation(TRANSLATIONS ${libdspdfviewer_SRCS} ${dspdfviewer_SRCS} ${UIFILES} ${TRANSLATIONFILES} OPTIONS -no-obsolete ) else() qt4_add_translation(TRANSLATIONS ${TRANSLATIONFILES}) endif() endif() # include/link poppler list(APPEND LIST_LIBRARIES ${POPPLER_LIBRARIES}) list(APPEND LIST_INCLUDE_DIRS ${POPPLER_INCLUDE_DIRS}) list(APPEND LIST_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) list(APPEND LIST_LIBRARIES ${Boost_PROGRAM_OPTIONS_LIBRARY}) if(BuildTests) list(APPEND LIST_TEST_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) endif() # This is needed for moc/uic results list(APPEND LIST_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) dspdfviewer-1.15/cmake/filelists.cmake000066400000000000000000000112701270070303100200340ustar00rootroot00000000000000# CMake file fragment # File lists list(APPEND TRANSLATIONFILES translations/dspdfviewer_de.ts ) list(APPEND UIFILES pdfviewerwindow.ui keybindings.ui ) list(APPEND libdspdfviewer_SRCS adjustedlink.cpp hyperlinkarea.cpp pdfpagereference.cpp pdfdocumentreference.cpp runtimeconfiguration.cpp renderutils.cpp renderthread.cpp renderingidentifier.cpp pagepart.cpp renderedpage.cpp pdfrenderfactory.cpp pdfviewerwindow.cpp dspdfviewer.cpp windowrole.cpp ) list(APPEND dspdfviewer_SRCS main.cpp ) list(APPEND QRCFILES dspdfviewer.qrc ) list(APPEND UIFILES pdfviewerwindow.ui keybindings.ui ) # Windows file lists: # Dynamic and static precompiled dependencies list(APPEND WINDOWS_PRECOMPILED_STATIC_LIBRARIES optimized "C:/dspdf/poppler/poppler/lib/poppler.lib" debug "C:/dspdf/poppler/poppler/lib/popplerd.lib" optimized "C:/dspdf/poppler/poppler/lib/poppler-qt5.lib" debug "C:/dspdf/poppler/poppler/lib/poppler-qt5d.lib" optimized "C:/dspdf/poppler/deps/cairo/lib/cairo-static.lib" debug "C:/dspdf/poppler/deps/cairo/lib/cairo-staticd.lib" optimized "C:/dspdf/poppler/deps/freetype/lib/freetype.lib" debug "C:/dspdf/poppler/deps/freetype/lib/freetyped.lib" optimized "C:/dspdf/poppler/deps/lcms/Lib/MS/lcms2_static.lib" debug "C:/dspdf/poppler/deps/lcms/Lib/MS/lcms2_staticd.lib" optimized "C:/dspdf/poppler/deps/fontconfig/lib/libfontconfig.lib" debug "C:/dspdf/poppler/deps/fontconfig/lib/libfontconfigd.lib" optimized "C:/dspdf/poppler/deps/libpng/lib/libpng16_static.lib" debug "C:/dspdf/poppler/deps/libpng/lib/libpng16_staticd.lib" optimized "C:/dspdf/poppler/deps/libtiff/lib/tiff_static.lib" debug "C:/dspdf/poppler/deps/libtiff/lib/tiff_staticd.lib" optimized "C:/dspdf/poppler/deps/zlib/lib/zlibstatic.lib" debug "C:/dspdf/poppler/deps/zlib/lib/zlibstaticd.lib" optimized "C:/dspdf/poppler/deps/expat/lib/expat.lib" debug "C:/dspdf/poppler/deps/expat/lib/expatd.lib" optimized "C:/dspdf/poppler/deps/openjpeg/lib/openjp2.lib" debug "C:/dspdf/poppler/deps/openjpeg/lib/openjp2d.lib" optimized "C:/dspdf/poppler/deps/libtiff/lib/port.lib" debug "C:/dspdf/poppler/deps/libtiff/lib/portd.lib" optimized "C:/dspdf/poppler/deps/libiconv/lib/libiconvStatic.lib" debug "C:/dspdf/poppler/deps/libiconv/lib/libiconvStaticD.lib" optimized "C:/dspdf/poppler/deps/pixman/lib/pixman-1_static.lib" debug "C:/dspdf/poppler/deps/pixman/lib/pixman-1_staticd.lib" # Microsoft SDK "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/WS2_32.Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/OpenGL32.Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/MSImg32.Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/Imm32.Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/Winmm.Lib" # Qt Platform Support optimized "C:/Qt/Static/qtbase/lib/Qt5PlatformSupport.lib" debug "C:/Qt/Static/qtbase/lib/Qt5PlatformSupportd.lib" optimized "C:/Qt/Static/qtbase/lib/qtharfbuzzng.lib" debug "C:/Qt/Static/qtbase/lib/qtharfbuzzngd.lib" optimized "C:/Qt/Static/qtbase/lib/qtpcre.lib" debug "C:/Qt/Static/qtbase/lib/qtpcred.lib" optimized "C:/Qt/Static/qtbase/plugins/platforms/qwindows.lib" debug "C:/Qt/Static/qtbase/plugins/platforms/qwindowsd.lib" ) list(APPEND WINDOWS_PRECOMPILED_DYNAMIC_LIBRARIES optimized "C:/dspdf/popplerDyn/poppler/lib/poppler.lib" debug "C:/dspdf/popplerDyn/poppler/lib/popplerd.lib" optimized "C:/dspdf/popplerDyn/poppler/lib/poppler-qt5.lib" debug "C:/dspdf/popplerDyn/poppler/lib/poppler-qt5d.lib" optimized "C:/dspdf/popplerDyn/deps/cairo/lib/cairo.lib" debug "C:/dspdf/popplerDyn/deps/cairo/lib/cairod.lib" optimized "C:/dspdf/popplerDyn/deps/freetype/lib/freetype.lib" debug "C:/dspdf/popplerDyn/deps/freetype/lib/freetyped.lib" optimized "C:/dspdf/popplerDyn/deps/lcms/Lib/MS/lcms2.lib" debug "C:/dspdf/popplerDyn/deps/lcms/Lib/MS/lcms2d.lib" optimized "C:/dspdf/popplerDyn/deps/fontconfig/lib/fontconfig.lib" debug "C:/dspdf/popplerDyn/deps/fontconfig/lib/fontconfigd.lib" optimized "C:/dspdf/popplerDyn/deps/libpng/lib/libpng16.lib" debug "C:/dspdf/popplerDyn/deps/libpng/lib/libpng16d.lib" optimized "C:/dspdf/popplerDyn/deps/libtiff/lib/tiff.lib" debug "C:/dspdf/popplerDyn/deps/libtiff/lib/tiffd.lib" optimized "C:/dspdf/popplerDyn/deps/zlib/lib/zlib.lib" debug "C:/dspdf/popplerDyn/deps/zlib/lib/zlibd.lib" "C:/dspdf/popplerDyn/deps/expat/lib/expat.lib" optimized "C:/dspdf/popplerDyn/deps/openjpeg/lib/openjpeg.lib" debug "C:/dspdf/popplerDyn/deps/openjpeg/lib/openjpegd.lib" optimized "C:/dspdf/popplerDyn/deps/libiconv/lib/libiconv.lib" debug "C:/dspdf/popplerDyn/deps/libiconv/lib/libiconvD.lib" optimized "C:/dspdf/popplerDyn/deps/pixman/lib/pixman-1_static.lib" debug "C:/dspdf/popplerDyn/deps/pixman/lib/pixman-1_staticd.lib" ) dspdfviewer-1.15/cmake/platforms.cmake000066400000000000000000000006551270070303100200520ustar00rootroot00000000000000# If building on MSVC, WIN32 is defined, but on Cygwin/MSYS/MinGW it is not. # use the WINDOWS variable for everything on windows (regardless of which # compiler type), MSVC or MINGW for compiler-specific things. message(STATUS "Compiling for system ${CMAKE_SYSTEM_NAME}") if( CMAKE_SYSTEM_NAME STREQUAL "Windows" ) set(WINDOWS ON) elseif( CMAKE_SYSTEM_NAME STREQUAL "MSYS" ) set(WINDOWS ON) else() set(WINDOWS OFF) endif() dspdfviewer-1.15/cmake/qrc_embedding.cmake000066400000000000000000000015131270070303100206200ustar00rootroot00000000000000# Copy all the filex in QRCFILES into the binary dir, # and execute qrc (Qt Resource Compiler) on them. foreach(qrc IN LISTS QRCFILES) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${qrc} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${qrc} ${TRANSLATIONFILES} COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${qrc} ${CMAKE_CURRENT_BINARY_DIR}/${qrc} ) set(TMP_OUTFILENAME "${CMAKE_CURRENT_BINARY_DIR}/${qrc}.cxx") if(UseQtFive) set(RCCCOMPILER ${Qt5Core_RCC_EXECUTABLE}) else() set(RCCCOMPILER ${QT_RCC_EXECUTABLE}) endif() add_custom_command( OUTPUT ${TMP_OUTFILENAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${qrc} COMMAND ${RCCCOMPILER} -o ${TMP_OUTFILENAME} ${CMAKE_CURRENT_BINARY_DIR}/${qrc} ) unset(RCCCOMPILER) list(APPEND EMBEDDED_QRC ${TMP_OUTFILENAME} ) unset(TMP_OUTFILENAME) endforeach() dspdfviewer-1.15/cmake/version_number.cmake000066400000000000000000000031571270070303100211000ustar00rootroot00000000000000### CMake Fragment: Include version number ### Version-number inclusion logic. # Rationale: dspdfviewer --version should print something meaningful # Especially on builds from git, it should include the git revision. if( DSPDFVIEWER_VERSION ) # Not-Empty version given on the command line. This has absolute priority. message(STATUS "Embedding the version number ${DSPDFVIEWER_VERSION} specified on the command line.") endif() if( NOT DSPDFVIEWER_VERSION AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" ) # We don't have a version number yet, but # this looks like a checkout from git. # Ask "git describe" for a version number. message(STATUS "Building from a git clone, using git describe for a version number.") execute_process(COMMAND git describe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GIT_DESCRIBE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) # Check if it gave something back if( NOT "${GIT_DESCRIBE_VERSION}" MATCHES "^$" ) message(STATUS "Embedding version number ${GIT_DESCRIBE_VERSION} as defined by git-describe.") set(DSPDFVIEWER_VERSION ${GIT_DESCRIBE_VERSION}) endif() endif() if( NOT DSPDFVIEWER_VERSION ) # We still don't know version number to embed. # Use default # TODO: Keep me updated! set(DSPDFVIEWER_VERSION "1.15") message(STATUS "Embedding version number ${DSPDFVIEWER_VERSION}. If you want to override this, " "for example to embed the git revision you built from, please pass " "-DDSPDFVIEWER_VERSION=1.2.3.4.5 to the cmake command.") endif() if( NOT "${DSPDFVIEWER_VERSION}" MATCHES "^$" ) add_definitions(-DDSPDFVIEWER_VERSION="${DSPDFVIEWER_VERSION}") endif() dspdfviewer-1.15/debug.h000066400000000000000000000015371270070303100152200ustar00rootroot00000000000000#pragma once #include #ifdef QT_NO_DEBUG_OUTPUT /* Qt is defined to squelch debug output. Simply forward to * qDebug()'s definition. */ #define DEBUGOUT qDebug() #define WARNINGOUT qWarning() << "WARNING" #else #include #include /* We're debugging. * Add crazy mumbo jumbo here. */ /** Print filename / line / function info */ #if defined(_MSC_VER) #define __func__ __FUNCTION__ #endif #define DEBUGOUT qDebug() << \ QString::fromUtf8("%1:%2 [%3()]"). \ arg(QFileInfo( QString::fromUtf8(__FILE__) ).fileName()). \ arg(QString::number( __LINE__ )). \ arg(QString::fromUtf8(__func__) ) #define WARNINGOUT qWarning() << "WARNING" << \ QString::fromUtf8("%1:%2 [%3()]"). \ arg(QFileInfo( QString::fromUtf8(__FILE__) ).fileName()). \ arg(QString::number( __LINE__ )). \ arg(QString::fromUtf8(__func__) ) #endif dspdfviewer-1.15/docs/000077500000000000000000000000001270070303100147035ustar00rootroot00000000000000dspdfviewer-1.15/docs/Windows/000077500000000000000000000000001270070303100163355ustar00rootroot00000000000000dspdfviewer-1.15/docs/Windows/Help.hhp000066400000000000000000000005711270070303100177310ustar00rootroot00000000000000[OPTIONS] Compatibility=1.1 or later Compiled file=Help.chm Contents file=TOC.hhc Default Font=Segoe UI,10,0 Default topic=name.html Display compile progress=No Language=0x409 English (United States) Title=DSPDFVIEWER help [FILES] bugs.html controls.html description.html files.html name.html options.html presenter.html seealso.html synopsis.html license.html [INFOTYPES] dspdfviewer-1.15/docs/Windows/TOC.hhc000066400000000000000000000030661270070303100174530ustar00rootroot00000000000000
dspdfviewer-1.15/docs/Windows/bugs.html000066400000000000000000000011521270070303100201620ustar00rootroot00000000000000 Bugs

Bugs

None currently known.

If you find any bugs, the preferred way to report them is at the github issue tracker at GitHub.

As a fallback, you can send e-mail to mail@danny-edel.de with the word "dspdfviewer" in the subject line.

dspdfviewer-1.15/docs/Windows/controls.html000066400000000000000000000060071270070303100210710ustar00rootroot00000000000000 Controls

Controls

You can use the following controls while dspdfviewer is running.

Note that mouse-clicks are subject to pointer position if the hyperlink support is enabled. If your mouse cursor changes to a hand, it will follow the link you are hovering on a click.

Use this if you want the clocks to stop at zero, use the G (goto) function to page 1 if you want to them to keep running.

Note that the Home key may be called "Pos 1" on some keyboards.

Keyboard: ?, F1

Display quick-help (most important key bindings)

Keyboard: Spacebar, Page Down /
Mouse: Left-Click, Wheel Down

Go one page forward

Additional keyboard aliases: Down, Right, Return, Enter, N, F

Keyboard: Backspace, Page Up /
Mouse: Right-Click, Wheel Up

Go one page backward

Additional keyboard aliases: Up, Left, P

Keyboard: B, . (period)

Toggle blanking of the audience screen

Keyboard: G

Go to specific page (a number entry window will pop up)

Note that the this counts PDF pages, so a single slide having 6 unmasking steps will be 7 PDF pages long, including the initial near-blank page.

Keyboard: Home, H

Go to the first page and reset the clocks to zero.

Keyboard: Escape, Q

Quit dspdfviewer.

Keyboard: S, F12

Switch primary and secondary screens

Use this if the audience sees your ’note’ side with the clocks and you see the actual presentation on your screen.

If you just want to see the main presentation (for example, because the projector is behind you), use T.

Keyboard: T

Switch secondary screen’s function

Use this if you want to see the audience side on your screen, without showing the notes to the audience. Pressing the button again will switch back to normal operation.

Keyboard: D

Switch duplication of the audience’s screen

Use this if you want to see the audience side and the notes on your screen (so the full presentation), leaving the audience’s screen untouched. Pressing the button again will switch back to split mode.

dspdfviewer-1.15/docs/Windows/description.html000066400000000000000000000035111270070303100215460ustar00rootroot00000000000000 Description

Description

dspdfviewer is intended for presentations created with the latex-beamer class, specifically documents created with the show notes on second screen=right option.

Its purpose is to display the left half of a page on the audience’s screen (which will most commonly be a projector) and the right half on your "private" screen (think: the notebook you carried to the presentation site). The basic idea is that you will display personal notes and information to help you with your presentation on your personal screen, while the audience sees only the PDF they are supposed to see.

On the secondary screen you will see three clocks: A "wall clock", a "presentation" clock and a "slide" clock.

The wall clock in the bottom left corner that display current time of day, the presentation clock (labeled "Total") counts the time you spent since you started the presentation, and the "slide" counter (bottom right) displays the time you spent on the current slide.

The clocks don’t start the second you launch the program, the idea behind that behaviour is that many people have a "title" slide active long before the audience even arrives.

Once you use the next/previous slide commands, the clock starts. If you want to start the clock, but still display the title slide, just press "previous" (for example, Up Arrow or Right Mouse Button) on the title slide.

If you want to reset the clocks to zero, press the "Home" button on the keybord.

dspdfviewer-1.15/docs/Windows/files.html000066400000000000000000000006551270070303100203330ustar00rootroot00000000000000 Files

Files

%userprofile%\.config\dspdfviewer.ini

You can specify all long command-line options (without leading --) here, in a "option=value" format, one per line.

dspdfviewer-1.15/docs/Windows/license.html000066400000000000000000000050421270070303100206460ustar00rootroot00000000000000 Licenses

Licenses

dspdfviewer contains several third party components with different licenses. All licenses are provided in the License subfolder in the application's location.

  • boost 1.59.0 — Homepage • License: boost.txt
  • cairo 1.14.2 — Homepage • License: cairo-LGPL.txt and cairo-MPL.txt
  • dspdfviewer — Homepage • License: GPLv2.txt
  • Expat 2.1.0 — Homepage • License: expat.txt
  • Fontconfig 2.11.94 — Homepage • License: fontconfig.txt
  • FreeType 2.6 — Homepage • License: GPLv2.txt
  • lcms 2.7 — Homepage • License: lcms.txt
  • libiconv 1.14 — Homepage • License: libiconv.txt
  • libpng 1.6.18 — Homepage • License: libpng.txt
  • LibTIFF 4.0.5 — Homepage • License: libtiff.txt
  • OpenJPEG 2.1.0 — Homepage • License: openjpeg.txt
  • pixman 0.32.6 — Homepage • License: pixman.txt
  • poppler 0.36.0 — Homepage • License: GPLv2.txt
  • Qt 5.5.0 — Homepage • License: GPLv2.txt
  • zlib 1.2.8 — Homepage • License: zlib.txt
dspdfviewer-1.15/docs/Windows/name.html000066400000000000000000000004411270070303100201420ustar00rootroot00000000000000 Name

Name

dspdfviewer − Dual-Screen PDF Viewer

dspdfviewer-1.15/docs/Windows/options.html000066400000000000000000000117031270070303100207200ustar00rootroot00000000000000 Options

Options

The exact behaviour of dspdfviewer is configurable using command-line parameters.

Unless otherwise noted, all command-line options can be written in a configuration file. See the Files section for the location.

For the command-line parameters, this program follows the usual GNU command line syntax, with long options starting with two dashes (’-’). A summary of options is included below.

If you specify those in a configuration file, write one option per line, use the long version without the two leading dashes, followed by an equals sign and the desired value.

For example, bottom-pane-height=20 is a valid configuration line.

If an option is specified in both the configuration file and the command-line, the command-line value takes precedence.

Note for options of type <bool>: They take true, false, 0 or 1 as arguments. For example, --use-second-screen false can be expressed as --use-second-screen 0 or shortened to -u0.
-h, --help Show summary of options.
-f, --full-page

Use the presentation program with a normal single-screen PDF, that is display the full page on both the primary and secondary monitor.

The secondary screen still contains timers and thumbnails to help you with your presentation, unless you disable them using the options below.

-u, --use-second-screen <bool>

Controls whether the program actually works as a dual-screen viewer.

The default is true.

If you only have one screen, but still want to use the cache features, you can set this option to false. If the window ends up on the wrong display, you can still use the switch command (see below).

What comes out on the primary screen depends on whether you use the -f Option: -u0 -f results in a simple full-screen PDF viewer, while -u0 without -f still splits the page in half and outputs only the left frame.

-l, --hyperlink-support <bool>

Controls whether the program makes PDF Hyperlinks clickable.

The default is true.

While it is normally desired to have clickable hyperlinks, if you cannot control the position of your pointer (if your remote control has only left-click and right-click) it might be safer to disable hyperlinks.

--prerender-previous-pages n

When jumping to slide m, also pre-render the slides (m-n)..(m-1) (default 3)

Note: If you set this to zero, you will not get a thumbnail for the previous page rendered before you have visited it.

--prerender-next-pages n

When jumping to slide m, also pre-render the slides (m+1)..(m+n) (default 10)

Note: If you set this to zero, you will not get a thumbnail for the next page rendered before you have visited it.

-a, --presenter-area <bool>

Show (true, 1) or hide (false, 0) the presenter’s area on the second screen. If this is set to zero, the following options will have no effect.

-d, --duplicate <bool>

Duplicate (true, 1) or hide (false, 0) the audience’s screen next to the notes on the second screen.

The default is false.

-t, --thumbnails <bool>

Show (true, 1) or hide (false, 0) the thumbnails on the second screen.

-w, --wall-clock <bool>

Show (true, 1) or hide (false, 0) the wall clock on the second screen.

-p, --presentation-clock <bool>

Show (true, 1) or hide (false, 0) the presentation clock on the second screen.

-s, --slide-clock <bool>

Show (true, 1) or hide (false, 0) the slide clock on the second screen.

-b, --bottom-pane-height n

Configure how large the presenter area on the second screen will be, unit is percent of second screen’s total height.

Default value 20.

dspdfviewer-1.15/docs/Windows/presenter.html000066400000000000000000000012361270070303100212340ustar00rootroot00000000000000 Presenter remote controls

Presenter remote controls

Most (maybe not all) presenter remote controls technically act as a keyboard sending one of the keystrokes listed either in the main or additional keybindings. If your remote control does not work as intended, please file a bug report describing what keystrokes it generates when you press its buttons. If there is no conflict, a keybinding will be included in the next release.

dspdfviewer-1.15/docs/Windows/seealso.html000066400000000000000000000013171270070303100206600ustar00rootroot00000000000000 See also

See also

<path to LaTeX>\doc\latex\beamer\beameruserguide.pdf from the latex-beamer package on how to create beamer presentations.

Note: Use "show notes on second screen=right" because dspdfviewer assumes that the right half of the page is the notes.

http://dspdfviewer.danny-edel.de for installation instructions and new release announcements.

dspdfviewer-1.15/docs/Windows/style.css000066400000000000000000000002531270070303100202070ustar00rootroot00000000000000td { vertical-align: top; } td p { margin: 0; } html, body { font-family: 'Segoe UI', 'Trebuchet MS', Tahoma, Verdana, Arial, sans-serif; font-size: 12pt; } dspdfviewer-1.15/docs/Windows/synopsis.html000066400000000000000000000004701270070303100211130ustar00rootroot00000000000000 Synopsis

Synopsis

dspdfviewer [options] [filename]

dspdfviewer-1.15/docs/dspdfviewer.1000066400000000000000000000247561270070303100173250ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" (C) Copyright 2012 Danny Edel , .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH DSPDFVIEWER 1 "July 30, 2015" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME dspdfviewer \- Dual-Screen PDF Viewer .SH SYNOPSIS .B dspdfviewer .RI [ options ] " [filename]" .br .SH DESCRIPTION .B dspdfviewer is intended for presentations created with the .B latex-beamer class, specifically documents created with the .RI "show notes on second screen=right" option. Its purpose is to display the left half of a page on the audience's screen (which will most commonly be a projector) and the right half on your "private" screen (think: the notebook you carried to the presentation site). The basic idea is that you will display personal notes and information to help you with your presentation on your personal screen, while the audience sees only the PDF they are supposed to see. On the secondary screen you will see three clocks: A "wall clock", a "presentation" clock and a "slide" clock. The wall clock in the bottom left corner that display current time of day, the presentation clock (labeled "Total") counts the time you spent since you started the presentation, and the "slide" counter (bottom right) displays the time you spent on the current slide. The clocks don't start the second you launch the program, the idea behind that behaviour is that many people have a "title" slide active long before the audience even arrives. Once you use the next/previous slide commands, the clock starts. If you want to start the clock, but still display the title slide, just press "previous" (for example, Up Arrow or Right Mouse Button) on the title slide. If you want to reset the clocks to zero, press the "Home" button on the keybord. .\" .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .\"\fBdspdfviewer\fP is a program that... .SH OPTIONS The exact behaviour of \fBdspdfviewer\fR is configurable using command-line parameters. Unless otherwise noted, all command-line options can be written in a configuration file. See the \fIFILES\fR section for the location. For the command-line parameters, this program follows the usual GNU command line syntax, with long options starting with two dashes ('-'). A summary of options is included below. If you specify those in a configuration file, write one option per line, use the \fIlong version without the two leading dashes\fR, followed by an equals sign and the desired value. For example, \fBbottom-pane-height=20\fR is a valid configuration line. If an option is specified in \fBboth\fR the configuration file and the command-line, the command-line value takes precedence. Note for options of type \fI\fR: They take \fBtrue\fR, \fBfalse\fR, \fB0\fR or \fB1\fR as arguments. For example, \fB\-\-use-second-screen false\fR can be expressed as \fB\-\-use-second-screen 0\fR or shortened to \fB\-u0\fR. .TP .BR \-h ", " \-\-help Show summary of options. .TP .B \-f, \-\-full-page Use the presentation program with a normal single-screen PDF, that is display the full page on both the primary and secondary monitor. The secondary screen still contains timers and thumbnails to help you with your presentation, unless you disable them using the options below. .TP .B \-u, \-\-use-second-screen \fI\fR Controls whether the program actually works as a dual-screen viewer. The default is true. If you only have one screen, but still want to use the cache features, you can set this option to false. If the window ends up on the wrong display, you can still use the switch command (see below). What comes out on the primary screen depends on whether you use the \-f Option: \-u0 \-f results in a simple full-screen PDF viewer, while \-u0 without \-f still splits the page in half and outputs only the left frame. .TP .B \-l, \-\-hyperlink-support \fI\fR Controls whether the program makes PDF Hyperlinks clickable. The default is true. While it is normally desired to have clickable hyperlinks, if you cannot control the position of your pointer (if your remote control has only left-click and right-click) it might be safer to disable hyperlinks. .TP .B \-\-prerender\-previous\-pages \fIn\fR When jumping to slide m, also pre-render the slides (m-n)..(m-1) (default 3) Note: If you set this to zero, you will not get a thumbnail for the previous page rendered before you have visited it. .TP .B \-\-prerender\-next\-pages \fIn\fR When jumping to slide m, also pre-render the slides (m+1)..(m+n) (default 10) Note: If you set this to zero, you will not get a thumbnail for the next page rendered before you have visited it. .TP .B \-\-cache\-size \fIn\fR (in megabytes / MiB) This controls how much memory dspdfviewer is allowed to use for caching rendered pages. The default value is 1024, meaning 1 GiB of RAM. Note that the memory used for active rendering processes, as well as the currently displayed images are not included in the accounting, so the total memory usage of dspdfviewer will be higher than this. CAVEAT: It is currently not verified that this memory is enough to hold all prerendered pages. If you make the cache too small to hold the prerendered pages, it will start dropping the first rendered (adjacent to the current page), resulting in very poor performance. If in doubt, set a larger cache or a lower prerender amount. .TP .B \-a, \-\-presenter-area \fI\fR Show (true, 1) or hide (false, 0) the presenter's area on the second screen. If this is set to zero, the following options will have no effect. .TP .B \-d, \-\-duplicate \fI\fR Duplicate (true, 1) or hide (false, 0) the audience's screen next to the notes on the second screen. The default is false. .TP .B \-t, \-\-thumbnails \fI\fR Show (true, 1) or hide (false, 0) the thumbnails on the second screen. .TP .B \-T, \-\-thumbnail\-page\-part \fI\fR Show this part of the page for thumbnails, valid values are "left", "right" or "both". .TP .B \-w, \-\-wall\-clock \fI\fR Show (true, 1) or hide (false, 0) the wall clock on the second screen. .TP .B \-p, \-\-presentation\-clock \fI\fR Show (true, 1) or hide (false, 0) the presentation clock on the second screen. .TP .B \-s, \-\-slide\-clock \fI\fR Show (true, 1) or hide (false, 0) the slide clock on the second screen. .TP .B \-b, \-\-bottom\-pane\-height \fIn\fR Configure how large the presenter area on the second screen will be, unit is \fIpercent of second screen's total height\fR. Default value 20. .TP .B \-\-i3\-workaround \fI\fR This tries to ask the i3 window manager (by calling the i3-msg command) to move the audience window to "the other screen". This is considered an experimental feature and disabled by default. Read the runtimeconfiguration.cpp source code for more info. .SH CONTROLS You can use the following controls while .B dspdfviewer is running. Note that mouse-clicks are subject to pointer position if the hyperlink support is enabled. If your mouse cursor changes to a hand, it will follow the link you are hovering on a click. .TP .B Keyboard: ?, F1 Display quick-help (most important key bindings) .TP .B Keyboard: Spacebar, Page Down / Mouse: Left-Click, Wheel Down Go one page forward Additional keyboard aliases: Down, Right, Return, Enter, N, F .TP .B Keyboard: Backspace, Page Up / Mouse: Right-Click, Wheel Up Go one page backward Additional keyboard aliases: Up, Left, P .TP .B Keyboard: B, . (period) Toggle blanking of the audience screen .TP .B Keyboard: G Go to specific page (a number entry window will pop up) Note that the this counts PDF pages, so a single slide having 6 unmasking steps will be 7 PDF pages long, including the initial near-blank page. .TP .B Keyboard: Home, H Go to the first page and reset the clocks to zero. Use this if you want the clocks to stop at zero, use the G (goto) function to page 1 if you want to them to keep running. Note that the Home key may be called "Pos 1" on some keyboards. .TP .B Keyboard: Escape, Q Quit \fBdspdfviewer\fR. .TP .B Keyboard: S, F12 Switch primary and secondary screens Use this if the audience sees your 'note' side with the clocks and you see the actual presentation on your screen. If you just want to see the main presentation (for example, because the projector is behind you), use \fBT\fR. .TP .B Keyboard: T Switch secondary screen's function Use this if you want to see the audience side on your screen, without showing the notes to the audience. Pressing the button again will switch back to normal operation. .TP .B Keyboard: D Switch duplication of the audience's screen Use this if you want to see the audience side and the notes on your screen (so the full presentation), leaving the audience's screen untouched. Pressing the button again will switch back to split mode. .SH FILES .TP .B ~/.config/dspdfviewer.ini You can specify all long command-line options (without leading \-\-) here, in a "option=value" format, one per line. .SH PRESENTER REMOTE CONTROLS Most (maybe not all) presenter remote controls technically act as a keyboard sending one of the keystrokes listed either in the main or additional keybindings. If your remote control does not work as intended, please file a bug report describing what keystrokes it generates when you press its buttons. If there is no conflict, a keybinding will be included in the next release. .SH BUGS None currently known. If you find any bugs, the preferred way to report them is at the github issue tracker at https://github.com/dannyedel/dspdfviewer/issues. As a fallback, you can send e-mail to mail@danny-edel.de with the word "dspdfviewer" in the subject line. .SH SEE ALSO .TP .BR /usr/share/doc/latex-beamer/beameruserguide.pdf.gz from the .B latex-beamer package on how to create beamer presentations. Note: Use "show notes on second screen=right" because .B dspdfviewer assumes that the right half of the page is the notes. .TP .BR http://dspdfviewer.danny-edel.de for installation instructions and new release announcements. dspdfviewer-1.15/dspdfviewer.cpp000066400000000000000000000273151270070303100170110ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dspdfviewer.h" #include "renderutils.h" #include "renderingidentifier.h" #include "sconnect.h" #include #include #include #include #include #include #include #include "debug.h" #include #include using boost::numeric_cast; DSPDFViewer::DSPDFViewer(const RuntimeConfiguration& r): runtimeConfiguration(r), clockDisplayTimer(), slideStart(), presentationStart(), presentationClockRunning(false), documentFileWatcher(), renderFactory(r), m_pagenumber(0), audienceWindow(1, r.useFullPage() ? PagePart::FullPage : PagePart::LeftHalf , false, r, WindowRole::AudienceWindow), secondaryWindow(0, (r.useFullPage() | r.duplicate())? PagePart::FullPage : PagePart::RightHalf, true , r, WindowRole::PresenterWindow, r.useSecondScreen()) { DEBUGOUT << tr("Starting constructor") ; if ( ! r.useSecondScreen() ) { secondaryWindow.hide(); } audienceWindow.showLoadingScreen(0); secondaryWindow.showLoadingScreen(0); #if 0 // FIXME Make sure exceptions on startup get handled correctly if ( ! pdfDocument || pdfDocument->isLocked() ) { /// FIXME: Error message throw std::runtime_error( tr("I was not able to open the PDF document. Sorry.").toLocal8Bit().constData() ); } #endif DEBUGOUT << tr("Connecting audience window"); audienceWindow.setPageNumberLimits(0, numberOfPages()-1); sconnect( &renderFactory, SIGNAL(pageRendered(QSharedPointer)), &audienceWindow, SLOT(renderedPageIncoming(QSharedPointer))); sconnect( &renderFactory, SIGNAL(pdfFileRereadSuccesfully()), this, SLOT(renderPage())); sconnect( &audienceWindow, SIGNAL(nextPageRequested()), this, SLOT(goForward())); sconnect( &audienceWindow, SIGNAL(previousPageRequested()), this, SLOT(goBackward())); sconnect( &audienceWindow, SIGNAL(pageRequested(uint)), this, SLOT(gotoPage(uint))); sconnect( &audienceWindow, SIGNAL(quitRequested()), this, SLOT(exit())); sconnect( &audienceWindow, SIGNAL(rerenderRequested()), this, SLOT(renderPage())); sconnect( &audienceWindow, SIGNAL(restartRequested()), this, SLOT(goToStartAndResetClocks())); sconnect( &audienceWindow, SIGNAL(screenSwapRequested()), this, SLOT(swapScreens()) ); sconnect( &audienceWindow, SIGNAL(blankToggleRequested()), this, SLOT(toggleAudienceScreenBlank())); sconnect( &audienceWindow, SIGNAL(secondScreenFunctionToggleRequested()), this, SLOT(toggleSecondaryScreenFunction())); sconnect( &audienceWindow, SIGNAL(secondScreenDuplicateRequested()), this, SLOT(toggleSecondaryScreenDuplication())); if ( r.useSecondScreen() ) { DEBUGOUT << tr("Connecting secondary window"); secondaryWindow.setPageNumberLimits(0, numberOfPages()-1); sconnect( &renderFactory, SIGNAL(pageRendered(QSharedPointer)), &secondaryWindow, SLOT(renderedPageIncoming(QSharedPointer))); sconnect( &secondaryWindow, SIGNAL(nextPageRequested()), this, SLOT(goForward())); sconnect( &secondaryWindow, SIGNAL(previousPageRequested()), this, SLOT(goBackward())); sconnect( &secondaryWindow, SIGNAL(pageRequested(uint)), this, SLOT(gotoPage(uint))); sconnect( &secondaryWindow, SIGNAL(quitRequested()), this, SLOT(exit())); sconnect( &secondaryWindow, SIGNAL(rerenderRequested()), this, SLOT(renderPage())); sconnect( &secondaryWindow, SIGNAL(restartRequested()), this, SLOT(goToStartAndResetClocks())); sconnect( &secondaryWindow, SIGNAL(screenSwapRequested()), this, SLOT(swapScreens()) ); sconnect( &secondaryWindow, SIGNAL(blankToggleRequested()), this, SLOT(toggleAudienceScreenBlank())); sconnect( &secondaryWindow, SIGNAL(secondScreenFunctionToggleRequested()), this, SLOT(toggleSecondaryScreenFunction())); sconnect( &secondaryWindow, SIGNAL(secondScreenDuplicateRequested()), this, SLOT(toggleSecondaryScreenDuplication())); sconnect( this, SIGNAL(presentationClockUpdate(QTime)), &secondaryWindow, SLOT(updatePresentationClock(QTime))); sconnect( this, SIGNAL(slideClockUpdate(QTime)), &secondaryWindow, SLOT(updateSlideClock(QTime))); sconnect( this, SIGNAL(wallClockUpdate(QTime)), &secondaryWindow, SLOT(updateWallClock(QTime))); } renderPage(); clockDisplayTimer.setInterval(TIMER_UPDATE_INTERVAL); clockDisplayTimer.start(); sconnect( &clockDisplayTimer, SIGNAL(timeout()), this, SLOT(sendAllClockSignals())); sendAllClockSignals(); } DSPDFViewer::~DSPDFViewer() {} void DSPDFViewer::goBackward() { resetSlideClock(); if ( pageNumber() > 0 ) gotoPage(pageNumber()-1); } void DSPDFViewer::goForward() { resetSlideClock(); if ( pageNumber() < numberOfPages()-1 ) gotoPage(pageNumber()+1); } unsigned int DSPDFViewer::pageNumber() const { return m_pagenumber; } void DSPDFViewer::renderPage() { DEBUGOUT << tr("Requesting rendering of page %1").arg(m_pagenumber); if ( m_pagenumber >= numberOfPages() ) { DEBUGOUT << "Page number out of range, clamping to " << numberOfPages()-1; m_pagenumber = numberOfPages()-1; } audienceWindow.showLoadingScreen(m_pagenumber); secondaryWindow.showLoadingScreen(m_pagenumber); if ( runtimeConfiguration.showThumbnails() ) { theFactory()->requestPageRendering( toThumbnailRenderIdent(m_pagenumber, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering( toRenderIdent(m_pagenumber, audienceWindow), QThread::HighestPriority); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering( toRenderIdent(m_pagenumber, secondaryWindow), QThread::HighPriority); } /** Pre-Render next pages **/ for ( unsigned i=m_pagenumber; irequestPageRendering( toThumbnailRenderIdent(i, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering( toRenderIdent(i, audienceWindow)); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering( toRenderIdent(i, secondaryWindow)); } } /** Pre-Render previous pages **/ for ( unsigned i= std::max(m_pagenumber,runtimeConfiguration.prerenderPreviousPages())-runtimeConfiguration.prerenderPreviousPages(); irequestPageRendering( toThumbnailRenderIdent(i, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering(toRenderIdent(i, audienceWindow)); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering(toRenderIdent(i, secondaryWindow)); } } } void DSPDFViewer::gotoPage(unsigned int pageNumber) { if ( m_pagenumber != pageNumber && numberOfPages() > pageNumber ) { m_pagenumber = pageNumber; renderPage(); } else { WARNINGOUT << tr("Requested page number %1 which is out of range! Ignoring request.").arg(pageNumber); } } void DSPDFViewer::swapScreens() { if ( audienceWindow.getMonitor() == 0 ) { audienceWindow.setMonitor(1); secondaryWindow.setMonitor(0); renderPage(); } else { audienceWindow.setMonitor(0); secondaryWindow.setMonitor(1); renderPage(); } } void DSPDFViewer::exit() { audienceWindow.close(); secondaryWindow.close(); } PdfRenderFactory* DSPDFViewer::theFactory() { return &renderFactory; } unsigned int DSPDFViewer::numberOfPages() const { int pages = renderFactory.numberOfPages(); // Numeric cast includes error handling. return numeric_cast(pages); } void DSPDFViewer::goToStartAndResetClocks() { presentationClockRunning=false; sendAllClockSignals(); gotoPage(0); } QTime DSPDFViewer::presentationClock() const { if ( ! presentationClockRunning ) return QTime(0,0); return timeSince(presentationStart); } QTime DSPDFViewer::wallClock() const { return QTime::currentTime(); } QTime DSPDFViewer::slideClock() const { if ( ! presentationClockRunning ) return QTime(0,0); return timeSince( slideStart ); } void DSPDFViewer::resetSlideClock() { /* Always resets the slide clock. */ slideStart.start(); if ( ! presentationClockRunning ) { /* If this starts a presentation, also reset the presentation clock. */ presentationStart.start(); } /* and make sure they'll get refreshed a second later aswell. */ slideStart.start(); presentationClockRunning=true; /* Refresh display times immediately */ sendAllClockSignals(); } void DSPDFViewer::sendAllClockSignals() const { emit wallClockUpdate(wallClock()); emit slideClockUpdate(slideClock()); emit presentationClockUpdate(presentationClock()); } QTime DSPDFViewer::timeSince(const QTime& startPoint) const { QTime result(0,0); result = result.addMSecs(startPoint.elapsed()); return result; } RenderingIdentifier DSPDFViewer::toRenderIdent(unsigned int pageNumber, const PDFViewerWindow& window) { return RenderingIdentifier ( pageNumber, window.getMyPagePart(), window.getTargetImageSize()); } RenderingIdentifier DSPDFViewer::toThumbnailRenderIdent(unsigned int pageNumber, PDFViewerWindow& window) { QSize newSize = window.getPreviewImageSize();; static QSize thumbnailSize; if ( thumbnailSize != newSize ) { DEBUGOUT << "Thumbnail size changed from" << thumbnailSize << "to" << newSize; thumbnailSize=newSize; renderPage(); } return RenderingIdentifier( pageNumber, runtimeConfiguration.thumbnailPagePart(), thumbnailSize); } bool DSPDFViewer::isAudienceScreenBlank() const { return audienceWindow.isBlank(); } void DSPDFViewer::setAudienceScreenBlank() { audienceWindow.setBlank(true); } void DSPDFViewer::setAudienceScreenVisible() { audienceWindow.setBlank(false); } void DSPDFViewer::toggleAudienceScreenBlank() { if ( isAudienceScreenBlank() ) { setAudienceScreenVisible(); } else { setAudienceScreenBlank(); } } void DSPDFViewer::toggleSecondaryScreenFunction() { DEBUGOUT << "Swapping second screen"; switch ( secondaryWindow.getMyPagePart() ) { case PagePart::FullPage: // Nothing to do break; case PagePart::LeftHalf: secondaryWindow.setMyPagePart(PagePart::RightHalf); break; case PagePart::RightHalf: secondaryWindow.setMyPagePart(PagePart::LeftHalf); break; } emit renderPage(); } void DSPDFViewer::toggleSecondaryScreenDuplication() { DEBUGOUT << "Swapping second screen duplication"; switch ( secondaryWindow.getMyPagePart() ) { case PagePart::FullPage: if (!runtimeConfiguration.useFullPage()) { secondaryWindow.setMyPagePart(PagePart::RightHalf); } break; case PagePart::LeftHalf: case PagePart::RightHalf: secondaryWindow.setMyPagePart(PagePart::FullPage); break; } emit renderPage(); } const QRect DSPDFViewer::audienceGeometry() const { return audienceWindow.geometry(); } const QRect DSPDFViewer::secondGeometry() const { return secondaryWindow.geometry(); } dspdfviewer-1.15/dspdfviewer.desktop000066400000000000000000000003631270070303100176720ustar00rootroot00000000000000[Desktop Entry] Type=Application Version=1.0 Name=DS PDF Viewer Comment=Dual-Screen PDF Viewer for LaTeX-Beamer presentations utilizing the "show notes on second screen" option Keywords=pdf;presentation;latex;beamer;notes; Exec=dspdfviewer %f dspdfviewer-1.15/dspdfviewer.h000066400000000000000000000065571270070303100164630ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef dspdfviewer_H #define dspdfviewer_H #include #include #include #include "poppler-qt.h" #include "pdfviewerwindow.h" #include "pdfrenderfactory.h" #include "runtimeconfiguration.h" class DSPDFViewer: public QObject { Q_OBJECT private: const RuntimeConfiguration& runtimeConfiguration; enum { TIMER_UPDATE_INTERVAL=250 }; private: QTimer clockDisplayTimer; QTime slideStart; QTime presentationStart; bool presentationClockRunning; private: QFileSystemWatcher documentFileWatcher; PdfRenderFactory renderFactory; unsigned int m_pagenumber; PDFViewerWindow audienceWindow; PDFViewerWindow secondaryWindow; private: QString timeToString(QTime time) const; QString timeToString(int milliseconds) const; void resetSlideClock(); RenderingIdentifier toRenderIdent(unsigned int pageNumber, const PDFViewerWindow& window); RenderingIdentifier toThumbnailRenderIdent(unsigned int pageNumber, PDFViewerWindow& window); private slots: void sendAllClockSignals() const; public: DSPDFViewer(const RuntimeConfiguration& r); virtual ~DSPDFViewer(); /** get current page number */ unsigned int pageNumber() const; /** get page count */ unsigned int numberOfPages() const; void setHighQuality(bool hq); bool isReadyToRender() const; PdfRenderFactory* theFactory(); QTime wallClock() const; QTime slideClock() const; QTime presentationClock() const; QTime timeSince( const QTime& startPoint) const; bool isAudienceScreenBlank() const; /** Allow const access to the windows' positions. * Needed for tests to inspect things. */ const QRect audienceGeometry() const; const QRect secondGeometry() const; signals: void wallClockUpdate(const QTime& wallClock) const; void slideClockUpdate(const QTime& slideClock) const; void presentationClockUpdate(const QTime& presentationClock) const; public slots: /** (re-)Renders the current page on both monitors */ void renderPage(); /** goes to the specified page. Pages start at zero. */ void gotoPage(unsigned int pageNumber); /** go Forward one page */ void goForward(); /** go backward one page */ void goBackward(); void goToStartAndResetClocks(); void swapScreens(); void toggleAudienceScreenBlank(); void setAudienceScreenBlank(); void setAudienceScreenVisible(); void toggleSecondaryScreenFunction(); void toggleSecondaryScreenDuplication(); void exit(); }; #endif // dspdfviewer_H dspdfviewer-1.15/dspdfviewer.qrc000066400000000000000000000002351270070303100170040ustar00rootroot00000000000000 dspdfviewer_de.qm dspdfviewer-1.15/hyperlinkarea.cpp000066400000000000000000000046131270070303100173210ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "hyperlinkarea.h" #include #include #include #include "debug.h" using boost::math::iround; HyperlinkArea::HyperlinkArea(QLabel* imageLabel, const AdjustedLink& link): QLabel(), targetPage(link.targetPageNumber()) { if ( link.linkType() != Poppler::Link::Goto ) throw WrongLinkType(); QRect mySize; const QPixmap* pixmap = imageLabel->pixmap(); if ( pixmap == 0 ) throw /** FIXME Exception **/ std::runtime_error("Tried to construct a HyperlinkArea from an image label without a pixmap"); QRectF sizeWithinImageLabel = link.linkArea(); mySize.setTop( iround(sizeWithinImageLabel.top() * pixmap->height()) ); mySize.setLeft( iround(sizeWithinImageLabel.left() * pixmap->width()) ); mySize.setHeight(std::abs( iround(sizeWithinImageLabel.height() * pixmap->height())) ); mySize.setWidth( iround(sizeWithinImageLabel.width() * pixmap->width()) ); setParent(imageLabel); setGeometry(mySize); /* setAutoFillBackground(true); QPalette pal = palette(); pal.setColor(QPalette::Window, QColor(Qt::black) ); setPalette(pal); */ // setText( QString("%1 @ +%2,%3").arg(targetPage).arg( link.linkArea().right() ). arg(link.linkArea().top()) ); show(); setCursor( Qt::PointingHandCursor ); DEBUGOUT << "Added an hyperlink to" << link.targetPageNumber() << "at" << geometry(); } void HyperlinkArea::mousePressEvent(QMouseEvent* ev) { DEBUGOUT << "Hyperlink clicked" << ev << "Target page" << targetPage; emit gotoPageRequested(targetPage); } dspdfviewer-1.15/hyperlinkarea.h000066400000000000000000000024531270070303100167660ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef HYPERLINKAREA_H #define HYPERLINKAREA_H #include #include "poppler-qt.h" #include "adjustedlink.h" class HyperlinkArea : public QLabel { Q_OBJECT /** FIXME Exception class */ struct WrongLinkType{}; uint targetPage; public: HyperlinkArea(QLabel* imageLabel, const AdjustedLink& gotoLink); virtual void mousePressEvent(QMouseEvent* ev); signals: void gotoPageRequested(uint targetPage); }; #endif // HYPERLINKAREA_H dspdfviewer-1.15/keybindings.ui000066400000000000000000000157771270070303100166410ustar00rootroot00000000000000 KeybindingsDialog 0 0 554 224 0 0 Keybindings true 10 75 true Key 10 75 true Action Next slide N or Left/Down arrow P or Right/Down arrow Blank/Unblank audience screen Quit Previous slide Go to specific slide Toggle between notes and slides on the secondary screen Toggle between displaying both notes and slides on the secondary screen or only notes Switch primary and secondary screens Go to first page and reset counters Show this help box B or . G H or Home Q or Esc S or F12 T D ? or F1 Qt::Horizontal 40 20 OK true Qt::Horizontal 40 20 dspdfviewer version %1 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter pushButton clicked() KeybindingsDialog accept() 272 200 398 201 dspdfviewer-1.15/main.cpp000066400000000000000000000071241270070303100154070ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "debug.h" #include "dspdfviewer.h" #include "runtimeconfiguration.h" #include #include #include #include #include #include #if defined ( _WIN32 ) #pragma comment(linker, "/ENTRY:mainCRTStartup") #if defined( NDEBUG ) #pragma comment(linker, "/SUBSYSTEM:windows") #endif #endif int main(int argc, char** argv) { QApplication app(argc, argv); app.setApplicationName( QString::fromUtf8("dspdfviewer") ); app.setApplicationVersion( QString::fromUtf8( DSPDFVIEWER_VERSION ) ); const auto locale = QLocale::system(); const auto localeName = locale.name(); const auto systemTranslationsPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); QTranslator qtTranslator; DEBUGOUT << "Loading qt system translation for" << localeName << "from" << systemTranslationsPath; if ( !qtTranslator.load( QString::fromUtf8( "qt_" ) + localeName, systemTranslationsPath ) ) { WARNINGOUT << "Failed to load qt translations for locale" << localeName; } else { app.installTranslator(&qtTranslator); DEBUGOUT << "Qt system translation loaded for" << localeName; } QTranslator appTranslator; DEBUGOUT << "Loading dspdfviewer translation for current locale:" << localeName; if ( ! appTranslator.load(QString::fromUtf8(":/translations/dspdfviewer") ) ) { WARNINGOUT << "Failed to load dspdfviewer translation for current locale" << localeName; } else { app.installTranslator(&appTranslator); DEBUGOUT << "dspdfviewer translation loaded for" << localeName; } /* If anything goes wrong, try to display the exception to the user. * Its the least i can do. */ try { /* Register the meta-type so that rendered pages can be passed around * using Qt's event/callback system */ qRegisterMetaType< QSharedPointer >("QSharedPointer"); RuntimeConfiguration rc(argc, argv); if ( ! rc.filePathDefined() ) { rc.filePath( qPrintable( QFileDialog::getOpenFileName( nullptr, QFileDialog::tr("Load PDF from disk"), QString(), QFileDialog::tr("PDF (*.pdf)") ) ) ); } DSPDFViewer foo( rc ); return app.exec(); } catch ( std::exception& e ) { std::cerr << "----- FATAL ERROR -----" << std::endl << qPrintable( QCoreApplication::translate("DSPDFViewer", "Dual-Screen PDF Viewer has encountered an error and cannot continue") ) << std::endl << e.what() << std::endl; QMessageBox errorMsg; errorMsg.setText( QCoreApplication::translate("DSPDFViewer", "Dual-Screen PDF Viewer has encountered an error and cannot continue") ); errorMsg.setInformativeText( QString::fromLocal8Bit( e.what() ) ); errorMsg.setDefaultButton(QMessageBox::Discard); errorMsg.setIcon( QMessageBox::Critical ); errorMsg.exec(); return 1; } } dspdfviewer-1.15/pagepart.cpp000066400000000000000000000030321270070303100162600ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pagepart.h" #include namespace { const char * const left = "left"; const char * const right = "right"; const char * const both = "both"; } std::ostream& operator << ( std::ostream& out, const PagePart& pagepart ) { if ( pagepart == PagePart::LeftHalf ) return out << left; else if ( pagepart == PagePart::RightHalf ) return out << right; else return out << both; } std::istream& operator >> ( std::istream& in, PagePart& pagepart ) { std::string s; in >> s; if ( s == left ) pagepart = PagePart::LeftHalf; else if ( s == right ) pagepart = PagePart::RightHalf; else if ( s == both ) pagepart = PagePart::FullPage; else in.setstate( std::ios::failbit ); return in; } dspdfviewer-1.15/pagepart.h000066400000000000000000000021611270070303100157270ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PAGEPART_H #define PAGEPART_H #include enum class PagePart { FullPage, LeftHalf, RightHalf }; std::istream& operator >> ( std::istream& in, PagePart& pagepart ); std::ostream& operator << ( std::ostream& out, const PagePart& pagepart ); #endif // PAGEPART_H dspdfviewer-1.15/pdfcacheoption.h000066400000000000000000000002221270070303100171060ustar00rootroot00000000000000#ifndef PDFCACHEOPTION_H #define PDFCACHEOPTION_H enum class PDFCacheOption { keepPDFinMemory, rereadFromDisk }; #endif // PDFCACHEOPTION_H dspdfviewer-1.15/pdfdocumentreference.cpp000066400000000000000000000060141270070303100206470ustar00rootroot00000000000000#include "pdfdocumentreference.h" #include "pdfpagereference.h" #include #include #include "debug.h" QSharedPointer< const Poppler::Document > PDFDocumentReference::popplerDocument() const { // Make sure this function is "single threaded" Lock lk{mutex_}; if ( ! popplerDocument_ ) { /** No document defined yet. Create it from the disk/memory cache. */ QSharedPointer m_document; if ( cacheOption() == PDFCacheOption::rereadFromDisk ) { DEBUGOUT << "Trying to build a Poppler::Document from file" << filename(); QSharedPointer diskDocument( Poppler::Document::load(filename()) ); m_document.swap(diskDocument); } else if ( cacheOption() == PDFCacheOption::keepPDFinMemory ) { DEBUGOUT << "Trying to build a document from" << fileContents_.size() << "byte memory cache"; QSharedPointer memoryDocument( Poppler::Document::loadFromData(fileContents_) ); m_document.swap(memoryDocument); } if ( !m_document || m_document->isLocked() ) throw std::runtime_error("Document not readable"); m_document->setRenderHint(Poppler::Document::Antialiasing, true); m_document->setRenderHint(Poppler::Document::TextAntialiasing, true); m_document->setRenderHint(Poppler::Document::TextHinting, true); popplerDocument_ = m_document; } return popplerDocument_; } PDFPageReference PDFDocumentReference::page(unsigned int pageNumber) const { PDFPageReference ppr(*this, pageNumber); return ppr; } PDFDocumentReference::PDFDocumentReference(const QString& theFilename, const PDFCacheOption& theCacheOption): filename_(theFilename), fileContents_(), cacheOption_(theCacheOption), mutex_(), popplerDocument_() { if ( cacheOption() == PDFCacheOption::keepPDFinMemory ) { DEBUGOUT << "Reading file into memory"; QFile file(filename()); file.open(QIODevice::ReadOnly); fileContents_ = file.readAll(); DEBUGOUT << fileContents_.size() << "bytes read"; } } const PDFCacheOption& PDFDocumentReference::cacheOption() const { return cacheOption_; } const QString& PDFDocumentReference::filename() const { return filename_; } PDFDocumentReference& PDFDocumentReference::operator=(const PDFDocumentReference& rhs) { if ( rhs.filename() != filename() ) { throw std::runtime_error("This PDFDocumentReference has a different filename"); } if ( rhs.cacheOption() != cacheOption() ) { throw std::runtime_error("This PDFDocumentReference has a different cache setting"); } fileContents_=rhs.fileContents_; popplerDocument_=rhs.popplerDocument_; return *this; } bool operator==(const PDFDocumentReference& lhs, const PDFDocumentReference& rhs) { if ( lhs.cacheOption() != rhs.cacheOption() ) { return false; } else if ( lhs.cacheOption() == PDFCacheOption::keepPDFinMemory ) { DEBUGOUT << "Using memory cache, comparing byte-by-byte."; return lhs.fileContents_ == rhs.fileContents_; } else { DEBUGOUT << "Not using memory cache, just comparing the filename."; return lhs.filename() == rhs.filename(); } } dspdfviewer-1.15/pdfdocumentreference.h000066400000000000000000000043571270070303100203240ustar00rootroot00000000000000#ifndef PDFDOCUMENTREFERENCE_H #define PDFDOCUMENTREFERENCE_H #include #include #include "pdfcacheoption.h" #include "poppler-qt.h" #include // forward-declare struct PDFPageReference; /** Holds a reference to a PDF document. * * What exactly this means depends on the cache option: * If the cache option is hold in memory, this class wild have a * complete in-memory copy of the PDF file. * * If the cache option is reread from disk, the reference is * just the file name. * */ class PDFDocumentReference { private: const QString filename_; QByteArray fileContents_; const PDFCacheOption cacheOption_; // protects access to the poppler document mutable std::mutex mutex_; typedef std::lock_guard Lock; mutable QSharedPointer popplerDocument_; public: /** Create the document reference. * * If the cache option is keep in memory, this will read the entire file into memory. */ PDFDocumentReference( const QString& filename, const PDFCacheOption& cacheOption); /** Returns a Poppler::Page reference to the requested page number * NOTE: Until poppler supports multi-threading, this will * create a new Poppler::Document for each request. */ PDFPageReference page(unsigned pageNumber) const; /** Create a Poppler::Document from this reference. * If you did not cache the file to memory, this step will try to * read the file. */ QSharedPointer popplerDocument() const; /** get filename */ const QString& filename() const; /** get cache setting */ const PDFCacheOption& cacheOption() const; /** Update the document reference. * This only works if rhs has the same filename and cache options, * effectively only updating the file contents buffer. */ PDFDocumentReference& operator = (const PDFDocumentReference& rhs); /** Compares the references. Exact behaviour depends on the cache option: * If we are memory-caching, this compares the ByteArrays (i.e. checks for * identical files). * * If we are not caching, it just checks for the same filename. */ friend bool operator == (const PDFDocumentReference& lhs, const PDFDocumentReference& rhs); }; #endif // PDFDOCUMENTREFERENCE_H dspdfviewer-1.15/pdfpagereference.cpp000066400000000000000000000005151270070303100177450ustar00rootroot00000000000000#include "pdfpagereference.h" #include using boost::numeric_cast; PDFPageReference::PDFPageReference(const PDFDocumentReference& documentReference, const unsigned int& pageNumber): document( documentReference.popplerDocument() ), page( document->page( numeric_cast(pageNumber) ) ) { } dspdfviewer-1.15/pdfpagereference.h000066400000000000000000000011711270070303100174110ustar00rootroot00000000000000#ifndef PDFPAGEREFERENCE_H #define PDFPAGEREFERENCE_H #include #include "poppler-qt.h" #include "pdfdocumentreference.h" /** This holds a reference to a page in a certain document * WARNING: This also contains a shared pointer to the Poppler::Document, * so if you delete this object, using the returned page() is undefined * behaviour. */ struct PDFPageReference { const QSharedPointer document; const QSharedPointer page; PDFPageReference( const PDFDocumentReference& documentReference, const unsigned int& pageNumber ); }; #endif // PDFPAGEREFERENCE_H dspdfviewer-1.15/pdfrenderfactory.cpp000066400000000000000000000160041270070303100200210ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pdfrenderfactory.h" #include "renderthread.h" #include "sconnect.h" #include #include #include #include "debug.h" #include "renderutils.h" #include namespace { /** Estimates size in bytes of a rendered Page * * This currently assumes 32 bit (4 byte) per pixel, and no * overhead. */ int cacheCost(const RenderedPage& renderedPage) { const QSize& imageSize = renderedPage.getImage().size(); return boost::numeric_cast(sizeof(RenderedPage)) + 4 * imageSize.width() * imageSize.height(); } } void PdfRenderFactory::pageThreadFinishedRendering(QSharedPointer renderedPage) { { QMutexLocker lock(&mutex); const RenderingIdentifier ident( renderedPage->getIdentifier() ); // Ignore this incoming rendering if it was from an old version if ( ident.theVersion != currentVersion ) return; renderedPages.insert(ident, new RenderedPage(*renderedPage), cacheCost(*renderedPage) ); currentlyRenderingPages.remove(ident); if ( renderedPages.contains(ident) ) { DEBUGOUT << "Stored" << ident << "into cache"; } else { WARNINGOUT << "Unable to store" << ident << "into the cache! It would cost" << cacheCost(*renderedPage) << "but Current load is" << renderedPages.totalCost() << "/" << renderedPages.maxCost(); } DEBUGOUT << "Current cache fill is " << renderedPages.totalCost() << "/" << renderedPages.maxCost() << '(' << 100.0 * renderedPages.totalCost() / renderedPages.maxCost() << "% )"; } emit pageRendered(renderedPage); } void PdfRenderFactory::rewatchFile() { if ( ! fileWatcher.files().contains( documentReference.filename() ) ) { fileWatcher.addPath( documentReference.filename() ); // Check if it has been added (i.e. if it exists) if ( fileWatcher.files().contains( documentReference.filename() ) ) { // The file was created in the meantime. FileWatcher does not report this as a "change", // So we have to check it manually. fileOnDiskChanged(documentReference.filename()); } } } PdfRenderFactory::PdfRenderFactory( const RuntimeConfiguration& rc): QObject(), documentReference(rc.filePathQString(), rc.cacheSetting()), fileWatcher(), fileWatcherRewatchTimer(), currentlyRenderingPages(), renderedPages( boost::numeric_cast(rc.cacheSizeBytes()) ), mutex(), currentVersion(0), // Attempt to read the document to get the number of pages within. // This will throw an error if the document is unreadable. numberOfPages_(documentReference.popplerDocument()->numPages()) { rewatchFile(); // register the on-change function sconnect(&fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fileOnDiskChanged(QString))); // Make sure it re-watches the file fileWatcherRewatchTimer.setInterval(1000); sconnect(&fileWatcherRewatchTimer, SIGNAL(timeout()), this, SLOT(rewatchFile())); fileWatcherRewatchTimer.start(); } void PdfRenderFactory::requestPageRendering(const RenderingIdentifier& originalIdentifier, QThread::Priority priority) { QMutexLocker lock(&mutex); RenderingIdentifier renderingIdentifier(originalIdentifier); renderingIdentifier.theVersion = currentVersion; if ( renderedPages.contains(renderingIdentifier) ) { /* Page is ready. Take a copy and lets go. */ QSharedPointer page( new RenderedPage( * renderedPages.object(renderingIdentifier) )); emit pageRendered(page); return; } /* Page was not in cache. Check if its currently in the render stage. */ if ( currentlyRenderingPages.contains(renderingIdentifier) ) { /* Page is already rendering, so there is nothing to do. */ return; } /* Nobody is working on the page right now. Lets create it. */ RenderThread* t = new RenderThread( documentReference, renderingIdentifier ); sconnect(t, SIGNAL(renderingFinished(QSharedPointer)), this, SLOT(pageThreadFinishedRendering(QSharedPointer))); currentlyRenderingPages.insert(renderingIdentifier); QThreadPool::globalInstance()->start(t, priority); } void PdfRenderFactory::fileOnDiskChanged(const QString& filename) { DEBUGOUT << "File" << filename << "has changed on disk"; if ( filename != documentReference.filename() ) { DEBUGOUT << "Ignoring that file."; return; } // Add path back in case it was modified via "move temporary onto filename", // which filewatcher treats as a remove and stops watching try { emit pdfFileChanged(); { // Lock mutex QMutexLocker locker(&mutex); // Create a new File Reference PDFDocumentReference newDoc(filename, documentReference.cacheOption()); if ( documentReference.cacheOption() == PDFCacheOption::keepPDFinMemory ) { // If we keep them in memory, a byte-by-byte compare should be resonably fast. // If they are *identical*, we can skip the reloading. if( documentReference == newDoc ) { DEBUGOUT << "The new document compares identical to the old one, not doing anything."; return; } } DEBUGOUT << "The file on disk has different contents. Trying to parse it as PDF."; // Verify poppler can read this newDoc.popplerDocument(); // replace the current reference with the new one documentReference = newDoc; numberOfPages_ = documentReference.popplerDocument()->numPages(); DEBUGOUT << "New document has" << numberOfPages_ << "pages."; // clear the page cache clearAllCaches(); } emit pdfFileRereadSuccesfully(); } catch( std::runtime_error& ) { DEBUGOUT << "Unable to read the new reference. keeping the old one."; emit pdfFileRereadFailed(); } } void PdfRenderFactory::clearAllCaches() { // Increment version, so that incoming "old" renders will get ignored /// TODO: Send a termination signal to these lingering threads ++currentVersion; // No renders of the current version are taking place, incoming old renders // will be ignored. currentlyRenderingPages.clear(); // Remove the caches. Since we use explicit copy semantics, its safe to empty // these. renderedPages.clear(); } int PdfRenderFactory::numberOfPages() const { QMutexLocker lock(&mutex); return numberOfPages_; } PdfRenderFactory::~PdfRenderFactory() { RenderUtils::notifyShutdown(); } dspdfviewer-1.15/pdfrenderfactory.h000066400000000000000000000077161270070303100175000ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PDFRENDERFACTORY_H #define PDFRENDERFACTORY_H #include #include #include #include #include #include #include #include "poppler-qt.h" #include "renderedpage.h" #include "runtimeconfiguration.h" #include "pdfcacheoption.h" #include "pdfdocumentreference.h" /** Factory for rendered pages * * This class is responsible for rendering the PDF to images. * * You create a factory using a filename (if the file cannot be opened this will throw), * then you can simply request renderings by supplying the desired size and page part * to requestPageRendering. * * The class uses the global instance of QThreadPool and renders in paralell, meaning it * can take advantage of SMP or Multi-Core Systems. * * NOTE for updating the external file: * If you plan on rewriting the displayed file while this application is running, * you can cache the entire file to memory. This can be done via constructor parameter. * * That way, if you create an invalid PDF file (for example you make an error in your latex * document), it still has a copy of the old file present. * The tradeoff is that this will cost you additional memory for *the entire PDF file*, which * can be a lot if you have an image-heavy PDF file. * * The class will notify you via signals when it detects a file change. Then it will try to re-read * the file, and it will notify you if that has succeeded or failed. * * If the re-reading failed, you can still get pages from cache, and you can keep rendering new ones * if you have used the memory cache. However, if you try to render new pages, you will probably * experience strange behaviour or the program will crash. * * If the re-reading went well, the cache will be cleared and new page renders will use the new pdf * file. * */ class PdfRenderFactory : public QObject { Q_OBJECT private: PDFDocumentReference documentReference; QFileSystemWatcher fileWatcher; QTimer fileWatcherRewatchTimer; QSet< RenderingIdentifier > currentlyRenderingPages; QCache< RenderingIdentifier, RenderedPage> renderedPages; mutable QMutex mutex; /** This is a little helper for the cache-clear-function to detect * renderings that have been started before, but finished after * a cache clearing. */ quint64 currentVersion; int numberOfPages_; private: void clearAllCaches(); public: PdfRenderFactory( const RuntimeConfiguration& ); ~PdfRenderFactory(); /** Request a page rendering. Defaults to low priority (i.e. background rendering), please set High priority manually * on the current page. */ void requestPageRendering( const RenderingIdentifier& originalIdentifier, QThread::Priority priority = QThread::LowPriority); int numberOfPages() const; private slots: void fileOnDiskChanged(const QString& filename); void pageThreadFinishedRendering( QSharedPointer renderedPage ); signals: void pageRendered( QSharedPointer renderedPage); void pdfFileChanged(); void pdfFileRereadSuccesfully(); void pdfFileRereadFailed(); public slots: void rewatchFile(); }; #endif // PDFRENDERFACTORY_H dspdfviewer-1.15/pdfviewerwindow.cpp000066400000000000000000000347641270070303100177200ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pdfviewerwindow.h" #include #include #include #include #include #if defined(POPPLER_QT5) && defined(_WIN32) #include #endif #include "debug.h" #include #include #include "sconnect.h" #include #include #include "ui_keybindings.h" using boost::numeric_cast; void PDFViewerWindow::setMonitor(const unsigned int monitor) { if ( m_monitor != monitor ) { m_monitor = monitor; reposition(); } } unsigned int PDFViewerWindow::getMonitor() const { return m_monitor; } PDFViewerWindow::PDFViewerWindow(unsigned int monitor, PagePart pagePart, bool showInformationLine, const RuntimeConfiguration& r, const WindowRole& wr, bool enabled): QWidget(), ui(), m_enabled(enabled), m_monitor(monitor), currentImage(), blank(false), informationLineVisible(false), currentPageNumber(0), minimumPageNumber(0), maximumPageNumber(65535), correctImageRendered(false), myPart(pagePart), windowRole(wr), runtimeConfiguration(r), linkAreas() { if ( ! enabled ) return; ui.setupUi(this); unsigned mainImageHeight=100-r.bottomPaneHeight(); ui.verticalLayout->setStretch(0, numeric_cast(mainImageHeight) ); ui.verticalLayout->setStretch(1, numeric_cast(r.bottomPaneHeight()) ); setWindowRole(to_QString(wr)); /*: User visible Window Title Line */ if ( windowRole == WindowRole::AudienceWindow ) { setWindowTitle(tr("DS PDF Viewer - Audience Window")); } else { setWindowTitle(tr("DS PDF Viewer - Secondary Window")); } if ( !showInformationLine || ! r.showPresenterArea()) { /* If the information line is disabled because we're the primary screen, * or the user explicitly said so, disable it completely. */ hideInformationLine(); } else { /* Enable the information line, but control visibility of the components as requested by the user. */ this->showInformationLine(); ui.wallClock->setVisible(r.showWallClock()); ui.thumbnailArea->setVisible(r.showThumbnails()); ui.slideClock->setVisible(r.showSlideClock()); ui.presentationClock->setVisible(r.showPresentationClock()); } reposition(); // This will fullscreen on its own } void PDFViewerWindow::reposition() { if ( ! m_enabled ) return; this->setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); this->showNormal(); #if defined(POPPLER_QT5) && defined(_WIN32) static QList screens = QApplication::screens(); if ( m_monitor < numeric_cast(screens.count()) ) this->windowHandle()->setScreen(screens[m_monitor]); else this->windowHandle()->setScreen(0); this->showFullScreen(); #else QRect rect = QApplication::desktop()->screenGeometry( numeric_cast(getMonitor()) ); move(rect.topLeft()); resize( rect.size() ); this->showFullScreen(); #endif /* Note: The focus should be on the primary window, because at least * Gnome draws the primary window's border onto the secondary. * * I dont mind the border on my helper screen, but the * audience shouldnt see it. */ if ( !informationLineVisible ) this->activateWindow(); // this->resize( 100, 100 ); // this->move(rect.topLeft()); //this->showFullScreen(); } void PDFViewerWindow::displayImage(QImage image) { ui.imageLabel->setText( QString() ); ui.imageLabel->resize( image.size() ); if ( blank ) { // If we're supposed to display a blank image, leave it at this state. return; } currentImage= image; ui.imageLabel->setPixmap(QPixmap::fromImage(image)); //imageArea->setWidgetResizable(true); /* if ( geometry().size() != getTargetImageSize() ) reposition(); */ } void PDFViewerWindow::wheelEvent(QWheelEvent* e) { // QWidget::wheelEvent(e); if ( e->delta() > 0 ) { DEBUGOUT << "Back"; emit previousPageRequested(); } else{ DEBUGOUT << "Next"; emit nextPageRequested(); } } void PDFViewerWindow::keyPressEvent(QKeyEvent* e) { QWidget::keyPressEvent(e); switch( e->key() ) { case Qt::Key_F1: case Qt::Key_Question: // Help keybindingsPopup(); break; case Qt::Key_G: changePageNumberDialog(); break; case Qt::Key_F12: case Qt::Key_S: //Swap emit screenSwapRequested(); break; case Qt::Key_Escape: case Qt::Key_Q: //quit emit quitRequested(); break; case Qt::Key_T: emit secondScreenFunctionToggleRequested(); break; case Qt::Key_D: emit secondScreenDuplicateRequested(); break; case Qt::Key_Space: case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_PageDown: case Qt::Key_Down: case Qt::Key_Right: case Qt::Key_F: // Forward case Qt::Key_N: // Next emit nextPageRequested(); break; case Qt::Key_PageUp: case Qt::Key_Up: case Qt::Key_Left: case Qt::Key_Backspace: case Qt::Key_P: //Previous emit previousPageRequested(); break; case Qt::Key_B: case Qt::Key_Period: emit blankToggleRequested(); break; case Qt::Key_Home: case Qt::Key_H: //Home emit restartRequested(); break; } } QSize PDFViewerWindow::getTargetImageSize() const { return ui.imageArea->geometry().size(); } QSize PDFViewerWindow::getPreviewImageSize() { QSize completeThumbnailArea = ui.thumbnailArea->frameRect().size(); DEBUGOUT << "Space for all thumbnails:" << completeThumbnailArea; /** FIXME Work needed: * since this space must fit three images, we divide horizontal size by three */ QSize thirdThumbnailArea ( completeThumbnailArea.width()/3, completeThumbnailArea.height()); static QSize lastThumbnailSize = thirdThumbnailArea; if ( lastThumbnailSize != thirdThumbnailArea ) { lastThumbnailSize=thirdThumbnailArea; emit rerenderRequested(); } DEBUGOUT << "Space for one thumbnail:" << thirdThumbnailArea; return thirdThumbnailArea; } void PDFViewerWindow::mousePressEvent(QMouseEvent* e) { // QWidget::mousePressEvent(e); if ( e->button() == Qt::LeftButton ) { emit nextPageRequested(); } else if ( e->button() == Qt::RightButton ) { emit previousPageRequested(); } // Ignore other buttons. } void PDFViewerWindow::hideInformationLine() { if ( ! m_enabled ) return; informationLineVisible=false; this->ui.bottomArea->hide(); } bool PDFViewerWindow::isInformationLineVisible() const { return informationLineVisible; } void PDFViewerWindow::showInformationLine() { if ( ! m_enabled ) return; informationLineVisible=true; this->ui.bottomArea->show(); } void PDFViewerWindow::addThumbnail(uint pageNumber, QImage thumbnail) { if ( pageNumber == currentPageNumber-1) ui.previousThumbnail->setPixmap(QPixmap::fromImage(thumbnail)); else if ( pageNumber == currentPageNumber ) ui.currentThumbnail -> setPixmap(QPixmap::fromImage(thumbnail)); else if ( pageNumber == currentPageNumber+1 ) ui.nextThumbnail->setPixmap(QPixmap::fromImage(thumbnail)); } void PDFViewerWindow::renderedPageIncoming(QSharedPointer< RenderedPage > renderedPage) { if ( ! m_enabled ) return; // If we're blank, don't do anything with incoming renders. // Un-blanking will request a rerender. if ( blank ) return; // It might be a thumbnail. If we're waiting for one, check if it would fit. if ( isInformationLineVisible() && renderedPage->getPart() == runtimeConfiguration.thumbnailPagePart() && renderedPage->getIdentifier().requestedPageSize() == this->getPreviewImageSize() ) { this->addThumbnail(renderedPage->getPageNumber(), renderedPage->getImage()); } // If we are not waiting for an image, ignore incoming answers. if ( correctImageRendered ) return; if ( renderedPage->getPageNumber() != this->currentPageNumber ) return; // This page is not for us. Ignore it. if ( renderedPage->getPart() != this->myPart ) return; // This is not our part // There is an image incoming that might fit. displayImage(renderedPage->getImage()); // It was even the right size! Yeah! if ( renderedPage->getIdentifier().requestedPageSize() == getTargetImageSize() ) { if ( this->runtimeConfiguration.hyperlinkSupport() ) { this->parseLinks(renderedPage->getLinks()); } this->correctImageRendered= true; } } void PDFViewerWindow::showLoadingScreen(uint pageNumberToWaitFor) { if ( !m_enabled ) return; // If we're blanked, don't render anything. if ( blank ) return; /// FIXME Loading image this->currentPageNumber = pageNumberToWaitFor; this->correctImageRendered = false; this->currentImage = QImage(); ui.imageLabel->setPixmap(QPixmap()); ui.imageLabel->setText(tr("Loading page number %1").arg(pageNumberToWaitFor) ); /** Clear Thumbnails, they will come back in soon */ ui.previousThumbnail->setPixmap( QPixmap() ); ui.currentThumbnail->setPixmap( QPixmap() ); ui.nextThumbnail->setPixmap( QPixmap() ); } PagePart PDFViewerWindow::getMyPagePart() const { return myPart; } void PDFViewerWindow::resizeEvent(QResizeEvent* resizeEvent) { if ( !m_enabled ) return; QWidget::resizeEvent(resizeEvent); DEBUGOUT << "Resize event" << resizeEvent; DEBUGOUT << "Resized from" << resizeEvent->oldSize() << "to" << resizeEvent->size() << ", requesting re-render."; static bool i3shellcode_executed = false; if ( windowRole == WindowRole::AudienceWindow && runtimeConfiguration.i3workaround() && resizeEvent->spontaneous() && // i3 generates a spontaneous resize. ! i3shellcode_executed // Make sure to do this only once ) { QApplication::flush(); // Make sure the window has been painted // This is the second screen. It has now been created. // so we should call the i3 shellcode now const std::string shellcode = runtimeConfiguration.i3workaround_shellcode(); DEBUGOUT << "Running i3 workaround shellcode" << shellcode.c_str(); int rc = std::system( shellcode.c_str() ); DEBUGOUT << "Return code of i3-workaround was" << rc ; i3shellcode_executed=true; } emit rerenderRequested(); } QString PDFViewerWindow::timeToString(const QTime & time) const { return time.toString( tr("HH:mm:ss", "This is used by QTime::toString. See its documentation before changing this.") ); } QString PDFViewerWindow::timeToString(int milliseconds) const { return timeToString(QTime(0,0).addMSecs(milliseconds)); } void PDFViewerWindow::updatePresentationClock(const QTime& presentationClock) { ui.presentationClock->setText( QCoreApplication::translate("Form", "Total\n%1").arg(timeToString(presentationClock))); } void PDFViewerWindow::updateSlideClock(const QTime& slideClock) { ui.slideClock->setText(timeToString(slideClock) ); } void PDFViewerWindow::updateWallClock(const QTime& wallClock) { ui.wallClock->setText(timeToString(wallClock)); } void PDFViewerWindow::keybindingsPopup() { Ui::KeybindingsDialog keybindUi; QDialog popup; keybindUi.setupUi(&popup); keybindUi.label_versionstring->setText( keybindUi.label_versionstring->text().arg( QString::fromUtf8(DSPDFVIEWER_VERSION ) ) ); popup.exec(); } void PDFViewerWindow::changePageNumberDialog() { bool ok; /* While PDF counts zero-based, users probably think that the first * page is called "1". */ uint displayMinNumber = minimumPageNumber+1; uint displayMaxNumber = maximumPageNumber+1; uint displayCurNumber = currentPageNumber+1; int targetPageNumber = QInputDialog::getInt(this, /* Window Caption */ tr("Select page"), /* Input field caption */ tr("Jump to page number (%1-%2):").arg(displayMinNumber).arg(displayMaxNumber), /* Starting number. */ numeric_cast(displayCurNumber), /* minimum value */ numeric_cast(displayMinNumber), /* maximum value */ numeric_cast(displayMaxNumber), /* Step */ 1, /* Did the user accept? */ &ok); targetPageNumber-=1; // Convert back to zero-based numbering scheme if ( ok ) { emit pageRequested(numeric_cast(targetPageNumber)); } } void PDFViewerWindow::setPageNumberLimits(uint minPageNumber, uint maxPageNumber) { this->minimumPageNumber = minPageNumber; this->maximumPageNumber = maxPageNumber; } void PDFViewerWindow::setBlank(const bool newBlank) { if ( this->blank == newBlank) return; /* State changes. request re-render */ this->blank = newBlank; DEBUGOUT << "Changing blank state to" << blank; if ( blank ) { ui.imageLabel->clear(); } else { emit rerenderRequested(); } } bool PDFViewerWindow::isBlank() const { return blank; } void PDFViewerWindow::setMyPagePart(const PagePart& newPagePart) { this->myPart = newPagePart; } void PDFViewerWindow::parseLinks(QList< AdjustedLink > links) { QList< HyperlinkArea* > newLinkAreas; for( AdjustedLink const & link: links ) { const QRectF& rect = link.linkArea(); if ( rect.isNull() ) { WARNINGOUT << "Null Link Area not supported yet."; continue; } const Poppler::Link::LinkType& type = link.link()->linkType(); if ( type == Poppler::Link::LinkType::Goto ) { // type is Goto. Bind it to imageLabel const Poppler::LinkGoto& linkGoto = dynamic_cast( * link.link() ); if( linkGoto.isExternal() ) { WARNINGOUT << "External links are not supported yet."; continue; } HyperlinkArea* linkArea = new HyperlinkArea(ui.imageLabel, link); sconnect( linkArea, SIGNAL(gotoPageRequested(uint)), this, SLOT(linkClicked(uint)) ); newLinkAreas.append(linkArea); } else { WARNINGOUT << "Types other than Goto are not supported yet."; continue; } } // Schedule all old links for deletion for( HyperlinkArea* hla: this->linkAreas) hla->deleteLater(); // Add the new list this->linkAreas = newLinkAreas; } void PDFViewerWindow::linkClicked(uint targetNumber) { DEBUGOUT << "Hyperlink detected"; emit pageRequested(targetNumber); } dspdfviewer-1.15/pdfviewerwindow.h000066400000000000000000000075611270070303100173600ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PDFVIEWERWINDOW_H #define PDFVIEWERWINDOW_H #include "renderedpage.h" #include "pdfrenderfactory.h" #include "runtimeconfiguration.h" #include "hyperlinkarea.h" #include "windowrole.h" #include "ui_pdfviewerwindow.h" /** Shared base class for both windows (primary and secondary) * */ class PDFViewerWindow : public QWidget { Q_OBJECT private: Ui::Form ui; bool m_enabled; unsigned int m_monitor; QImage currentImage; bool blank; bool informationLineVisible; uint currentPageNumber; uint minimumPageNumber; uint maximumPageNumber; bool correctImageRendered; PagePart myPart; const WindowRole windowRole; // Reference to the runtime configuration object. const RuntimeConfiguration& runtimeConfiguration; /** Display this image */ void displayImage(QImage image); virtual void wheelEvent(QWheelEvent* e); virtual void keyPressEvent(QKeyEvent* e); virtual void mousePressEvent(QMouseEvent* e); void addThumbnail(uint pageNumber, QImage thumbnail); QString timeToString(const QTime& time) const; QString timeToString(int milliseconds) const; void keybindingsPopup(); void changePageNumberDialog(); void parseLinks( QList< AdjustedLink > links); QList< HyperlinkArea* > linkAreas; public: /** Standard constructor * @param monitor monitor to start on (usually 0 for primary) */ explicit PDFViewerWindow(unsigned int monitor, PagePart myPart, bool showInformationLine, const RuntimeConfiguration& r, const WindowRole& windowRole, bool enabled=true); /** Sets the monitor to display this window on * Automatically calls reposition */ void setMonitor(const unsigned int monitor); /** Gets the current monitor setting */ unsigned int getMonitor() const; /** Reposition the window (for example after a monitor change) */ void reposition(); QSize getTargetImageSize() const; QSize getPreviewImageSize(); PagePart getMyPagePart() const; void showInformationLine(); void hideInformationLine(); bool isInformationLineVisible() const; bool isBlank() const; void showLoadingScreen(uint pageNumberToWaitFor); public slots: void renderedPageIncoming( QSharedPointer renderedPage); void resizeEvent(QResizeEvent* resizeEvent); void updateWallClock(const QTime& wallClock); void updateSlideClock(const QTime& slideClock); void updatePresentationClock(const QTime& presentationClock); void setPageNumberLimits(uint minimumPageNumber, uint maximumPageNumber); void setBlank(const bool blank); void setMyPagePart(const PagePart& newPagePart); signals: void nextPageRequested(); void previousPageRequested(); void pageRequested(unsigned requestedPageNumber); void restartRequested(); void screenSwapRequested(); void rerenderRequested(); void quitRequested(); void secondScreenFunctionToggleRequested(); void secondScreenDuplicateRequested(); void blankToggleRequested(); private slots: void linkClicked(uint targetNumber); }; #endif // PDFVIEWERWINDOW_H dspdfviewer-1.15/pdfviewerwindow.ui000066400000000000000000000166631270070303100175510ustar00rootroot00000000000000 Form 0 0 1216 808 DS PDF Viewer #Form, #thumbnailAreaWidget, #bottomArea QLabel, #imageArea, #imageLabel { background-color: black; color: white; } 0 0 Qt::NoFocus QFrame::NoFrame 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff false Qt::AlignCenter 508 311 200 25 0 0 The actual image goes here. false Qt::AlignCenter Qt::NoTextInteraction 0 0 16777215 16777215 0 0 0 25 09:12:34 Qt::AlignCenter 16777215 16777215 Qt::NoFocus QFrame::NoFrame 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff true 0 0 829 162 0 0 Next Page Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Current Page Qt::AlignCenter Previous Page Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 0 25 Total 00:00:00 Qt::AlignCenter 0 0 50 00:00 Qt::AlignCenter thumbnailArea presentationClock wallClock slideClock dspdfviewer-1.15/poppler-qt.h000066400000000000000000000004421270070303100162270ustar00rootroot00000000000000#ifndef POPPLER_QT5 #include #else #include #if defined( WINDOWS_STATIC_LINK ) && ! defined( qtplugin ) #define qtplugin #if defined(WIN32) || defined(WIN64) #include Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) #endif #endif #endif dspdfviewer-1.15/renderedpage.cpp000066400000000000000000000032301270070303100171020ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "renderedpage.h" #include "renderingidentifier.h" RenderingIdentifier RenderedPage::getIdentifier() const { return theIdentifier; } QImage RenderedPage::getImage() const { return theRenderedImage; } QList< AdjustedLink > RenderedPage::getLinks() const { return theLinks; } unsigned RenderedPage::getPageNumber() const { return getIdentifier().pageNumber(); } PagePart RenderedPage::getPart() const { return getIdentifier().pagePart(); } RenderedPage::RenderedPage(QImage img, QList< AdjustedLink > links, PagePart whichPart, unsigned pageNum) : theRenderedImage(img), theLinks(links), theIdentifier(pageNum, whichPart, img.size()) { } RenderedPage::RenderedPage(QImage img, QList< AdjustedLink > links, RenderingIdentifier identifier) : theRenderedImage(img), theLinks(links), theIdentifier(identifier) { } dspdfviewer-1.15/renderedpage.h000066400000000000000000000030631270070303100165530ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RENDEREDPAGE_H #define RENDEREDPAGE_H #include #include "poppler-qt.h" #include "pagepart.h" #include "renderingidentifier.h" #include "adjustedlink.h" class RenderedPage { private: QImage theRenderedImage; QList< AdjustedLink > theLinks; RenderingIdentifier theIdentifier; public: RenderedPage(QImage img, QList links, PagePart whichPart, unsigned pageNum); RenderedPage(QImage img, QList links, RenderingIdentifier identifier); QImage getImage() const; QList< AdjustedLink > getLinks() const; PagePart getPart() const; uint getPageNumber() const; RenderingIdentifier getIdentifier() const; }; Q_DECLARE_METATYPE(QSharedPointer) #endif // RENDEREDPAGE_H dspdfviewer-1.15/renderingidentifier.cpp000066400000000000000000000052201270070303100204760ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "renderingidentifier.h" #include #include #include #if defined ( POPPLER_QT5 ) #include #else #include #endif bool RenderingIdentifier::operator==(const RenderingIdentifier& other) const { return thePageNumber == other.thePageNumber && thePagePart == other.thePagePart && theRequestedPageSize == other.theRequestedPageSize; } #if 0 RenderingIdentifier::operator QString() const { QString s( "page%1_%2_size%3x%4" ) ; QString partId; switch( pagePart() ) { case PagePart::LeftHalf: partId= "LeftHalf"; break; case PagePart::RightHalf: partId="RightHalf"; break; case PagePart::FullPage: partId="FullPage"; break; } return s.arg(pageNumber()).arg(partId).arg(requestedPageSize().width()).arg(requestedPageSize().height()); } #endif unsigned RenderingIdentifier::pageNumber() const { return thePageNumber; } PagePart RenderingIdentifier::pagePart() const { return thePagePart; } QSize RenderingIdentifier::requestedPageSize() const { return theRequestedPageSize; } RenderingIdentifier::RenderingIdentifier(unsigned pagenum, PagePart pagepart, QSize pagesize): thePageNumber(pagenum), thePagePart(pagepart), theRequestedPageSize(pagesize), theVersion(1) { } uint qHash(const RenderingIdentifier& ri) { return qHash(ri.pageNumber()) ^ qHash(ri.requestedPageSize().height()) ^ qHash(ri.requestedPageSize().width()); } QDebug operator << (QDebug d, const RenderingIdentifier& ri) { #if defined ( POPPLER_QT5 ) // QDebugStateSaver only exists in Qt5.1 QDebugStateSaver s(d); #endif std::ostringstream oss; oss << ri.pagePart(); d.nospace() << "[page " << ri.pageNumber() << '/' << oss.str().c_str() << '/' << ri.requestedPageSize().width() << 'x' << ri.requestedPageSize().height() << ']'; return d; } dspdfviewer-1.15/renderingidentifier.h000066400000000000000000000034111270070303100201430ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RENDERINGIDENTIFIER_H #define RENDERINGIDENTIFIER_H #include "pagepart.h" #include #include #include class RenderingIdentifier { private: unsigned thePageNumber; PagePart thePagePart; QSize theRequestedPageSize; public: /* * * This will be set and checked by PDFRenderFactory */ quint64 theVersion; RenderingIdentifier(unsigned pagenum, PagePart pagepart, QSize requestedPageSize); unsigned pageNumber() const; PagePart pagePart() const; QSize requestedPageSize() const; bool operator == (const RenderingIdentifier& other) const; #if 0 /** Cast to a string that is usable as a hash identifier **/ operator QString() const; #endif }; /** Hashes this object to something useable in the cache */ uint qHash(const RenderingIdentifier& ri); /** allow outputting a rendering identifier */ QDebug operator << ( QDebug, const RenderingIdentifier&); #endif // RENDERINGIDENTIFIER_H dspdfviewer-1.15/renderthread.cpp000066400000000000000000000041141270070303100171260ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "renderthread.h" #include "renderutils.h" #include "adjustedlink.h" #include "debug.h" RenderThread::RenderThread(const PDFDocumentReference& theDocument, const RenderingIdentifier& renderIdent): QObject(), QRunnable(), m_page( theDocument.page( renderIdent.pageNumber() ) ), renderMe(renderIdent) { } void RenderThread::run() { DEBUGOUT << "RenderThread for" << renderMe << "started"; QImage renderImage = RenderUtils::renderPagePart(m_page.page, renderMe.requestedPageSize(), renderMe.pagePart()); if ( renderImage.isNull() ) { WARNINGOUT << "RenderThread for" << renderMe << "failed"; QSharedPointer ri( new RenderingIdentifier(renderMe) ); emit renderingFailed(ri); return; } QList< AdjustedLink > links; for( Poppler::Link* link: m_page.page->links() ) { QSharedPointer ptrLink(link); try{ AdjustedLink al(renderMe, ptrLink); links.append(al); } catch( AdjustedLink::OutsidePage &) { // no-op } } QSharedPointer renderResult(new RenderedPage( renderImage, links, renderMe )); DEBUGOUT << "RenderThread for" << renderMe << "successful, image has size" << renderResult->getImage().size(); emit renderingFinished(renderResult); } dspdfviewer-1.15/renderthread.h000066400000000000000000000026371270070303100166030ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RENDERTHREAD_H #define RENDERTHREAD_H #include #include #include "renderedpage.h" #include "pdfpagereference.h" class RenderThread: public QObject, public QRunnable { Q_OBJECT private: const PDFPageReference m_page; RenderingIdentifier renderMe; public: RenderThread( const PDFDocumentReference& theDocument, const RenderingIdentifier& renderIdent); void run(); signals: void renderingFinished(QSharedPointer result); void renderingFailed(QSharedPointer requestedRender); }; #endif // RENDERTHREAD_H dspdfviewer-1.15/renderutils.cpp000066400000000000000000000054751270070303100170320ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "renderutils.h" #include "debug.h" #include #include #include using boost::math::iround; namespace { bool shuttingDown = false; } QImage RenderUtils::renderPagePart(QSharedPointer< const Poppler::Page > page, QSize targetSize, PagePart whichPart) { if ( ! page ) { throw std::runtime_error( std::string( qPrintable( QApplication::translate("RenderUtils", "RenderUtils::renderPagePart called with null page. Target size was %1x%2"). arg(targetSize.width()).arg(targetSize.height()) ) ) ); } if ( shuttingDown ) return QImage(); /* pagesize in points, (72 points is an inch) */ QSizeF pagesize = page->pageSizeF(); QSizeF fullsize = pagesize; if ( whichPart != PagePart::FullPage ) { /* Only render half the page */ pagesize.setWidth(pagesize.width()/2); } /* Calculate DPI for displaying on size */ /* the 72 comes from converting from points to inches */ double dpiWidth = 72.0 * targetSize.width() / pagesize.width(); double dpiHeight = 72.0 * targetSize.height() / pagesize.height(); /* Take the smaller one, so that the image surely fits on target area */ double dpi = std::min(dpiWidth, dpiHeight); /* Calculate Page Size in pixels */ QSize fullSizePixels( iround(dpi * fullsize.width() / 72.0), iround(dpi * fullsize.height() / 72.0) ); /* Calculate rendered image size */ QSize imageSizePixels( iround(dpi * pagesize.width() / 72.0), iround( dpi * pagesize.height() / 72.0 ) ); /* Calculate x-offset */ int x = 0; if ( whichPart == PagePart::RightHalf ) { /* start at an offset of width() pixels to the right, rounding up */ x = (fullSizePixels.width()+1)/2; } /* render it */ QImage renderedImage = page->renderToImage( dpi, dpi, x, /* x-offset */ 0, /* y-offset */ imageSizePixels.width(), imageSizePixels.height() ); return renderedImage; } void RenderUtils::notifyShutdown(void) { shuttingDown = true; } dspdfviewer-1.15/renderutils.h000066400000000000000000000026231270070303100164670ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RENDERUTILS_H #define RENDERUTILS_H #include #include "pagepart.h" #include #include "poppler-qt.h" class RenderUtils { public: static QImage renderPagePart(QSharedPointer page, QSize targetSize, PagePart whichPart); /** Since only the static functions of this class are used, we do not need to construct instances */ RenderUtils() =delete; /** Notifies about impending shutdown. * * All further render attempts will return null images. */ static void notifyShutdown(void); }; #endif // RENDERUTILS_H dspdfviewer-1.15/runtimeconfiguration.cpp000066400000000000000000000265721270070303100207460ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "runtimeconfiguration.h" #include #include #include #include #ifndef DSPDFVIEWER_VERSION #warning DSPDFVIEWER_VERSION was not set by the build system! #define DSPDFVIEWER_VERSION "UNKNOWN" #endif #ifndef I3WORKAROUND_SHELLCODE /* This will get executed once both windows are created, provided * the user passed --i3-workaround=true via command line or configuration file. * * You can override the shellcode by providing -DI3WORKAROUND_SHELLCODE="your shellcode" * at the cmake step. */ #define I3WORKAROUND_SHELLCODE "i3-msg '[class=\"Dspdfviewer\" window_role=\"Audience_Window\"] move to output right, fullscreen'" #endif using namespace std; using namespace boost::program_options; RuntimeConfiguration::RuntimeConfiguration(int argc, const char* const * argv): m_useFullPage(false), m_showPresenterArea(true), m_duplicate(false), m_showWallClock(true), m_showThumbnails(true), m_thumbnailPagePart(PagePart::FullPage), m_showPresentationClock(true), m_showSlideClock(true), m_filePath(), m_hyperlinkSupport(true), m_cacheToMemory(true), m_cacheSizeMegaBytes(0), m_useSecondScreen(true), m_i3workaround(false), m_prerenderPreviousPages(3), m_prerenderNextPages(10), m_bottomPaneHeightPercent(20) { options_description generic( tr("Generic options").toLocal8Bit().constData() ); /** FIXME: Using .toLocal8Bit() [.constData()] is Very Very Very Ugly, * but works because program_options copies the string... */ generic.add_options() ("help,h", tr("Print help message").toLocal8Bit() ) ("version,v", tr("Print version statement").toLocal8Bit() ) ; options_description global( tr("Options affecting program behaviour") .toLocal8Bit().constData() ); global.add_options() ("full-page,f", //value(&m_useFullPage)->default_value(false)->implicit_value(true), tr("Display the full slide on both screens (useful for PDFs created by presentation software other than latex-beamer)").toLocal8Bit() ) ("prerender-previous-pages", value(&m_prerenderPreviousPages)->default_value(3), tr("Pre-render the preceding arg slides\n" "NOTE: If you set this to zero, you might not get a thumbnail for the previous slide unless it was loaded already." ).toLocal8Bit() ) ("prerender-next-pages", value(&m_prerenderNextPages)->default_value(10), tr( "Pre-render the next arg slides\n" "NOTE: If you set this to zero, you might not get a thumbnail for the next slide unless it was loaded already." ).toLocal8Bit() ) ("hyperlink-support,l", value(&m_hyperlinkSupport)->default_value(true), tr( "Support PDF Hyperlinks\n" "Follow hyperlinks when clicked (mouse pointer will change to a pointing hand) - set this to false if " "you cannot reliably control your mouse pointer position and want to always go ahead one slide on click.").toLocal8Bit() ) ("cache-to-memory", value(&m_cacheToMemory)->default_value(true), tr( "Cache the PDF file into memory\n" "Useful if you are editing the PDF file with latex while using the presenter software.").toLocal8Bit() ) ("cache-size", value(&m_cacheSizeMegaBytes)->default_value(1024), tr( "Size of the cache for pre-rendered pages, in megabytes." ).toLocal8Bit() ) ("i3-workaround", value(&m_i3workaround)->default_value(false), tr( "Use i3 specific workaround: Execute shellcode once both windows have been created.") #ifndef NDEBUG .append( QString::fromUtf8( "\nDebug info: Shellcode is \n" I3WORKAROUND_SHELLCODE ) ) #endif .toLocal8Bit() ) ; options_description secondscreen( tr("Options affecting the second screen").toLocal8Bit().constData() ); secondscreen.add_options() ("use-second-screen,u", value(&m_useSecondScreen)->default_value(true), tr( "Use the second screen. If you only have one monitor and just want to use this application as a fast, pre-caching PDF viewer" " you might want to say 0 here.\n" "NOTE: Whatever you say on -a, -t, -w, -s or -p doesn't matter if you set this to false.\n" "NOTE: You might want to say -f if you set this to false." ).toLocal8Bit() ) ("presenter-area,a", value(&m_showPresenterArea)->default_value(true), tr( "Shows or hides the complete \"presenter area\" on the second screen, giving you a full-screen note page.\n" "NOTE: Whatever you say on -t, -w, -s or -p doesnt matter if you set this to false." ).toLocal8Bit() ) ("duplicate,d", value(&m_duplicate)->default_value(false), tr("Duplicates the audience's screen next to the notes on the second screen.").toLocal8Bit() ) ("thumbnails,t", value(&m_showThumbnails)->default_value(true), tr( "Show thumbnails of previous, current and next slide").toLocal8Bit() ) ("thumbnail-page-part,T", value(&m_thumbnailPagePart)->default_value(PagePart::FullPage), tr("Thumbnails show this page part. Valid values are \"left\", \"right\" or \"both\"").toLocal8Bit() ) ("wall-clock,w", value(&m_showWallClock)->default_value(true), tr( "Show the wall clock").toLocal8Bit() ) ("presentation-clock,p", value(&m_showPresentationClock)->default_value(true), tr( "Show the presentation clock").toLocal8Bit() ) ("slide-clock,s", value(&m_showSlideClock)->default_value(true), tr("Show the slide clock").toLocal8Bit() ) ("bottom-pane-height,b", value(&m_bottomPaneHeightPercent)->default_value(20), tr("Percentage of second screen to use for the bottom pane").toLocal8Bit() ) ; options_description hidden(tr("Hidden options").toLocal8Bit().constData()); hidden.add_options() ("pdf-file", value< string >(&m_filePath), tr("PDF File to display").toLocal8Bit()) ; positional_options_description p; p.add("pdf-file", 1); options_description help; help.add(generic).add(global).add(secondscreen); options_description commandLineOptions; commandLineOptions.add(help).add(hidden); options_description configFileOptions; configFileOptions.add(global).add(secondscreen); variables_map vm; store( command_line_parser(argc,argv).options(commandLineOptions).positional(p).run(), vm); QString configurationFileLocation = QString::fromUtf8( qgetenv("HOME").append("/.config/dspdfviewer.ini") ); { // See if the configuration file exists and is readable std::ifstream cfile( qPrintable(configurationFileLocation) ); if ( cfile.good() ) { store( parse_config_file( cfile, configFileOptions), vm); } } // close input file notify(vm); if ( vm.count("version") || vm.count("help") ) { cout << "dspdfviewer version " << DSPDFVIEWER_VERSION << endl; cout << tr( "Written by Danny Edel\n" "\n" "Copyright (C) 2012 Danny Edel.\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.").toLocal8Bit().constData() << endl; if ( vm.count("help")) { cout << endl; cout << tr("Usage: %1 [options] pdf-file").arg( QString::fromUtf8(argv[0]) ).toLocal8Bit().constData() << endl; cout << help << endl; /*: Please try to keep line length below 70 chars and use \t (tab) for padding */ cout << tr("Interactive Controls:\n" "\tPress F1 or ? during program execution to get a quick\n" "\toverview about available controls.\n" "\tPlease read the manpage (man dspdfviewer) for the full list.").toLocal8Bit().constData() << endl; } exit(0); } if ( m_bottomPaneHeightPercent < 1 || m_bottomPaneHeightPercent > 99 ) { throw std::runtime_error( tr("Invalid height in specified. Please use a value from 1 to 99 (inclusive)").toLocal8Bit().constData() ); } m_useFullPage = ( 0 < vm.count("full-page") ); /** Implied options */ if ( ! m_useSecondScreen ) { /* If we dont use a second screen, there's no point in using the presenter area */ m_showPresenterArea = false; } if ( ! m_showPresenterArea ) { /* If the presenter area is hidden, disable all clocks and the thumbnails */ m_showPresentationClock = false; m_showWallClock = false; m_showSlideClock = false; /* This option will effectively disable rendering of thumbnails */ m_showThumbnails = false; } } string RuntimeConfiguration::filePath() const { if ( m_filePath.empty() ) { throw noFileNameException(); } return m_filePath; } QString RuntimeConfiguration::filePathQString() const { return QString::fromLocal8Bit( filePath().c_str() ); } bool RuntimeConfiguration::useFullPage() const { return m_useFullPage; } bool RuntimeConfiguration::showPresentationClock() const { return m_showPresentationClock; } bool RuntimeConfiguration::showPresenterArea() const { return m_showPresenterArea; } bool RuntimeConfiguration::duplicate() const { return m_duplicate; } bool RuntimeConfiguration::showSlideClock() const { return m_showSlideClock; } bool RuntimeConfiguration::showThumbnails() const { return m_showThumbnails; } bool RuntimeConfiguration::showWallClock() const { return m_showWallClock; } unsigned int RuntimeConfiguration::prerenderNextPages() const { return m_prerenderNextPages; } unsigned int RuntimeConfiguration::prerenderPreviousPages() const { return m_prerenderPreviousPages; } bool RuntimeConfiguration::useSecondScreen() const { return m_useSecondScreen; } PDFCacheOption RuntimeConfiguration::cacheSetting() const { return m_cacheToMemory? PDFCacheOption::keepPDFinMemory : PDFCacheOption::rereadFromDisk; } unsigned int RuntimeConfiguration::bottomPaneHeight() const { return m_bottomPaneHeightPercent; } bool RuntimeConfiguration::hyperlinkSupport() const { return m_hyperlinkSupport; } void RuntimeConfiguration::filePath(const std::string& newPath ) { m_filePath = newPath; } bool RuntimeConfiguration::filePathDefined() const { return ! m_filePath.empty(); } noFileNameException::noFileNameException(): logic_error( QCoreApplication::translate("DSPDFViewer", "You did not specify a PDF-File to display.").toLocal8Bit().constData() ) { } bool RuntimeConfiguration::i3workaround() const { return m_i3workaround; } std::string RuntimeConfiguration::i3workaround_shellcode() const { return std::string( I3WORKAROUND_SHELLCODE ); } PagePart RuntimeConfiguration::thumbnailPagePart() const { return m_thumbnailPagePart; } unsigned RuntimeConfiguration::cacheSizeBytes() const { return cacheSizeMegaBytes()*1024*1024; } unsigned RuntimeConfiguration::cacheSizeMegaBytes() const{ return m_cacheSizeMegaBytes; } dspdfviewer-1.15/runtimeconfiguration.h000066400000000000000000000102451270070303100204010ustar00rootroot00000000000000/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RUNTIMECONFIGURATION_H #define RUNTIMECONFIGURATION_H #include #include #include #include #include "pagepart.h" #include "pdfcacheoption.h" struct noFileNameException: public std::logic_error { noFileNameException(); }; class RuntimeConfiguration: public QObject { Q_OBJECT /** Use the program to render a standard PDF (i.e. display * the full page on both sides) */ bool m_useFullPage; /** Show presenter area */ bool m_showPresenterArea; /** Show a full second screen */ bool m_duplicate; /** Show the wall clock */ bool m_showWallClock; /** Show the thumbnails of previous, this and next slide */ bool m_showThumbnails; /** Select page part of the thumbnails */ PagePart m_thumbnailPagePart; /** Show the total presentation time **/ bool m_showPresentationClock; /** Show the current slide time **/ bool m_showSlideClock; /** complete path to the PDF file */ std::string m_filePath; /** Support PDF Hyperlinks**/ bool m_hyperlinkSupport; /** Shall the complete PDF be read into memory */ bool m_cacheToMemory; /** Size of the pre-render-cache in megabytes */ unsigned m_cacheSizeMegaBytes; /** Single-Display mode * * If True, there is only the audience display, the presenter's screen will remain hidden * Probably most useful with -f */ bool m_useSecondScreen; /** Workaround for i3 window manager active */ bool m_i3workaround; /** Make sure that so many previous pages are pre-rendered * (Probably wont make sense until you can jump to slide * n without visiting 0..(n-1) first, but once PDF hyperlinks * are enabled, this will be quite useful. */ unsigned m_prerenderPreviousPages; /** Make sure so many next pages are pre-rendered */ unsigned m_prerenderNextPages; /** * Percentage of the second screen devoted to the bottom pane */ unsigned m_bottomPaneHeightPercent; public: /** fill the variables based on the config file and the C-style arguments to main() * * Note: Reads the config file before command-line arguments. * Command-line overrides config. * * Note: Might call exit() if a terminating option like --help * or --version was called. * * Note: Might throw exceptions if not parsable. */ RuntimeConfiguration(int argc, const char * const * argv); bool useFullPage() const; bool filePathDefined() const; QString filePathQString() const; std::string filePath() const; void filePath(const std::string& newPath); bool showPresenterArea() const; bool duplicate() const; bool showWallClock() const; bool showThumbnails() const; PagePart thumbnailPagePart() const; bool showPresentationClock() const; bool showSlideClock() const; unsigned prerenderPreviousPages() const; unsigned prerenderNextPages() const; unsigned cacheSizeMegaBytes() const; unsigned cacheSizeBytes() const; bool useSecondScreen() const; PDFCacheOption cacheSetting() const; unsigned bottomPaneHeight() const; bool hyperlinkSupport() const; /* Use i3-workaround. * * In the future, this might auto-detect if we're running on i3. */ bool i3workaround() const; /* What shellcode to execute. * * This may be programatically generated in the future. */ std::string i3workaround_shellcode() const; }; #endif // RUNTIMECONFIGURATION_H dspdfviewer-1.15/sconnect.h000066400000000000000000000010021270070303100157310ustar00rootroot00000000000000#include /** secure connect: * * Quick and dirty hack to check QObject::connect()'s return value */ template void sconnect(Sender sender, Signal signal, Receiver receiver, Slot slot, Qt::ConnectionType type=Qt::AutoConnection) { bool okay = QObject::connect( sender, signal, receiver, slot, type); if ( ! okay ) { throw std::runtime_error( std::string("QObject::connect failed. sender: ")+signal+" receiver: "+slot); } } dspdfviewer-1.15/testing/000077500000000000000000000000001270070303100154305ustar00rootroot00000000000000dspdfviewer-1.15/testing/CMakeLists.txt000066400000000000000000000103051270070303100201670ustar00rootroot00000000000000# Test driver library for convenience linking. add_library(testhelp testhelpers ) # Link this helper library to dspdfviewer's functions target_link_libraries(testhelp libdspdfviewer ${LIST_TEST_LIBRARIES} ) list(APPEND PDFFILENAMES colored-rectangles.pdf images.pdf many-many-pages.pdf ) if(DownloadTestPDF) message(WARNING "DownloadTestPDF option has been deprectated, since " "prerendered PDFs are now included in the source. Use the option -DUsePrerenderedPDF=ON from now on.") set(UsePrerenderedPDF ON) endif() if(UsePrerenderedPDF) foreach(pdffile IN LISTS PDFFILENAMES) file( COPY "pdfs/${pdffile}" DESTINATION . ) message(STATUS "Using pre-rendered ${pdffile}") endforeach() else() # Compile from source find_program(PDFLATEX NAMES pdflatex ) if( PDFLATEX STREQUAL "PDFLATEX-NOTFOUND") message(FATAL_ERROR "Compiling PDFs from source requires pdflatex with latex-beamer installed") endif() foreach(pdffile IN LISTS PDFFILENAMES) # get source file name. # replace .pdf with .tex ending, # prefix source directory # and replace any ~ characters in filename with \\string~ STRING(REGEX REPLACE "\\.pdf$" ".tex" SOURCEFILENAME ${pdffile}) SET(SOURCEDIRNAME "${CMAKE_CURRENT_SOURCE_DIR}/pdfs") SET(SOURCEFULLPATH "${SOURCEDIRNAME}/${SOURCEFILENAME}") STRING(REPLACE "~" "\\\\string~" PDFLATEX_FIXED_DIRNAME ${SOURCEDIRNAME}) SET(PDFLATEX_FIXED_FILENAME "${PDFLATEX_FIXED_DIRNAME}/${SOURCEFILENAME}") add_custom_command(OUTPUT ${pdffile} DEPENDS ${SOURCEFULLPATH} COMMAND # env TEXINPUTS=${SOURCEDIRNAME} ${PDFLATEX} -interaction=nonstopmode -output-directory ${CMAKE_CURRENT_BINARY_DIR} ${PDFLATEX_FIXED_FILENAME} WORKING_DIRECTORY ${SOURCEDIRNAME} ) message(STATUS "Building ${pdffile} from source.") endforeach() endif() foreach(pdffile IN LISTS PDFFILENAMES) list(APPEND PDFTARGETFILES ${CMAKE_CURRENT_BINARY_DIR}/${pdffile}) endforeach() add_custom_target(testpdfs DEPENDS ${PDFTARGETFILES} ) add_dependencies(testhelp testpdfs) # Allow the programs headers to be found using #include <...> include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../ ) # Now add actual tests. add_executable(testrunner testrenderonepage.cc test-images.cc testrunner.cc test-thumbnail-cmdline.cc ) target_link_libraries(testrunner testhelp) # Clang/G++: Don't promote warnings to errors when building the test suite. if(CMAKE_COMPILER_IS_GNUCXX) set_target_properties(testrunner PROPERTIES COMPILE_FLAGS "-Wno-error=effc++" ) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) set_target_properties(testrunner PROPERTIES COMPILE_FLAGS "-Wno-error=global-constructors" ) endif() add_test(NAME BoostTestRunner COMMAND testrunner --report_level=detailed ) # Check whether --version and --help contain # the version in their output add_test(NAME TestCommandLineVersion COMMAND dspdfviewer --version ) add_test(NAME TestCommandLineHelp COMMAND dspdfviewer --help ) set_tests_properties( TestCommandLineVersion TestCommandLineHelp PROPERTIES PASS_REGULAR_EXPRESSION ${DSPDFVIEWER_VERSION} ) # Only build and run the swap screen test on Qt5 if(UseQtFive) add_executable(testswapscreen testswapscreen.cc testswapscreen-main.cc ) target_link_libraries(testswapscreen testhelp ) if(RunDualScreenTests) add_test(NAME testswapscreen COMMAND testswapscreen --prerender-prev=0 --prerender-next=0 images.pdf ) set_tests_properties(testswapscreen PROPERTIES SKIP_RETURN_CODE 77 ) endif() endif() # On windows you cannot change the locale on a case-by-case basis. # # FIXME: Windows: Discover current locale # FIXME: Windows: Include the translation test for the current locale if(NOT WINDOWS) add_test(NAME EnglishByDefault COMMAND dspdfviewer --help ) set(ENVVARS "LC_ALL=C.UTF-8:C" "LANGUAGE=C" ) set_tests_properties(EnglishByDefault PROPERTIES PASS_REGULAR_EXPRESSION "Interactive Controls" ENVIRONMENT "${ENVVARS}" ) # the ENVIRONMENT property was added in 2.8.0 add_test(NAME GermanWithDeDE COMMAND dspdfviewer --help ) set(ENVVARS "LC_ALL=de_DE.UTF-8:de_DE" "LANGUAGE=de" ) set_tests_properties(GermanWithDeDE PROPERTIES PASS_REGULAR_EXPRESSION "Interaktive Tasten" ENVIRONMENT "${ENVVARS}" ) endif() dspdfviewer-1.15/testing/pdfs/000077500000000000000000000000001270070303100163645ustar00rootroot00000000000000dspdfviewer-1.15/testing/pdfs/colored-rectangles.pdf000066400000000000000000000474641270070303100226520ustar00rootroot00000000000000%PDF-1.5 % 35 0 obj << /Length 949 /Filter /FlateDecode >> stream xXKo0 WzYn 촴 ; k"]))q+ڮicY4I(JV@AO_7&w4A۹jWuKp I'zާ^cecOT3Ŋ-ߎ>a.aCXbJP$ *?vCQj %Κ¢AzFC`P^E 3QI$V "ߢ9$a^TATv@sH۝շLbjFٔ > |J_IMq튼b <{^`xM!8X_>KC%iGG_M)D~h+L^D+u &p|2trxn=?;uOzyvϻ3u;ͯvO2dJ?R_b endstream endobj 44 0 obj << /Length1 1607 /Length2 7929 /Length3 0 /Length 8962 /Filter /FlateDecode >> stream xڍTo6tIt/] ,.Kw  %H(%- 4ҭHwx};{s\3<,  !yyA?yA ]( Eↀa0 `M%a/HJ@$&(=6:/A8,po7=vk_\\w8 qZa:iq=`G"]$<==y^47 Ebj;Cnе"rm`7pkpZC`w =QV@`"p7ѿAapg0 lN/ a6`'6:n KJZÿCXA]^W|܎Yf#wv_)@ ַs:0-fc w>=7ք @ 1 q ^|vv2wloۀCm!?8@C}t7lH b'b}nP/t+?~٭l0'b>m-e5[)'|yDa .. F ?Bap@joOK;V?:7 o;O俲_u);9EP'uG.:v`K5ze9;H(B ф"_v_A4读w^֎V]#ap_k& ,8[-  h-cGކp7_TDe@HEj|? g(9ު ަrަBE>?m *[gzn-Pt^!Kɳ29*ި0u2^nQW9KRʾTͩ∑ ?A/ӶW )(]r<Gmvz8ݐhj2^ [uhZjfĚ 1 ceP12E~62%#q3IsU w&Cul|ϫTת"H}T^|"KxR@a>/N@9oqv!~m.Z} \ !{!2_RcY'Ȋ/:f[`C5d;ι"| &nyRB~V"_Q{[j?aQc83N"5XSg]7*/ lgֺEl`3ۻݧ"itx -bŹ^cH4-w.t ֵ>'ԉ.DYyWn=X~mN2mpb3פ]6w?Xn :4ʱQTQr`}*x;rV @B4y]qo6s_Aa/yp^/z Yl=wٕM` H{bկ9m&}lScWyBjASeivOݧ3Wdg'ꀽ]&yP S6&eӸ.\8,?K)XoonyF/p5{~Yw"1a_ ւ\$&nǻ7IԠ4{RTMS b4?Pr3KnOM^{1 "e]71,%]2E@K@5UT!ۖjFF] c0i,IJ|5S3v>K 1;D:a4qUYӸrtAN9yhD!Ь=CzVQN7eZPd,x/) 工xJv?i+k*'Cjc(,ͭ\\6m.mv/:[?, 'm/䈂?Ew តHRTEJ~`F I#)ϒ۳nd1UۼN]⛨X: o$ܼfJm(ĉ1rM( NʖQuOL;)\%-%%dinw^T2L@Jn"̓W6v1G];(e!::2PnYV|S 2m L˲i)zW-ק>#?+ l$9`jΛϳM >&ӭ 1#+Tz%;ը~ ?H'k4C7 ˌ*cזy;Nբ}>YU]O̎/#(&еV0()n|tf4(ǁ(XF blU]2ܝr_Èn/Ha>{_,`,N7,A~Fu` l!G|T@6xD#^'٘3RޙukmP1.' 2( LD#݅oo>G<:3)E289H1}#1_Qwh<9gnCt۹bq&͇[݁M9wC_.ژ.c4Ac`lFsWݩg ubΎswz9(Ʒj|Y`[`ٛݶ2iK*J_r/\D|K.)5-N r3cŢ~!fNuG? cai%|UbȱWIҼ͍hvx]}ܗUwyaLWw8ET\ehKeAz' 7jZQn!E .4;?_ndKR.;M1( >ݠq5 C~F\v%˻ zݗ$ArhɫΆ36qM5CpNHo[ Pv(YߡUQy-vryCcZlj>SCuU!W%N:_ 9)mzV ޒ~4puT,ı,%/мS`0a>AߟTc(QൿC zlW ,씮Y[GauavG5e~h/ѐXhjMw`_Ls109p5̎+J~vkQ7K1ѩԧn%b7Ebuw8#mC >?[!:qѳ9fVUfoI_ MU~y `|[{x"J7_:Sec샧WB6=жزpu+"[1dVBVF n2)I*ixG=T{ 9-l`̴cV))Gq5+TPjuo@:E@!.GJN5ڹs},^gR_ڗSQeZ rpw{holP&XZU -{x)jG?ꏙ 2!eN8wTM3md6:rTKa_:#PKsJ(%Wm 6ϕxwPL Z.-2-pYa KF>*bվRNJ/X_'oPPP3+`LnvDwScp{;" LЩ r_x:q>a 1|K0煢Lh"96xJJPz x Վ2 s6{ {G?  bUcw" q9Ueώ IH#xjwP~m_*,A|o_G-r7 t5tIūx#L}*SK;Nd]-WSGe}eDqY2./CYQ;]$MS|kT`4IBdW dAMO|QFYԓyrHw(7\9-H76M鶰8&Oք;YSTBs d*J)ng,UZđ+F#L-7YfPJ{uC7!UȶX<1)i@:^!Npy^B2)XO9%{\0 I4|@Ā훌lЗx$u1I[RS;fO5sPnqaj'm9Bdɪ'EcfO\o9tb3Hx]zo  5MbPkʮE;=jJ\="eb,^p 7n*ID|dGg4<ρf9BCf^ٙ9Mn9rTjGKj[bVsψřřp`HR?R\iK?bx5qyc7\%'Y&G]-wa#wPtg/ީș^\ufyA0[>S¨~{:uŅok^ZK[/.rk9لbJu?B`BIW]v'')ޗ3˲Q,?׫4X^7ޯ)}v{Gcjviq\4 VğJtz~9ų?Ljު9]'c65IU#t"`>`祘b3lޏ.+UUAIyK29% jڣ_d*EllԘvǮǃV(Q[ 2 V ( Q2: CT泮N&5-0~xpε-CcpEt06f=O>Rzegz7AB<9X Pv?]Dyw" o'P'E 1+rrR_۪/NhmYpcpxԥM],T3l\Ly*BJ"\"uEqag{u)x+5؞'OtnzJdT^#=%u؏{#633lײyzr*g;(#QUkR"Jh^% #<ڧ:"?fc_Xno0U9af9 VLK`^ҞQۛRy)Y1]ⴝOlLWMA8@i (aS]o'N|0ZdLy6[Unc}fM7fn|~3qB]'(go(T|Nz:׏1d^Hb-~D̓ YZD 7E3xj]3k)~JѲdV 6S{wӢ{[#rZIQ܏6;z?Y."yZ-w_DꚙZɯ:%Ȳ%} ⡐ MGũQAC& P[}@n^EC繾r{?&.o h/겠oԹ1+5nZ4Sf%x4+ }!xX/tيJ6iDf؉TSb=[RL zO?!> g DTOu*|قr0-GF>2"0v;+jTU 0 ɑlƓ) =nљ 'TrLy-)j@sÓun5|b]x$&7cF5qI%m* N#A:BVA!n'ID< _hf GNg>~npg:*j\4jlj&*Ԛ.ׇo%0chCy\(cٙY* ݋~6r :X4?0_c`3Eq-: &cʡrPR, uI ¯rH=J^(1+M fE6$I\; kX gx:c, !l|V<6O';n(R%y.>;p#u\KMؘxy1 56+[ 'Q"!pA$ř4AMbhR/%abVӡUjn+>rjZ;ȓH.<6ׁc.RYY{1 3D2JsAW[A/|uo67{Þ'a6lsI/i\XVS"BceS5+A_жWx{/cm*Ϗlj=ʈaADMh~&5+j_+J038|>"LxG#řPQ3'm'OM> G>۫&:s`MCUu-?֙'/?+‚ BoJ*='-,ͻr/8>G'ppqJq,(MS5[ޔ0Z鞯:ucNNl(SHKzꞦx^+Q J*T83yr Ԙ;hȕ Ƨ t S$U`Q@[PIh4)keEPT@Fy' ɍ 1 ?{:H4}rI˪Ć|8DqՁ05;u 㘔dk]cbϼ<Ϧc ܱ6 Imػ`v; g{k)_|hcr\Ew{ fm'T. -9t@ATW!&jpUC?-g?ޅрjz~jR@FGF̾x&*TIF&mϥm Qvc?qMvl7-_,.eat6 7q j9nH^%%PiSP. endstream endobj 46 0 obj << /Length1 1450 /Length2 6764 /Length3 0 /Length 7749 /Filter /FlateDecode >> stream xڍtTTm6tx膡CiNaffhAK@Bi)EJBZ:>}y_gsξ}_`3P"m`Hg HP64@ QAHF 9L`n(8Y``5 F_}w , KKʀ@$"MP{ p Cr(!]ve pCxaiiIဂ ;`=zE"!p_)eh!!OOOAJfw tN? rFpikQPp8`;_ѿ! l0@WUK_D0{5w`@UA_7W{(DZzUJH''3E>e rBNa wj"d wui(E`h@ID+ Jo_wtlmaB_aoPX!hfw&'5 c_ 0]kOz|T4 tOQ H" @$" HJ*O-S6bpΥV- G q%-!7@vsnZF^S 6 wwo|= vR{zp4Zƿ w!Q_ z WZ]*$׈K`777!_[ ,Ba^E  :#!u{-ҍ׉BHw_o@Lv!i@PBhO?|)@Ww7Vc`0/$r'MHI BKdiW8 _{-L1WEkh9]Tq#ɐE[VlzCkQqGe~dL\$ǜ+yd+y-fPpDƒʈUn6ea_@ZQi_`EbO2ffz>t쨐(ϒݟMOu- SQH8YxƋ>͛U5F:;4Et,X4ob+}+1pnP ,?vum1zaäx)̫˙ѥD|XCm՟j-84oݫn8DeR FVX3:8uav *!8&Se0#&yN$ !IE!=ʆ\ L~9$71Zď#Epo3T"qԇv| c{H>ʲ'7c㋜=1ެݘu51kNɴRnxM ?S<BDݯD s ^ՆiD+ѸNn4m|`(C1@rM<^eV*ެ!tSRM.5;& nK$?`RF&Xl{hE6{ѻWEٯs3 < c_ymac4SdSCδq Bds:]<{cüpi+rǁ:T,/!kqKد9b] {n~Hj6q=Iץs)awGn2ՂC =2@C[Iט3L{H=X` HV'/ evYROͤDZZjzbϋ#JIX:qIHGޭ="JM-rJi.G ._Ώ/X߫djy>A=9V"WTcX!^Ta\DzD<qeh{פRB/ )V~И <%c?`8'iFӹTw;_oW7!<:k I*>Ӭđ| !;u!޷Ze^eN̕pD9 |Je+2u VU@YgN>9TIϘtL9)yLeN8kd &KP .gf@>ٓ{;BjɆmWSE7P#O5np[ q$DQ{`V t1.m:\;8?<=~̈́94k-z'IRS9^ifLTNCbRs@Z1nﳛUړXb[enW^[uG+lWt@Qan&drp8ڎubk8YHiX le(<2% ?ku$C:,'w`{/fT5![קX_kl0is;'"^AwW;OH[.-j|v_ǼuISƨܝ=R k0G(|GbO]p~~d|VAHǧf/#hR*k;jvmqJH,q'H,9g(-#R_=H)w w: ˚UTuHŅZQPH^Dtʉ~Δ\eDka)pH4.h4H]c3ew0.kjξ rDc Ґ0vm$t %Ou-`eFݹ:o۾苑 aP ޝ8b =y~4m '7ZFqn:[ozIlv" ߈v5}$LHXР;Jmtq[㪄~#gwuh&ZPףEN+E<]k3voN[*Rߋj'#bҜwqώ]B\e|Bo+i%p[ i7'ڧUdŬ}ssr岉ڱQDp44Շ+| +HF0l-~Έyk@z3(> BҶ2W^']jm[r2q}5✋7Q)״O-3t#jsaCCBe[ ȒuKӛTMu =74na2dh,~sgT1áE)M33T`Yi2Nr!y~uvz{A%JOѤS30ڡwb ̝ϲe;io+te%4yQ62$y>ـ0 !ϑM%DDb|]A d1=n5ז%N;Jjҵ|BګuofG#*QӞ"VD8yxn .W׳ h帬i=.)Ex߁]Lռfݻu/LC2ȈxO0ެL _Y.=i{ћrHLBc>mnlx] zqcGEj鋮:qVEJ.#9=^o$kJ@^nwQ܄r~4 f:}X2FA\*tRWӱaqp/)wWc H +-hǰu"m ܆'UO3Klw,@Bhr\GL(~tV*eg2p;-{kO$xYdZoCfxmcU'*_O(>oHyFw >Րk [yLT;1W$w\ kS>MBv奧he|rqFoq`T)7 J"K^Isq{9biz'H5^4~vCA !a˹WBrf&*gQb\¶WI>;?,f~bmLNDž#.mcGԌa0뵖J?=)Yqd&}П$;+W̅$C"EEXt1rf3W~˅x,Dʽ7bړÙR'2?}_fV*>Do%Xk%YsGKP_jl=X!_8e ?sߵSS)*)H=3i 5#Nc_FdUfH|-aPďZեy3Mx\`֭ h>I`2d2X~ϋvSy e\ IiTZai E.=tsp5δarWűT=¡+SLMʷ[D7=G^]sw)kc? endstream endobj 51 0 obj << /Author()/Title(Colored Pages without content)/Subject()/Creator(LaTeX with Beamer class version 3.36)/Producer(pdfTeX-1.40.16)/Keywords() /CreationDate (D:20160404141652+02'00') /ModDate (D:20160404141652+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015/Debian) kpathsea version 6.2.1) >> endobj 11 0 obj << /Type /ObjStm /N 40 /First 306 /Length 1440 /Filter /FlateDecode >> stream xXn8}W1"_D ˺6MqvۭfdHrh;H. D 93 N8(I"J"4:'Z!%P3Q "b%RaUDi0DDVJ00 Q0Rf VQ-(@%DKall`&ƣ V;94'ZEN2 ]`F .kMOؐgEM !x6(198]|[BյDhfڨyeqT_Q 9Bd "gk%+QmqXU58FqScRcSRIȄ"L(2Ȅ"L(*J(VXz.?tiY}QLCsvƎGԐ7Atd٦ ũ`EN0:$w&gRΊ+=!c5*/*GuH;gCvV|SE1 `~RFVT!xPs )j;BrF>[a$$\ [!zB,""vqIBt8KN8Mmɐ0ƺ`o݆Pkʷ!;KY|Nj-$DsDDo%Y5GDADݎ~(Gَk! ;0C6;V9uF=qTL>=XY&5sC<(izMFP#עIG?0ǩ 8*ԏR?Ivj9i25L *y $t0Gu ٰ+@1>c/ʯ*XM~_4d* W}!-fmxIRNtY$'NJ*.^skb-2(C4Ei梍oFܙC J`AT,9p=2{} L/b-xg_i葌#סo++{==^qzu3c9.jZ163%>:Ĺ> )]ݤ_*?_Wáx1/f-⨯[LI#Nb)B \,rvz,ݞO/b^N<o[A<qY41v1{ 6aS،]Yjְulɾ&e:طV\9 vzяaUԷ|Ƃ;V '}oJo$G0(u;-VB :oiw';.67Vc?] rڮ͢';'d]ƿzv{V@i|v 粻^C y\ YnIOOݫz_mxeھ-6vqfG~/<98U"Tru-?w endstream endobj 52 0 obj << /Type /XRef /Index [0 53] /Size 53 /W [1 2 1] /Root 50 0 R /Info 51 0 R /ID [<302FE4DDA48E13C032869324B76EAA8F> <302FE4DDA48E13C032869324B76EAA8F>] /Length 118 /Filter /FlateDecode >> stream xGPD~I9 ˁKs9}SwՒGpQ E5!F&1 s(+TXc[n6WMcǷ6d endstream endobj startxref 19908 %%EOF dspdfviewer-1.15/testing/pdfs/colored-rectangles.tex000066400000000000000000000006171270070303100226660ustar00rootroot00000000000000\documentclass[aspectratio=169]{beamer} \usepackage{color} \usepackage{pgfpages} \setbeameroption{show notes on second screen} \title{Colored Pages without content} \begin{document} \definecolor{8f8}{HTML}{88FF88} \definecolor{f8f}{HTML}{FF88FF} \begingroup \setbeamercolor{background canvas}{bg=8f8} \setbeamercolor{note page}{bg=f8f} \begin{frame} \end{frame} \endgroup \end{document} dspdfviewer-1.15/testing/pdfs/images.pdf000066400000000000000000000625671270070303100203440ustar00rootroot00000000000000%PDF-1.5 % 57 0 obj << /Length 982 /Filter /FlateDecode >> stream xXKs1 WX~p`0C )]NPC &^˒,}uBA~6&&*tH0`?ep¬ 4͝4#w|0t9Ϣ#wZx4iQuY<C,~_;$5wos'R-,k Ȯ,1n!cA\u>I;+E;DeRP%V Դ'Ce T3Eʼn+#z{.8H^鄥k(gf}ȏB/ckHY[tk75~Z9+k (Gw/O*Ib(o.adF8.@$+{Uw$M`L % Z䒷+e{W8Dz.K!>z]l6nFa CPNpm/g!QqwٕMt)@XjtʜMX|2je|u`mShA[X]6O]N:.Yq\V&S>Ўt~LaV:T]fÇjb39@J)8VaQ@g70< `]l3}$`{Y;x6TeUt{Sc6|/u!a~ l(VS{{{uq6Xkןytg&o>ޖ۴e!6\_\%)8kJ-#U~ endstream endobj 13 0 obj << /Type /XObject /Subtype /Image /Width 640 /Height 400 /BitsPerComponent 8 /Length 3557 /ColorSpace /DeviceRGB /Filter /DCTDecode >> stream JFIFHHTarget Color 112 29 193CC Bz?z?z?z?!z ?z?z?z endstream endobj 68 0 obj << /Length 981 /Filter /FlateDecode >> stream xXKs1 WX~p`0C )]NP&^˒,}uj(3ڨS!)IhbVNCNRF=?Gq|p:xl uPG A_usI+I` A9ksQ9sBcnI䠾2{\O%T]NE60=Wڐdj*~N c8r j^mr0b]3huIxhfF'8mg !qgYGáwBLd[ 1OP&@+cnKJM,hPҿds'%h#>綏hSBӦL"mxb`_|D<.Tw&cxi2BfWFi#/R m2p6 D,+=cJ౵qDb-Dq?Ykl%靜I۬Ğ*-$mzT^U.,S Fz^f J;3GK` ы]VO>ef *5If3uuM;d͌nb4~ Qs<#cRy7Ydig ՝ y IMo[l8ki;~DM|1lktUkCZ領;<E;' <=UipG!lċt~]=zfO^ViN^[<eUt{Sc6|/u늴@!A~Sd1Vؽ8sO437voy6-~o n~9tk/ZRxy endstream endobj 35 0 obj << /Type /XObject /Subtype /Image /Width 640 /Height 400 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 772 /Filter /FlateDecode >> stream x k tQUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUY> stream xڍwP\[5-@pgk hpw`Af-8@ ܝ n5,$;<{w:Us^{wջjhյ8$``9 Hhiq@ /'ȃ ;c2]\!0?.`&?U`P- -(x@Ὲ0 bP( `WLi _fK p#b T@p[Ê {+-$ rt儹؈< p[&* rY'&@C f %Vh)(Ԝ?6tFJYZ@P/` q9pvjrp=ăAA9I ?st8]9]!jͲP+i# w? ^\= YCVֿʰrsҁB 2rLlp?-ׯܿ58e + |7XA, w3p.OA~?_& A>b.)%5= ?KSJ pA~a߿Ө n Pk@>)?\|?wO俲:ɹ983A Gן@0 \ p0HBmHl[?:&\!7_?\D~/) Y3~ |??ae !0_g* Re\0?A\H@~!oH.?C*? on..o>t/=S0KJI /˫=pl[eRVeI3%acSWÞKYpٞ5#0fD0)raS]`' w 뚡fj/}zⲗg E1h=. ui9-WT٫$!Fo<A;Q8Q%I[}Wa듍 lŦ=xU'< ]ZQ^ $SUæ$j:)9#b9Pq7-Y]y?'7Mx'“v`7do P⥠YS #;ţ򅧩Olm,ŕU f_y>5/ypfj+jY(鿥D@βo5-9Zl0s=;,u_ND9H&0E=p;Z'|j\PVWA^ 6(F:&QK+۩fbĖ✳( ٬k4=e7"*v4;zXaI+TWrπxgWklfܪzhV5*{n-#k߳Xi;Ժga`P@NQc]0K8ov,1ICREx4$B/k첬$dX+tSBEÒ7(v/L: 'hMP0㉟jPĐ 7|a~- z\T>烔^.doFAu}qDn%¢L/SEWkjzCgs$'T/&Q~RADu_ΚZ99Zs't=G>UWVsTs^ X$JMo|5\3xkHd '42P;mS;%#nZW1:=ӗ2kR.7%DY:XYf}yήUP~/fHֱU_YTMu!$NDuX''E>0=| `۔`)oUVxECh1mܶ݇u\>'Х;$xﴈrL0+"LuNxo/£~nS0xJinط/t6xcZ !~/Bfo)LW4%L|&ls]\kb0 gc5(?sYb]+Fsjp;y$STECk1?_RP%1oW%Mδj'P#5h:#߈nQjCxu`l #;"\t)!SOd9!E_pĝ ;8M7NKgm]^PjSͽ3{T f/Mή¾9ӎjU84uXC.J K.?Eqv:-&~_)hNDRBf8ޗb GLg=1)d@aMˤJZ]_Bk`^]( X.M]"?1]p?2LgP yw)ԽmRSlAI7X4w-@m{MHÊBE $uO_ 4™;vPJ2n59u&Ug=ϱ #ȮrReJIEc7Z w o9}fWVL,P'7|G7â-Jwi`u |[D]eM iy:Qio YY2!Q'_G/#uq#iߒ'L<欻~N{TLL{Xf$bhӃuylCMUuȄ}|^|ݮ{)o~La=XdӢ&{ ?sp"}r$h'}~dhJ8vbqy)KЕ}i2|!ɺhބƒ04t,<.G#!vtc흢D3-1d3aʐ( ўRN'ڏ̤z!lJCYq)I] %e$|ǫaBd4Q9юw?҃TtAkEfkb%ˋbͼ,">wSYۼ}*Nh0 ]vl]VrBt~Ug!~1qU'EiY)vňgfM"JOg/+1)n;uB#=9k\{ȋ^ٺ3iV(l= BpoX'_ѳ=l{5rr>o9,Tn\=n 4FayrCzSBAd]%15ǾyKpZ=co''uF#50 gsܿH؊c@&K7=͚S 7by鼖B/A3 %Se{! b YDi 4vU_a3ktx9#nq8;4N4"; V&QXtx'(k7V)\VNɹ^|֋8)b-$r{rkAFSP@p2_Bqͱ_+K'!W.XA7+Dj"X5UDZ+2j0jE}|*Bbm4V1U"c4:|Goځ&z5ӞrVC*/Wg_YpqV?;BS]j/ e?lTCYO.U{%;g0OUd?ըudI=;jLǸ)ckvqi._i%^K21ӣxߘB?hF,\2^, 8L0-"|C2zL@Bmx^5I+pf%Y=s%zGqkAEGQdOrgtb0oʹk K;hOc~<?; v}%mKaH'/î#D%)W?J]U%$]J(6ψmB>%? xChk?gc2rI_PmQֽI a֍#΋yAW{Wtv<9{pYгg$c^a}"Rgt5liקۨFYd)]w&xȝcQ)+?Ŝ% u }^̐ t a[+j7 ~alҸq^#bLmߢ`0'%K'7)ަ53+g)]ȭ/rP}?7CK;VqnrNm%pfjuNg=~b|~=9u~3N 0I!"*>nasd{6@ُnu%qs7#jϝHg؈JuӖi整ܫ+ی_^zEt7u!l > awAYr6R!_pGDR`K7oMuL=?sqּŋ-8g_HQG}BF)=9MdxnNeoiͰVpЕI2u!`tXC S0$_`lsG,lqO(azNYi' VEtkVqVuQYVrME Wc\p17/^UF.03[?/8q(F8XO ָ3,?߮IHTZl& D('BSVXɃ7e J`S^EL;NԱF+aAoc0YG 7wA %m.MW{޹m؅7aӡIPWGv/.Ln5',dW̛0Wp@Bq9v50%Emȷ!bu?RdvC[$LVń̛|vXlNA +b<Й7J=D1j㜘⨍p+x"97i5O41BK5q;Y(7DS=8LOuTCC1΀ʗ8օOWSocP3ʴu!!QHZKƣT-tNsuO\OE< }|>dbx:&[aAlMg[I+xkQTTF {m]/pR/<&K{1ێӧ.|yk&mEc7nBb;[ ~vfaX W\Ѓ3>&l܈k[,^*4-r'-G2;J{kbfЍ":o*̩,U+ϑDYшgXu> endstream endobj 74 0 obj << /Length1 1450 /Length2 6764 /Length3 0 /Length 7749 /Filter /FlateDecode >> stream xڍtTTm6tx膡CiNaffhAK@Bi)EJBZ:>}y_gsξ}_`3P"m`Hg HP64@ QAHF 9L`n(8Y``5 F_}w , KKʀ@$"MP{ p Cr(!]ve pCxaiiIဂ ;`=zE"!p_)eh!!OOOAJfw tN? rFpikQPp8`;_ѿ! l0@WUK_D0{5w`@UA_7W{(DZzUJH''3E>e rBNa wj"d wui(E`h@ID+ Jo_wtlmaB_aoPX!hfw&'5 c_ 0]kOz|T4 tOQ H" @$" HJ*O-S6bpΥV- G q%-!7@vsnZF^S 6 wwo|= vR{zp4Zƿ w!Q_ z WZ]*$׈K`777!_[ ,Ba^E  :#!u{-ҍ׉BHw_o@Lv!i@PBhO?|)@Ww7Vc`0/$r'MHI BKdiW8 _{-L1WEkh9]Tq#ɐE[VlzCkQqGe~dL\$ǜ+yd+y-fPpDƒʈUn6ea_@ZQi_`EbO2ffz>t쨐(ϒݟMOu- SQH8YxƋ>͛U5F:;4Et,X4ob+}+1pnP ,?vum1zaäx)̫˙ѥD|XCm՟j-84oݫn8DeR FVX3:8uav *!8&Se0#&yN$ !IE!=ʆ\ L~9$71Zď#Epo3T"qԇv| c{H>ʲ'7c㋜=1ެݘu51kNɴRnxM ?S<BDݯD s ^ՆiD+ѸNn4m|`(C1@rM<^eV*ެ!tSRM.5;& nK$?`RF&Xl{hE6{ѻWEٯs3 < c_ymac4SdSCδq Bds:]<{cüpi+rǁ:T,/!kqKد9b] {n~Hj6q=Iץs)awGn2ՂC =2@C[Iט3L{H=X` HV'/ evYROͤDZZjzbϋ#JIX:qIHGޭ="JM-rJi.G ._Ώ/X߫djy>A=9V"WTcX!^Ta\DzD<qeh{פRB/ )V~И <%c?`8'iFӹTw;_oW7!<:k I*>Ӭđ| !;u!޷Ze^eN̕pD9 |Je+2u VU@YgN>9TIϘtL9)yLeN8kd &KP .gf@>ٓ{;BjɆmWSE7P#O5np[ q$DQ{`V t1.m:\;8?<=~̈́94k-z'IRS9^ifLTNCbRs@Z1nﳛUړXb[enW^[uG+lWt@Qan&drp8ڎubk8YHiX le(<2% ?ku$C:,'w`{/fT5![קX_kl0is;'"^AwW;OH[.-j|v_ǼuISƨܝ=R k0G(|GbO]p~~d|VAHǧf/#hR*k;jvmqJH,q'H,9g(-#R_=H)w w: ˚UTuHŅZQPH^Dtʉ~Δ\eDka)pH4.h4H]c3ew0.kjξ rDc Ґ0vm$t %Ou-`eFݹ:o۾苑 aP ޝ8b =y~4m '7ZFqn:[ozIlv" ߈v5}$LHXР;Jmtq[㪄~#gwuh&ZPףEN+E<]k3voN[*Rߋj'#bҜwqώ]B\e|Bo+i%p[ i7'ڧUdŬ}ssr岉ڱQDp44Շ+| +HF0l-~Έyk@z3(> BҶ2W^']jm[r2q}5✋7Q)״O-3t#jsaCCBe[ ȒuKӛTMu =74na2dh,~sgT1áE)M33T`Yi2Nr!y~uvz{A%JOѤS30ڡwb ̝ϲe;io+te%4yQ62$y>ـ0 !ϑM%DDb|]A d1=n5ז%N;Jjҵ|BګuofG#*QӞ"VD8yxn .W׳ h帬i=.)Ex߁]Lռfݻu/LC2ȈxO0ެL _Y.=i{ћrHLBc>mnlx] zqcGEj鋮:qVEJ.#9=^o$kJ@^nwQ܄r~4 f:}X2FA\*tRWӱaqp/)wWc H +-hǰu"m ܆'UO3Klw,@Bhr\GL(~tV*eg2p;-{kO$xYdZoCfxmcU'*_O(>oHyFw >Րk [yLT;1W$w\ kS>MBv奧he|rqFoq`T)7 J"K^Isq{9biz'H5^4~vCA !a˹WBrf&*gQb\¶WI>;?,f~bmLNDž#.mcGԌa0뵖J?=)Yqd&}П$;+W̅$C"EEXt1rf3W~˅x,Dʽ7bړÙR'2?}_fV*>Do%Xk%YsGKP_jl=X!_8e ?sߵSS)*)H=3i 5#Nc_FdUfH|-aPďZեy3Mx\`֭ h>I`2d2X~ϋvSy e\ IiTZai E.=tsp5δarWűT=¡+SLMʷ[D7=G^]sw)kc? endstream endobj 79 0 obj << /Author()/Title(Image on primary screen)/Subject()/Creator(LaTeX with Beamer class version 3.36)/Producer(pdfTeX-1.40.16)/Keywords() /CreationDate (D:20160404141703+02'00') /ModDate (D:20160404141703+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015/Debian) kpathsea version 6.2.1) >> endobj 11 0 obj << /Type /ObjStm /N 65 /First 506 /Length 1719 /Filter /FlateDecode >> stream xZnF}W죃"{_$ȵQڴ5ȌΥ_3\Yu* \Ξ93;\$Lif4SrȔg.V`A"2Xi+ѰMRL֞iXzov(a12X#t20=L.@FkD !)L]BdΣuX #<\Gø&1+Y,N@ȉ. S<%l`N[Tʁ?3$/$(02Ory r=mOO"H(]h@4 h$( N. OE_x !(5ux1 E +&Fu?a_ן/YUfWCdXWUלӪ=Ϫo>#$a8Ǖxm9+v؛8j$:ơ ICenTnr&茢3(:b2(&b2ʤj!oi=M{\CD<wlPҦ=;BrjčLLIn[zVu3W(m_"%jtf&p`u{@:*R`X5 V)7NDZ\8ɥ4:w#T+'Z 6iP$x‚"',sK")d"AqF2_=2s6B#XB"F&&$ "pd867K\f)r7%,XXqzE~3Dתĥ1sI݇6'Dc~wWhcwǣz~|wVŚ`A[;je{7N:m.cOYV_S~WϿ(F6??= qi>3eP"M;^ӊ9DDW-?e_/epR@@;}u 5lvcQLLE#t/3 C^opt'!tC|&ǧ,xFsc ɧIꏢЈ?rW2TXʮϦZJKknOzS'}djki^}[wŷIEwc9B<Ey\-4縹sLck{dx}mvqiY!'t _ssauWl<Ůw=,?ӽgq%~i=i:{~~IQvw> <5BA47D071B1FB753DAF8C3A7757C8241>] /Length 168 /Filter /FlateDecode >> stream x Q{uK Jz d2dDM|s,qDp lDo/!UI.>@!A1đ@R_GEPFUPGMF]CU ``r}+54}-8׋# endstream endobj startxref 25557 %%EOF dspdfviewer-1.15/testing/pdfs/images.tex000066400000000000000000000010241270070303100203500ustar00rootroot00000000000000\documentclass[aspectratio=169]{beamer} \usepackage{color} \usepackage{pgfpages} \setbeameroption{show notes on second screen} \title{Image on primary screen} \begin{document} \begingroup \begin{frame} \begin{center} \includegraphics[width=0.5\textwidth]{jpeg-image} % Target color: % 112 29 193 // 701dc1 \end{center} \end{frame} \begin{frame} \begin{center} \includegraphics[width=0.5\textwidth]{png-image} % Target color: % 20 99 180 // 1463b4 \end{center} \end{frame} \endgroup \end{document} dspdfviewer-1.15/testing/pdfs/jpeg-image.jpg000066400000000000000000000067451270070303100211070ustar00rootroot00000000000000JFIFHHTarget Color 112 29 193CC Bz?z?z?z?!z ?z?z?zdspdfviewer-1.15/testing/pdfs/many-many-pages.pdf000066400000000000000000005263031270070303100220730ustar00rootroot00000000000000%PDF-1.5 % 55 0 obj << /Length 985 /Filter /FlateDecode >> stream xXn1 SJL+TĶ{CBˁr@=vfdvPQ`ݙxllaJ|oLP1:MT>`uhkpv|Ug=hp*,bۏpZ߭ =L?љ9bI~Rf!, `K-rÂJ"ٜ<|+CR&dyQbeS$I}FKXj͒hߌaNi.`Gژbs8 0a*p fWP2|fDP8N.wBLEɶ@BfIbW"@Ss11az @WH>͝4#w$FKYv>yDN =>i]^M,~%Si.D#,1p ˚k$K8-` 9 GJ}hLTjU5PCjPȕ-|z{yWj$tҁ5Bс+K䷕B/֊[ͺߢK4~S'Z+罂i.%Oaܵy1MVIbOEys #6J]od2{ez*=X0Oח=t0=+,AuDq6ɛSN_<u'xnsVܬF֋cǷL€l}3ҩ%36).X W>GEd%g95N+;{ďż6Οq:GŊUt߭cȊC:Hg+OpfOApwefmR;SB]Md:~⡚0> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺsvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|l{g endstream endobj 111 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ{vxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|q{i endstream endobj 137 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺwvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|v{k endstream endobj 11 0 obj << /Type /ObjStm /N 100 /First 827 /Length 1455 /Filter /FlateDecode >> stream xZn7W1Y,@0%rX>$tHC3cƉyjm &ҰW |hOB)$J!P`b2eB⅂PePI+.*K=yx*i ńJ؞b# q SaXk͔е8$NTP.`,hP_^nΗW+//. Kg9L'͊K-xf6ّɏx2>[PѪ/,*) '1/܊LEηv"a_`fM.TRwVaf3--f{DRwDXC^_|> ֤37^ꃱ̑SuӞˏGW7k0c2Oa/ǗۛCs==^o~_l.0m闱Xu5Q jdO?SÅ4J_A:w͟t%(ɛla.7+y}z]lda> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|{{m endstream endobj 190 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺpvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|׀{o endstream endobj 216 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺxvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|ޅ{q endstream endobj 243 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺtvxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|{s endstream endobj 140 0 obj << /Type /ObjStm /N 100 /First 913 /Length 1506 /Filter /FlateDecode >> stream xZMo7ﯘc{rH?#@>AC[#J)iD>]-c#OR"qqϙgJ3T3s qBN^SQ̠B+9࿉*|FBQBS Bu( #&T91EF"":"SJ Ę5r*&|R[eJ5!mPE,]DUJQTS,L=@ё@DH$L5jI%i^9f^Tx0ҠF 3RZqQ3W+) yO%B.LET֨T ?)ULgZ=բjEк\Py`ݒ e jڭJ ȋ t\=YBp%9C|sU0DU˭b\C_\Eg )ʎ"=O AD(?/Z\zX-VfZ)\hMg`F҄94~tAOD&4MSD0>sƴ{u"J2HvW_Ӿ4>\\o^-.!Or0~fl.t XnX.(1t<[nxNi||]rvޠ7כʹ_^]/йh8"bu3:(,63j(4u{bmvNIekd  /^4hxEK5Ƴ׿lW/zsL M*%[S; lqE-6RftٚP>_=Yv|^ת/2O7oâT9l,N7]TKdcr2O.<8>VDA=]rA nE}υxu8갅x$j:  HsM_ R\ ͈d(S"]t3vindJvBJr+B9N沾 t iz ixn֪8Y+ň$EQ5y("aO|"n&nݣal7x'/>}|buEǏ+y_ 9N|>^]>l +^t}']~9%f=S ݏu539df>2>O2Ñp0$30 L2#f` I&gƧn|Ƨn|Ƨ/m|j;.0> wO6ysŜG1'Ss2ŜL1'S+W ^5jxUëq>;|Χ;|Χ;| Cϡ}󙿂I9O3y4sm<ɴ97}ܲhmVkdždždždžg?ΧtӝOw>tӝOw>tӝϗv>#u347 endstream endobj 270 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ|vxYN]!Q׎gG'E!fSIflSZ.l KFskVWwfy#m?tN4h[aYӑ3ubN̞nˌڤ"-4ṭC5ya09xH(jpJ)S,j^Bw( &OLkex!8FPuhЎguՁx(6q2|/5ߊoB>7Vg)zw4ٓ'a8,w) ؽ~x^ 㫐|{u endstream endobj 296 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺԧo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w { endstream endobj 322 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺӷljrx ؿv>;:$(= D&7#K2ӂ%iuesT\MXr63_Ĵ23HXiA[X]6O;*8DLtxgw[jLl&i=7(զISMãVBTcXOҦG` YjBzGY0ybWY+/ 10 Dv?+C9C|VCLUq>O1ջoɞ<aLYE\e_;{ endstream endobj 348 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ4o7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w{ endstream endobj 246 0 obj << /Type /ObjStm /N 100 /First 913 /Length 1493 /Filter /FlateDecode >> stream x[MoG ౽E:=APAH*Ҧ+)U&a;^ǔ)PLL)Q,J\"ň%F*> Ոq|°Tl!fJ+16lPQ0|7hDbX%B1(f,b b6ׂEtZ-J)S TILM$44 h&I ~zjJ1 C)$sms)I`%PՆ2*6T(\)e6vY)ZXX ?ͥ{,%QV *]EtKdȼo,1v)! JiBaO 5Q5[ QRRF$JVh ΍;H&HyDLejYmUV-WRC FV))w@(3&Ќ iK+PШ"Yq`C,hA8>AZVFrQY+" ժ(;P\F|3k0|Ā 2rT0| ިJ%#VD exAe87#ce89Gt@?O?#KYF˗ý{6٫db]nV߼>>.KcN׫-t < (m@OO7˳΁蔦g˷[{ji/XmkМ7_^]/ҹP`WAζL&R1Vk9'iΊ6-nvg;v57l]^ Ӄj.o'1uaDpub^F:ΕTxlM,~~^Wk 懈|3޹E塄ݭWȼt6=Y[@hnjsI2{P!&9a>q@8t?_SoIX`qܔbanǧ׫ۻMgB<֐zbs?&=Ҏ&v#FYSnjAjS)=s(#Ȯ8+ &ɮ`+ &Ɏ籈:^qxǫrwvӕOW>]tӕOW>]tӕϗV>W>νKGv}S]yTWՕG+W2ՕLs%\4W2͕Lsx5kzw.}ҧK.}ҧK.}G-CϡsI=g>w6mv[Vnj:;;;;;;;;nw7wӥO>]tӥO>]tӥϗ> (gW endstream endobj 375 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w{ endstream endobj 402 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺto7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w${ endstream endobj 428 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w*{ endstream endobj 454 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ o7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w0{ endstream endobj 351 0 obj << /Type /ObjStm /N 100 /First 913 /Length 1498 /Filter /FlateDecode >> stream xZMo7 ϯ౽h$Cum "0M>&M.0Ό'yVrHTIR*L¸-Rۨwj.zﰉR4SʃPhMʀ4. .*mT#:1\4K(M\+ZD["&<ɀH!gX?a b+iD6axƄ;A@EL;M}0@!.ٚ:ih*bRi+VD(em0kQr6KQrC#dD٦T,$)""D5 (EPmjJWVjLP Y[@2pQ 1`nE#$#H ؅yh#3(%CǛ DjDDS"p]U;B DzY2RS!UHC NDKyj ԛ7J 5˞Ǐw,%rS܎{8>\]ޭr05ns 5={\|¤/kĩ+$1Y4{ə{F~W2 '.9p ]d0>Gٿ-v%Lqx+W:^ux!#,}f3KYg>>q_o;%}M|j\r4%Gs \40%Ls \40u;^wx>b飳,}f3KYg>ϗ>\>>Ed ~ms#E-6uxɳ,}f3KYg>ϗ>;3/5 endstream endobj 481 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w6{ endstream endobj 507 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺLo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w<{ endstream endobj 533 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ̧o7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); wB{ endstream endobj 560 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZMGϯcr鮮iGXIV{pv-Ș) %=WHḮYa` ۳Rgj1ًJ wE x%RhD`ֈS(6,̵3۬L*jg H#.k 8)h=RL pjR.!S R1=Js'3XDmlA#WhhW Wq+AG%rH* $\1ieSfKEffé)U( Ɂ;E3Уh48\YbhddM,J*S8QɈ`f)G FN+HQfbBB-»b\ bym*v\@SQiU՚E4~BE4h}V O1(*O1"ܬOЪ8EYbFbZV,Z{ hQ1%> #ԋ2|pĪd#dsT#X3rb]nVo϶ ՖNNh8EXq)-am@7˳΁7[yri-;Xm+T,__o.7v^[ssBC-HvqwZx>O;&+nmv[VZpltxxxxxxxxxx:/\#n8{v|z7Wp18<J %88ZP?=Zyl^bޢ!&rLJ~Qz17 zW2.d{r2 ,dD<>F1H {.zr` >dhCH!R{;ҷ*G oe=7|:qJ j'RbH[>TH.N"z )ϸ "8TS! -uڧ|Ӭn X% "{"vWbH} 'x~!'>nq}tܗ/Ea>ʐO;>^]ܭpojjK{\Itܺ;XŔ>'&gj8H|$}4) }8_d]d0%Lv ]d0%Lvx+W8^qxUn),}f3KYg>7>бJL_¼#}Rbw8>եGuQ]zԝp)\42ͥLs)\4k9^F6K#},}f3KYg>,}f󭥏N s[%}H_A'#;nⶺu).eni,}f3KYg>Ϸ>;_r" endstream endobj 587 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w{ endstream endobj 613 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺcNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺcOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xMo7+xl/ gHF| :-FR)P6}g5V4i| `Kpɢb! UCaܪlA#1}NZ}ZC!%$KH9Z͚pQC⢅$ zP V$fZr qtR hڠ#%pLx9poRk`*Щ-0Wg  f=ܦJD\))di*ra+JAbğٍP9Y/sWW33!^ -4MUsP"Nʈ4c$\X=LkBIT `KPؓm$\r(ev|Fdk Oav#weV@R*QAaeL"? bиTh[C)«1Wa6ٳ̦DY#TALaZOd*rfb$SHJT 8' "M}6(!V ;@F(01}0l"! cؠXGHĔpr2L¹Hú!L?3~dH0՛//{m_7g.Or v]4?jNNt:%l {tg˷pcuzǫzj\m_#_uo6}^y lzuy!s#oXy$) }4r#'Cő:b#!9ƆQ)cËQD$'E``vGJoң8):j9NOG>}:tѧOG/>sh;>a>\>4G= z84G(enyL^eRT/KKuѧOG>}:tǿuNG3}qу=QeQeQe ww}OG>}:tѧOG/>/ 35 endstream endobj 692 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu{ endstream endobj 771 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZMoGϯcr,$>brH>$|+dAvzIȿϫ"$#df몚~)ES)RҘ* %âGLPZkv4 a(' %`VE$\q[,߀ tPEgU Y]F$?)2ڀ&ѳ4EA0y\`1U8Hta-H䓶`\\oN^-.)Or0~fd.a<^ttD1kÎt f}qx|韯rv^&ly~X^Oj`δyq 9 n Fa.0qZٴ?)Oݟfbרy- f /^6lxY̊K^ O^Zr3şߏOƇg.w@b`QU\Duw0ݿ P?㼦'ҙH紐s)V'9yxxq{DZ8)RLU"juy{)8U6Szכ?Ϙt=A'SW6`qu332.IQebCLˆI1 #&a$0UëW ^5jxrOҧK.}ҧK.}ҧK,}wg߱wJ73>O3Lry ll0&l1+fYccccccc IҥO>]tӥO>]tӥO>_[IdS>,1LzFz &eIhR&&eEË /^4hx%ó.}ҧK.}ҧK.}ǾG9 endstream endobj 798 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w"{ endstream endobj 877 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺkNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZM71]v.K+$> !jZAf쐐WݵC@%0{e=&\-1ڪ]e"W |ĢicB/! c "iRd;5`_>s/ c./χ{_7'K!Orrq~v]:*xں#7c-nX2;t8YntfnZVj{>ϖכW7L]P}@hc:z"uFl j|Rz>͖F,fjV_^1bx+W5Ɠ׿o矮V/zsLa ns"H>!ozS\t?=Yy|Z|_vMO[LƓ%G<*) '%O !q9 Dt9["7쐃f&R>LuQmD*a42R!^ 0R/zDD_ vҒOr ix4kS9i֊[L"IVH;"H,!l_C> }M/?37}qϩ7>'z`qS%pv8'QiWۻUML|כqlCU9yJsV]L{ə{D~_2 ҧ&90 #&a$0bFLˆ5k ^3_wG.}ҧK.}ҧK.}G<+}{Go8>as,l5+fْY#######Ë /ֻ#}RҧK.}ҧK.}ҧKo-}wZ{I=)3yG5O4Mz$ɤG2)L$2ɤL2)L$Kdždždždždždždždžg?҇ҧK.}ҧK.}[Kʙ7x endstream endobj 904 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺkOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream x[n7W\fsH #!FZ]cNOlKFb;Y+V壺Xlr$U 1H! -c(K(hkJ6kPUcHQRHTi$Ѻ"l RKPBlj+-@Vclk T$RZ+Z8PST ZIi,Bq, AZjƁКb-Xvǔ L *tUcd. R]ߦ) h4吩, 03lJȥ٬r#+ -)FprR(,RCiP0(hº*bPS]b !5ۘDɨpY jtc.KXĐ1HcY0AY)To0E'1ePd5cٚ- `[i:誐TÖ́ ² H)Eq 5Ev)+2sr,[XCڦ'4sN BȠsˋţpj!clfI),6m;aNx+ݚǮ]I^+W8^uxUk5l]^ ӣbegӷӳufZc8Pӑ])R=: zb{^1}mj~BH=u>d^h:-~]^ȈXz|"47\Gu? '%t9 F?dn=[#x,^V//999999999=>ҭO>tӭO>tӭO>wm}ie}{OO_[vn=za2VFʈ[q+#'''''/;^vx>[n}֧[n}֧[n}G;'ޅ endstream endobj 1009 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZ]o[7 }+Q(A~,VMyمnݿ+ڮI +tuKIA x_ d9mdE`J&m`HB B ֥XD QP#! jB:ЋrĨ8F!Ld9 T7gF9TBR+fd5 9= r5E JHݘdjդlEB4ǁM*סTGj2Ǥނz#P2 vY & Tm'e,,"ݢcU821bzRg0/o\TH);X}lqf{zy8OyV5w'n3b:ެwptӱÎNj+t9?YT|n+sbPj{cWӳ՛ͼ}WwpjNijeF"lMuS#* z:gQ3nnZnminsq 7 =ߥo٥j&#=ocW~> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w*{ endstream endobj 1141 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺgNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺgOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xM7 <,䣛 vkvuߗ MZgӬ X%EQ=10Q#`bmHPIʐQ KkPJV#'fM1 ռPKe"GK#d)F9`QX"d;GSp@ubH/H8PKeՍsl!IE'q2PT=GݳSMuN5u@7 v#!n$7 WFWFWFWr;u(PԡCQE:u(u(w /mއ$E8}W(oP' r!r!r!r!revevevevevevevevevQE:u(PԡCQEE;-(y*}(!!$:=8ވ8ވ8ވ㍸rrɕ+'Ww7;u(PԡCQE:u(u(' endstream endobj 1221 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream x[Ko7W̱쒜IG shkB`$YijGAU&C ? & >#ZKFT < d3bHd=JkԮZ"mҏĬE*Q>Cqf1m@fH2)kR!lRM1P(bJ4 "M-ŪV5 6B}5u)DPO9IhxPQZH P.5+i 8Q 01bllDL66p1Sd-( YY(7ҸJ(ѢQcE-dކI6!j dL }iR!d0@.l!ת0AZJEKPp,X f-)( 5ӯ*JGx)TIGuϕVh-bѴ^cVI+VM2 ChAG:XA͆kXjN궰ll-R`5Ju/Mt; 2I+g]IR+3iRo A"bC k*5}Cۥhդj/NN#8ׂG`_ ~uV7^],ݳ0>ܼl^//W0 yV5wowv bs,z''0j*۩RaBO˳)WowQ^Y&ޭֻa1>[ll/W7|?6o܂Ԑ9],s\ߴ`Xnٖ'At?-vNnٍF܈n8rqő#G.\:ru7ۤٛ_w`ZmYb~|2><ׅQ|3Y a#k@;@}yrwY}k,]\-I=ߦ;=46x6>YR KoF?B8= * a18 s$R=<jY)KN9Csb(#4Ohh}{"9bo9ad$S!) )S!|OD hh 8`BZ4e@42N,JAtHDI2O"zW߾xDN<%Ƌyo|~ YHzc<*a)i ?9\t1[O{T|Ϊαks>E?E_FI{OI&#@7 vCn79:rtё#GG9:r;$.(ꢨ.(ꢨ.(" Qtd%y_EDuR.Bt!.o A7]ޠ##########sCH(ꢨ.(ꢨ.(請EGv[EQ_Fv.B B\ްvy.o卸GGGGGGGGGΎ<9E.(ꢨ.(ꢨ.(u_wX endstream endobj 1326 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w2{ endstream endobj 1405 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺoNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZ]o[7 }+@?a+ۂC}i@R5R& /6`dh=\؞"W+)(Kf䘭ȐQ,fȔDc`̝Ks}@n 9B@(Ѭ T O/V+M"Kf>[dJPі(Ad\2w2e5Y4-Dm?PEt=4a>EShk-Ei\Zg MN: T,%$9Z]t$ӾmhŢYKhAQ#[1Z;2o&`R-bZoGn&+-lOr_ȭȶ/ŚE'`E9&3+VNn>!\Vo޲HmӌfͦGpng0/j\҃Wf`^' 픧f^B~fYvl:^-7ptӱl[n+t:?YlT|vuzz)f\Y2Ϧg՛j-Z5D=jRΖL1Uuӎ|"[J#nA!;E7Ѝnd798888883oeIͦ7n./gӃbŊgӓiά8>vt bk`u}/hc}drcL>wq%DťLz"dz2mq@FoUʡٖ SHԸF % ~1r  ҂]v<8#'sDj=wDV"ZNb(; -SK(0R9ȇ*hE>y"CB'諥O3i_AmmpԊAV$=H*!eab#B}zϩ7C_`~Zz^+a9i ?8c1_33.]Ts vwފUJ%MVt}qswKePe"Ⱦ&)^{T7;Uc_ t#An82:2:2:2:2:rrɑ#SCH(h!(h!(請n 9n*_t>("ڃU !W5䪆\ՐvUÎ̎̎̎̎̎̎̎̎9#rh!(h!(h.j(mEˈ"$.B򦸼).o˛򦸼)\8ruՑ#WG\y_|wCEC Q4DEC Q4DEC}mQĻGBO} endstream endobj 1432 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺoOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu{ endstream endobj 1458 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xn[7zYr0R6,^I@QQ6i #^</)$) ~@!G y5ej-j-j?I- w6 ,D6J j1ZpRzjҪmB*L.[%Ik9 a{YGOQ 9k"ŘZ:OR(Jm5joXGBzrQBz ޛ){ R{oEYM1>JD$$|0) \$VJҹ,$2e4f.*6tF*E,A ڲD$ *EsdTn#E%rZRokzVo)PHviM5ޛrּjGr$ \܊bbSj*k^ )a{,h1:$RyA̪ĶmVzpŁ4 6,m}.VkZ}Ьn3>@O@L}"#P f]7+~>سY\=o}W7֝ ?,ڗ4u\z=pW"=UqCȨk{=\PNy:l%w66͂BoMNNh:@a/TڐWg C)M6d^^Xnr6=[Y]_.lOsoqqu=zG|q.f\ϗozXg4킘Yj[ η!fd3Ռ37#fr3f͔)7Sn;evF0؎[f_7˗j}Xd Kl$BC\+;7 o|z"7O_oWKo5cC/Jo,Ns[I_td:[\AHѲ cI,( Tŷ#VQFipH:N5H .4?2%\S!%\R9N2^6,dD'\']ǩGKfspRv\@@w #f'8x/>re;~<_]RL̕p|zpdw/tc7]_8yuJ~]AYI'I*P&ْF0!3ŌjQ 222222222r;Eq@рE (P4h@р[")E[PC(P_|E $Dh oM2I7&$SNL9rwҀE (P4h@рEE?: |H_|Eׁl BAHCM6Ɇ7ÛbŔ)S.\Lr1jՔww7H (P4h@рE (m(|+޲j endstream endobj 1538 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xMo7 +xl/3%`G shkt7Xoߗ7H xIQflj*dҟI r6ToŌB1CfȭSUB-֏`h IZ!̵NcY 0k2 Y$j1`j `ŦW lm Ef[VjVjY N m+z5TEA@iN0IFM\^DȜ[o,f"ڐ@#*7U}(Hq)I)$bN X#2+%*G`[MۘPyfbU{e.f H,֫b$Ft"h޲jVMlzR E,垴p,4\JTS* 56WbEĶP[h2.s:yDЊòB C6ceb,Ѯ ժ,ln"6N@mjEE!˺-<;]$ ZB)9U6u*: Vх3];iC; k ֪89Y\ (O?(CEAÃW.`n^mg+H)Ojn[Al t ztڰS/;Uvsy=:+sbֻzwcu^lb|ټ^n^>ؼssR AsDjnS˭*ۥ'{F=A݃@7 r7dD7ЍFv`7čIKc[b<{}zr1>lVۖp1~?>R|3] a dW^2Gtx|ZX> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); w:{ endstream endobj 1670 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ`Nn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺ`On^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZMo7 ౽HHI uzhΡ^F`i_R3ܠMnuƚHOOF(@(1AbP?"Wmb@֒l6LUQ (ּ(BLMA9G:HDiZX1[HlL$@1 /PhŲDRtDZ5&cm)"P6M6!Ql(*\hO3$IiK{ZUEQlSUsT& pɦ eP]3Ȧ܆e*i?MeWHsh':67mqr,TɄEW;dm(s1'|΍:/SÇH1Eg] Aw5ՠrWCLLLLLLLL9{db7EuSMQ7EuSMQ7Eݹ)zStUM(H=ؽaw5쮆հvWjّؑőőőőőőőő3#SuSMQ7EuSMQ7EuSt禨 LQ~}.O))nB&$Mۛ)noۛ8rqő#G\:ru[|Q7EuSMQ7EuSMQ7Eݵ) xO endstream endobj 1749 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xMo7sl/~#@>AC[#JC(Hd} `\~3| qqb(9PQ1e/ZTR $RPXέ1Z(K Sa-թKat Ut,{̪́O:#Pu,'y™| 6 CIW*g A-QF,(U+B6uB-:_)u1AS Ei@1)i Q:7 K.iCU| LytldJ$Eむes9i\nՃUl@ YXYr1VjQK;.cERRZKBERkTj@ mqɞ$#$E#I.:6'$SӗBI+T}R56G59 Rfz~ U --Ox:5i6JkS.@ڕjUlօEU\h*>|i)[uIh{b@h1ݛ.7RR}qr9Ͽ/C@ G7^],4>ܼl^//WC.wvM~rb<ݬwtrB)6,ru;Ed!=>n.V;:ޣSȬ|RC/V 8ޭֻn6onnu?6o\TWE)..7ͭ3''[(zKsP}mTVVVHVV(V+ԩ̦̦̦̦̦̦̦̦̦}6x]{zr1>lV,w1~?>߅pV* GsČN-77fy!EJDvC7LD''d BꈦG,*!b i6#2讘Cp? C[eGPrɈH'E` {G Y_)s8RÖ)yc7J2D4O:d\DD382!<RjiVeyӬ;er$:D$Β!"AD|նo:4#[.Ɠ#o<߷{N9qǃyP G恏2|zelqQ=lXnE׫?:ܺ%y|ʪ7Ήr|RE%J2P=7F5&`TjQM0`)GSM9r4h!(J:u(PԡCQE:}u(*w dPTCyAH2I!!7&dÛlx o)gSΦM9r6lŔ)S.r(w(PԡCQE:u(PH|Ev[P=( 11111S\Mr5jՔ)WSLPT:u(PԡCQE:u(P4_9/@ endstream endobj 1855 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); wB{ endstream endobj 1933 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺhNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xMo7sl/s#@>AC[#JQ&i| `#.p8#_9SL"`84>q2W5*Zj-0<9jy>CYdbhOW > aKTORRJ}SbX*; :=qj0qumXX8QpQSYLjXQ D<$F}GH6䄃B1*ScX>YdJ.iɋzR`>)RvmlTb[)բyA6G' ^2KLyU&V,,E* Xk,xPblm([TDEDSPT\ јފ#hxSSt,+IRI:IӼ %U)W#44 iqsQ %55q:m)rh"UŤUfuijvvPqkt_xV/IWVm\ر֙appM8:pB"`N fh{ϩ r(:[89Y`?!.C@q£W.ign^mg+ Ӑn]Oo{{|[V[b<ݬwtrB)6;u;zelHO˳ՎΡ竷;2?_ыwFk5luy\L^>ؼsu" )..7-3 [('ΧsPcmlT3d63Ûf3 SMٛ222222r-mRͯ`Zm[d|x>$j?K,gYIn,!-5龥PXڷ7$5=P=<6x6>Ye ϷAH%{OVb$788,8cđ! D8A5x뢧HCc"~˔q*Ȑq@O!n_!PS!?ͤ!4<`́DģktH} ]lF/K H'o|h/o8})Fz}u{΄P\:pzK֕f(#* z|ȗ`=ž=jQM0 F5Ѩ&Dhє)GSM9r2dɔSCP$:u(PԡCQE:}u(3w d{P"P BAH6!&dÛlx oM6bŔ)S.\Lr1b5(*CQE:u(PԡCQ E2 (:݂> UjR BB oF oF oFLYLYLYLYfelx3،`Ƭ,E:u(PԡCQE:Qj endstream endobj 1960 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺhOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xM7>?~ .=ڮFhMP6?N&JK(L~&.rt%Èhh$&WRU#;BVQ#B"x`a,VB E-|RGձ)UA4%ֱ]ʭ-.q$K0|kARsI0r mr,DѨVEMZa5me}j֧Mtmz, S )9*ԟ#iAuҍq`M1ULjW8+St,'B:*1CDּp걸UA?i:*Gmb tqk#IaHN=kℨU}UҜH_2 N*S,0jiv5͋\^U&&"}zZuҰkmY>4]˥#9Kc=Uk\fsP0PfTi+(m)֕ Et6 VkЬn} >bjt_`'GazXi dRkRF7(wxK$]&Qg-Uꬲ&ձf(g'';G'lgn_\I7Y-߼zu1wO;j}z~py;|Yn|p7̦rNNtpnX/jCzz^]-6zN|v ub1Cbgӳrqͽ;N-8b|y:s.*"^=_97O_oWKIj?ɇ\_r7f'2?9Φ'W@l>V9 X({r?[!G1gA^c n4o"^_|@ 2B)=zH֎H-#q0| B+}5}s=N4Z4krӬ 2BG)V } H* {K $.7^ӷ=Go|^~<_\})r|z[JpdgqWC.A Qa>g;~|s>݂*_G6 c2!2!2!2!S&SfSfSfSfSfSfSfSfS.t((P4h@рE (Pաπ;%(J!@Q1(eF5ŨFjĨFjĨFjĔŔŔŔŔ)WS\Mr wh@рE (P4h@р E'Bmhw(e4fbx oM3iQhF2#Af 1aєwP (P4h@рE}u(_9/  endstream endobj 2066 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZMo7W^v9$gHF| 8-FR)6}(Hd} `x8a|"]1/H" BC*NBQ+%A|HjG$3: 5/vPI-qN"Ex s#Ūscpi"  _|6A蓶>7.Q+UmLmJ^D#֬H⒯ 1 QRq)f^{$I{\E{9 Aѱg 1T9 ,Scٱ68ΩͭKָ ⩭k\$`adT'LꭢM8*R5~W{ ٻLE{Q9МRkx9zr+.תb-ѸJj&Nꭐ+un H+unIsᲆƥQtH^UBëNQX[EvzMU6KmrW$&EW`Vn\(D\qV@LmJRiJ&2kQDE 8(&jԑOWУ"Fa[Țp V:՞"[ Ik3RNHb|Q,̍? y,W.`7>ܼl^//W.NS.wvrrfs''n[*g"ee9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); wJ{ endstream endobj 2198 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺdNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺdOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream x[Mo7W^v9$F| 8-FR)6}C(Hd} #.qHArTwRt񻐣4> P5`WdWkѠ8x:OYVF2Gg4T)rDm,^FicA(.ȑ0@ >6qf+E4j"HML:UE):.yim$(5fcp=.ԞJmO+T :7ǞuI4[&1r85ױ99.:A]'國JQ'.*2fqH!xHT?JQ5J MJJMUPVɕWDt=0骋(a55+ uSsic+1⪰E݉:и@0 Nث.XMj1u^p~lWm_ߏOƇB%|ف#kZ~Hx|PXrzI.L X9|N|gu[HꇦH1Elރ{y6Wj\ as5ldCΆ 9r6lِ!gCΆ\"ꦨn)ꦨn)ꦨn|):2Ea{H?" )fBb&foٛjfo!WC\ Y Y Y Y Y Y&dMQ7EuSMQ7EuSMQ7E_ _Gc;-SCS1EiD -(T &{_ɐɐɐɐɐɐɐ!Cw7LQ즨n)ꦨn)ꦨn(GU endstream endobj 2277 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xZn[7+fnx! Em /T[R +m=Ci4MM" `s8sf8#GcP#iOl$x1y*-)PEHǬVwf ,S(VPU)ԬRj'_!(rss$G̝IFÇr>j@[T\(y)z+jrSa5%(q3VPjEBٷކh0TWeQQ"R{oB&سX84jb)Ĺ3J,mES'E$=HX26W8ƄT,ժant&]*}FfKʥf*96We_J %P*J5]ڨ!B{]/mKT\,@46ڸjZ\@%AK{S znj*(GQOX o賴Z/$t_kZ Ьn}}>7IG]Y-:K(CB &PQwYђMްRMM{Oq#ä{꘵>;:M a?駟X\eqG/_4=\\O^/S7zIqݛ|fj#9:Zt8Yl xi:]ِyN|PG3Xn͵yzz}j`Iy=50uuBӎ| d-d\l{*~nfD3 6C(fr2lِ!gCΆ 9r6dގy'=tMjb6=X/,>}?=9s)|\Raopm o|:] '߯7Wej}Cx%wvIdt4LO-.Dl>V9HqF9L`<!R(] <8["5)u#"H>L5F'-Hs-Sq%; BJsAFr|"pBZrB^-up; q);"## Ś=2qOr"8ߛm|"8\wDx?oߏ>29ef׏wٗba_-/o6G83!sp =^/?R'{s+J SV"794N"ƻHElڃM{^{aS5bFLՈ1U#jĐŐŐŐŐ!C.\ r wHEC Q4DEC Q4DEC}mQ{XtESm?=^{jjijf͐!7Cn r!;#Q(!(h!(h!((OEEʝ0L!!fbF1Ð!GC 9r4hѐ!w7DQh!(h!(h.v_W endstream endobj 2383 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺo7/ٌzի|vtTIPzL oF:d?Kjll4gie|ug6`73NEXl%vU<Yq\?S'6 )ԘجM* x{nPMPzO=M >P.|`V^c_a*ZVXbs'ÇjRH.ƙ3~h}&bޫwN=y!r++); wR{ endstream endobj 2462 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺlNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xMo7<.Nm>5|PE`ĕYiw; &4e}g8CR41 #`JH&*F6%bj-bTCGe!e W#DS!XP ~k{D9ç,Z$E >.Te E/\1(MX HJcUjٛEtDm 9XPcrj11vXjȢgKb$"M R)HK4);y$Jlfj#;yj2Ca,P=$1OD c9,U "5')T%()qZiZRxJMj0YdϒΦƖ{F(Fͥ=(s,ctd9,oK,)a,)NV2= \E,#,"8TbJQĔGK)MAn@v$cLWK5G(aXk7HHU&Un@p #g$YGU鮈GEoZ1!o>ɕ7_MJE@Dk@aʡf$Qʄbxj, ?ŶL";.6N֫9:2 x)A_ f}y:n9l|5u8(^ ڎe`1oo7mzy~g bU)ɜ.,W-S3A,7PƎS$xZJmFQW#Q*GUU9rRI-mR׫׋x7-Yb~x><9ɴK,ǶYF7c˲[9M5gkWzU뷒E1x%~b<6p:<_6^AH; $xE/d \;[s0T=J㈖kmrkK}0`gm_pK px$G_ܾ})ۖgp+ww-yWp|rHzrs?.ԕf(!* zϺAAQt(*P=GޱRMVJ5Y&+dTSTrQE*U.\TrCQE:u(PԡCQ EhC( ˡ(N_|EU!*TVaVaVaVaUfUYY4 RëԈj$5fe@E:u(PԡCQE|E{hC(@r& R57^+x^*{UU٫rPA_= (E:u(PԡCQE|+S0 endstream endobj 2489 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺlOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xM7sl/~6[Z!ҥ!C ʆ~A B` ďx?%\2yxʌkPOr{KZELǬS?,b,S QV RY=qЪH1Uf0Ja`t>j%J7$Q a`Cġ@[MTJMtQM9I0DID|O3e)#Q&qhM*q >)%h^ Z|yL%2HT*\ApЄN3XUmh>"EJ$Ş5&ŃUUoB"QAJ*5{O5$a9LkyHu8,ZG#BxHsqp R|k- x@o)׎!ٕ|q*D+%AF}@:}R=N4Z4kw@N=2R## #'Ͷoy>N 8m o>29r~K10W88ns#;p-uWO;(0sj=s>"HA@Q1(CjĨFjĨFjĨFjĔŔŔ)WS\Mr5j-!(J (P4h@рE}m(=C;-(~i>e4fbx oö͝f$3lF1C̨fr0`CCP (P4h@рE (PaPt`EEE+ BAH47&DÛhx o)'SNL9r2dɔ)'Sn@(P4h@рE (Pաh?Js' endstream endobj 2594 0 obj << /Length 986 /Filter /FlateDecode >> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺNn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2S( vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\ַBӏ7~tfN:_"Fd C(kXR$t ܰH6'_K< YgzsԤX jRQ{FְZdr0d77pxXS>9'ؑ6zTy(tD ֺOn^7+$Wc訒(@Dߌt, NL ;<ՕQr6bh|l c1ogmbu> stream xXn1 SJL+TBBb۽!hVl9 ;3N2-vw&۱?q2{XҠ/TG@:$`?fpĬ 4_cjO2; {\?VWo̜t$E@M?)B3 `QʥHpaA%lNx!)af`noFN4}SsN#mL1Pjr Ty0 v }w]8E|Ah+^TLDrtEI(VDEDiyYQRAP;!_d[ !3P$H+gnsƹ0=+N;N ],J;<@M./j &?CFx=dK?*K!h ²҆F4q )C-ᑴRd!d,l+Ua@M+{2TZ!j8re +5W:aJY%J! ŗkkŇ-fo%K|[[g^^_r0˼?u&$Jy7UD22W {Ge,?=r0= ,OuDq6SN]}8u'_f5^ FH` SZ] m-W1?F8h+V}Ǝ#+g#<=(7&6k$9(ըIӽmţfB)Tc\⭈OyCԮy !4<1?CW ֢A;UVZԀ+Ρ r&L8ZdOp}4f.` yqp:D*莯Czq|% endstream endobj 2570 0 obj << /Type /ObjStm /N 100 /First 1016 /Length 1513 /Filter /FlateDecode >> stream x[]S7}ϯ㏱-҇.mWFhMP6 *6ll%K N|3㱯BFBL)%Sj$ I [*Z4bڙEf&̀q)HsaExQkHY[cAʬQMvDQ$# mU 'OچELPCO1si[yDɷކ|E(ŢJA|Z)IOR)ZP))27.95ck&̴B>j^j%󨍄$($\5˜un8@x1hUT#{*1"Ro X|V HL5`i+P (. K TscOUJ( P[ֱc1tiQt,2Dj:_s%(VEA8{!P= 0+GikKلXZoU{ 6j"c?sC}:QEmgzD4:D]zD}g@2`~܋PwY>7D U+ZB8ns{3"J}vr296`@ӏ?LW˗{3MW/WW퐧f^R~fYtZn䄦S,YnnX/dŠWg )Mo6dQgZh,7Fk5fz}z`5H~Ve :i'b-rHsPspj@@0 $Ɯ9s6lbbbbbe4Iͦ׿lﮗ/fӃjӷӓ\h/8(kz~-0:ݷt>^=[ '߮7׫e5S?.ō} Itd:]\Hh| r3ﵤrL yGR(ӑ\k:rBk#[N #8%xNZ8[õ(2JrRR| )͉;!Ȉ WH.THëOVny".䑑tbMa/ r!8ߛm|![nB"x>9eǃK10W{q@æ@v&̮||OWOzI3%j||̪wA>:)S>]SFbޣ({a)jbj1Wc\s5j՘17cn2L0E S4L0E S4L0E_C;-SMOo{;h @1P 333333333sHwa)ha)ha)(G?lWS"ai fBބfoٛh&fo1Gc9s2dɘ1'cn6L0E S4L0E S4L0E_??d endstream endobj 2659 0 obj << /Length1 1695 /Length2 8588 /Length3 0 /Length 9685 /Filter /FlateDecode >> stream xڍ4k6 Jw;ѣ^Qf0[5D/QC-E M99}[Z<{kZL-k+n>8@^COx@~ff}=!AHjUOOXODb< /@ 807_#?6[vYWh`WdF[ @f #|+#&r<ٹ#@ {x[h\ƃ w4 0p؂p'@f詨? w@p\@P_`qy>.jrÐ /dQ:$!;? C\~; r̊P;y+OEݗu¼!P;my@!`0H?:0 E;c;#o5@78bFp /0 %>>;@8DG̀H, A]|qļF*rQNp bbb!1@A*_*P{@jcO^Q`w,M`?<7 m?|l俣_y)yag\!.!D @\BnV P +A|v|So{\ P6 }!yFnT~0"/$Cxy0l/`}""^ߪ?$Q WoI i- x[II^"2?[#േx T<= qGD2Y9j%!cFVd_!/%_9m WȚްB#!$=zzx ?yp`-V"̩6Fڛ{csy-:Ѥ7J20Sy]ڮb0M{,bl*zǼMR곷ry?:v0*QGI:4ffFSw"][U?[b{W@%}*5"N6m-քiĜ ,S_`'Z,$J%f_ uW WFS LIF#ENR,Y[7JG6::i^Zzn8;wJkj/W,Q-ؑZJfҩQ"Řֲv?;@B^r:8T/t v(l7jrٳObN? ~jE]e{nssL;1W5}sGgv{"w"oF}ΔӨRl:!NOz &d{^bssym{֑ڙ@5N~$''zJףZaG]K=|<'0Hr+˼ˎႉ(S~Ek.0+hA|Ϝ)Ư#k.r)L=圌›5~ ]s0i,䪈 Bu'K .AkTqo,zah1u9&\P&.E:y 7hԚ]S^o}{iSOia|u"p38ߗ^8x%YuZMtS_ZH*<\~홃ݞF羫֜VNk3LZFcywW&T_1@jņs)iG3r{ G~J3CVͪA!;D&$KGwV~UD_]p&1?h? 3\T\]V*RTTK뵸Lq=ǞA^X^A_`ւC_sS*d]+>R2cYEݻu|nO—ʗQUvm Ɵ#ld]lU I{;gq~;6jVkpsxsLe&'X6cNl+$d.8D;mr/*,iԐ{~ 2ߡBlw -yTrةOi?&!%DgL\:=(|v8Ez]{8eAUiWdHW7$ sǗt&zvyAN6DY$?mHSԢ/jT"]vC0 tkxY[hIۻ}Q1\>dZj^=81|FRDQC_rdx玳!(?ءe*4t__\>H0Iiκb$O7j5<}RVe+oJm抟'UVUND\h炍mNѳmDzWu!7ΡY\;.z\ΝSܞ76Mt aSMߍޑ5G;(VJXWʉ۩Xp"R_P4HIk*5GOB۾ͷPxBH.iŸɍ_y2mƩPmYYb IcCm%<[I=u;^_|XZSaceOċy;yP~v]P /0kLਫ_]*!1S|*{ӒSDQl(89ţՠRPIq ١2?g%-1;6/vq1>d%;G+e*lu\AQÃ,o5cͿ2jPz$#Hp2ľso=L?!|78;^D1PL[Q%N2Y֮/M^ob#`)Ď3YiqF_SO]4رPNY\1p/@-zR*Q9GDt}aqS 21/,v~1)Ϯ)e Ʃv]]ROW0maM]X[2?26g VgZ!Lz)1K3yo~kկܺTxZe,]֖4@q {Ddc{象4UFRG{ոKĥ w:wu0;6ʨt,RT?hؤ 4~6tߓ}g[Yү+TbC |,O;p*Dwڏb!a&Cy^EK98<m3*23@bi4\~zTŝ甩I6 #%*_y^EK'ʻdʙ I0̚ΜSrL%I, 99#PaAוy dwmŻ:n`e3N ɍGql*` oT23"%̒ ]k4cf@bg#\9*S "ıgR=i׼(Ʉ^d SJ}/:`s>bB=_;Py'whbb=@ItIt\"*nTK@%6vf!!$.VlF5'CuR\K8[SI کK"G_]QX@Xl)o۪.xN;S/h`MoZfJL_/=`) a9oIj_3wÎAs:4AP*;M\JȮO?wKmrmiT47T"uX,#碗Z#+H uǒo'`1D׷awyfGjM1{

;j-2&ŌF3:߮d.oaEe=a_Y)οnV(K$rU`Պ HD[)Bsƥ6TXĬ÷Չ.]n4bKń>ڸ 2ꈚ[UϪmk,Žz HyDH11etؽq-|ƈGU;{k)MbOVeљaзexBnl!jJLe\|>7c CP)f@ͬ8( ˓|uѣ=[E(]m4ɳ.>~ݲ/kgmWvϳG` # qF&h?y2v_|moFISӻ'w,<칤ywʸ|qBɻKO="Xc K;N,XvMr01"Ų$|;֎n%/y߆Xf ];g%vߘq 0Ŧ$I6lv $ {s{VE'YO;1cq̒?#׾Gu[xo\0I_Boe_P<C_ե ^Zפ) V rؔ"s {/a7(!ϟ66own>0$n8*?XX?yEښ1[|PTE ^K{wJ 5%ɛNZ?tU"lo< cTT 8ksh/e ͛'E*?%S"qӍxVqf?irOAkyQ N;A2ም(CD_$웎K SE/Eah4h =Ūh3b}lihַ=4]fͼH[kwpk* bm7Ng[_%d֏J;=tcK6|sBa,!C?(%a4$7v[Ck&xtCXʼ-$ar^㟴eeL'gԞ˚1 QYǙj a~N̸/%L^4uQEiȯyh.e#uO۹,圡"נTB%-G+fk@!?'frӱ~c<~ bTh, ޼Jp0_4R @P/'\,(q+ ZbDb/6LCŦQkv}c"[ϲG 揉0tI-_pK9^Hr(Vs8Fe_6y4pKĪ [I-4Lu]/w*RTlTkŨ3%O`ۤ.&l@ ^[3cŕUleP @<)Oa9RRE%y,kJQq'ↆkQ =uNޮWH zuteiaQW\W3Gh /ޝ۸?3ΥT g!t^EMЃuoHʝJOσpNlf*ce2suGzYqTNG;iw2 ;ƪ4gY&ͯ imC(=E;$0!vY8;]3z`{rmeL)J8my2@1s՟TXv&ycc)ftH<~hy2 LP8ۈ)9KC3*jr"[uvCשDFuEyL.db/4"k\ڢ?XQyWZ\$2K K|ju+匭tQj>i'd~8}&x Pk&v4jDf}Y;F Va ln|8ׯ$cCtlsȿ M|}ѤW2u;sXI AOmaؚF72~&!~{ |t:xɩFy NsL!IR(ꪹ`wg~[×_w<~"@`oq,jOT&1 zAŃkB,έ=.OW'cwS 0ߓ>}{ӘOx?|4ȶ\4d̕ҡٴfro'5.4qA=QD@/I7b2W'tJ Z=f^㸍j]*,!\̊IS(O{Tsw)*PF4}M=p=g ^#TVlndK̊ɜDIjIy,bϟR1U:ƥ=SثF`9$LNVis1Ȫbe9Z.Zr#{:D%\/ΚCD-}PUT mjaֲ?S`J[ϰT!c ~c1  \@ endstream endobj 2661 0 obj << /Length1 1450 /Length2 6764 /Length3 0 /Length 7749 /Filter /FlateDecode >> stream xڍtTTm6tx膡CiNaffhAK@Bi)EJBZ:>}y_gsξ}_`3P"m`Hg HP64@ QAHF 9L`n(8Y``5 F_}w , KKʀ@$"MP{ p Cr(!]ve pCxaiiIဂ ;`=zE"!p_)eh!!OOOAJfw tN? rFpikQPp8`;_ѿ! l0@WUK_D0{5w`@UA_7W{(DZzUJH''3E>e rBNa wj"d wui(E`h@ID+ Jo_wtlmaB_aoPX!hfw&'5 c_ 0]kOz|T4 tOQ H" @$" HJ*O-S6bpΥV- G q%-!7@vsnZF^S 6 wwo|= vR{zp4Zƿ w!Q_ z WZ]*$׈K`777!_[ ,Ba^E  :#!u{-ҍ׉BHw_o@Lv!i@PBhO?|)@Ww7Vc`0/$r'MHI BKdiW8 _{-L1WEkh9]Tq#ɐE[VlzCkQqGe~dL\$ǜ+yd+y-fPpDƒʈUn6ea_@ZQi_`EbO2ffz>t쨐(ϒݟMOu- SQH8YxƋ>͛U5F:;4Et,X4ob+}+1pnP ,?vum1zaäx)̫˙ѥD|XCm՟j-84oݫn8DeR FVX3:8uav *!8&Se0#&yN$ !IE!=ʆ\ L~9$71Zď#Epo3T"qԇv| c{H>ʲ'7c㋜=1ެݘu51kNɴRnxM ?S<BDݯD s ^ՆiD+ѸNn4m|`(C1@rM<^eV*ެ!tSRM.5;& nK$?`RF&Xl{hE6{ѻWEٯs3 < c_ymac4SdSCδq Bds:]<{cüpi+rǁ:T,/!kqKد9b] {n~Hj6q=Iץs)awGn2ՂC =2@C[Iט3L{H=X` HV'/ evYROͤDZZjzbϋ#JIX:qIHGޭ="JM-rJi.G ._Ώ/X߫djy>A=9V"WTcX!^Ta\DzD<qeh{פRB/ )V~И <%c?`8'iFӹTw;_oW7!<:k I*>Ӭđ| !;u!޷Ze^eN̕pD9 |Je+2u VU@YgN>9TIϘtL9)yLeN8kd &KP .gf@>ٓ{;BjɆmWSE7P#O5np[ q$DQ{`V t1.m:\;8?<=~̈́94k-z'IRS9^ifLTNCbRs@Z1nﳛUړXb[enW^[uG+lWt@Qan&drp8ڎubk8YHiX le(<2% ?ku$C:,'w`{/fT5![קX_kl0is;'"^AwW;OH[.-j|v_ǼuISƨܝ=R k0G(|GbO]p~~d|VAHǧf/#hR*k;jvmqJH,q'H,9g(-#R_=H)w w: ˚UTuHŅZQPH^Dtʉ~Δ\eDka)pH4.h4H]c3ew0.kjξ rDc Ґ0vm$t %Ou-`eFݹ:o۾苑 aP ޝ8b =y~4m '7ZFqn:[ozIlv" ߈v5}$LHXР;Jmtq[㪄~#gwuh&ZPףEN+E<]k3voN[*Rߋj'#bҜwqώ]B\e|Bo+i%p[ i7'ڧUdŬ}ssr岉ڱQDp44Շ+| +HF0l-~Έyk@z3(> BҶ2W^']jm[r2q}5✋7Q)״O-3t#jsaCCBe[ ȒuKӛTMu =74na2dh,~sgT1áE)M33T`Yi2Nr!y~uvz{A%JOѤS30ڡwb ̝ϲe;io+te%4yQ62$y>ـ0 !ϑM%DDb|]A d1=n5ז%N;Jjҵ|BګuofG#*QӞ"VD8yxn .W׳ h帬i=.)Ex߁]Lռfݻu/LC2ȈxO0ެL _Y.=i{ћrHLBc>mnlx] zqcGEj鋮:qVEJ.#9=^o$kJ@^nwQ܄r~4 f:}X2FA\*tRWӱaqp/)wWc H +-hǰu"m ܆'UO3Klw,@Bhr\GL(~tV*eg2p;-{kO$xYdZoCfxmcU'*_O(>oHyFw >Րk [yLT;1W$w\ kS>MBv奧he|rqFoq`T)7 J"K^Isq{9biz'H5^4~vCA !a˹WBrf&*gQb\¶WI>;?,f~bmLNDž#.mcGԌa0뵖J?=)Yqd&}П$;+W̅$C"EEXt1rf3W~˅x,Dʽ7bړÙR'2?}_fV*>Do%Xk%YsGKP_jl=X!_8e ?sߵSS)*)H=3i 5#Nc_FdUfH|-aPďZեy3Mx\`֭ h>I`2d2X~ϋvSy e\ IiTZai E.=tsp5δarWűT=¡+SLMʷ[D7=G^]sw)kc? endstream endobj 2710 0 obj << /Author()/Title(Many, Many, Many pages.)/Subject()/Creator(LaTeX with Beamer class version 3.36)/Producer(pdfTeX-1.40.16)/Keywords() /CreationDate (D:20160404141717+02'00') /ModDate (D:20160404141717+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015/Debian) kpathsea version 6.2.1) >> endobj 2656 0 obj << /Type /ObjStm /N 75 /First 722 /Length 3924 /Filter /FlateDecode >> stream xڥmo7Sx 0ė^ѧny:# [ɥoY %% D3%i1 &k`Õ4Xcȃ8 Izh0ֺ#7.! !)g"jH,g3~(N?4YXlR:2> ?y`7?h6Au,,;6eٍ%c AZL~-Ha&<qy bn ބZcž{'ч'?]^e֤,]Ζwoʯ<}˳.ybd$߿=2+`8izӃdxk)/qf(?6h|_JVAְ9>_o j7̣իm_O=\p^]a*d߮^$kfLIĆ y/7s & KT-/vR1oSye=L˝WFt `JGNPJH2Xed)$*#}r*#`"[)l߬i*C0 (ʍtřyLkHK\j\9qZ+@]NCz+u0aLT1f(ɱZ ]ENlO$ P3%W\(kV 壀r|Is*4H#JŞ4Vji",jk1c4`6А,7<=ըN:[4pq)?w:kv+kC\>\~WA#F\5rS!îDl2v>tyN̼^҂NKHͩ(O " ~9#2 zK?&5Z5|t|纻Pwf1ww?j؊P-wρ3~z|S^]ul*B1W:uwn;qF_ݝԷ ؚ1܍Nucr7mD[fuqޢwʧ? 2h]C :R2IfFtJ@S{0 c:IgۭǤs&aR7may~gt r1ho~כ!UF']Pz1ݪ2/3YkGeaܦ. = vSD<A80`db;QEs܇nNFs? \@Yayp{0;(y.jG/GPtDz`M8y7)ts1ot`.}0<{nܗl~/N`NSu  j80 ̩z3J)ws0)',Z#n.wb3:ou -[So%r V`ő=ps] g0wR3|`IX냛4w\~&0`ASAnoߊ CG8勺|9AG7vKazz u\|݃3˜EK=R#nln# ׸I{L9P#Gz8Rf*[[F5r0uj\吰a09L4.Ѹ#D {#uc}K)n]]uCB j`hPOl94OIi_Rۻ+ML I/~-TSr}*miY_σVRഎo,x`)NymRRI^ kKZ^fv2Rzn䕺fWjg,Ԛ\AM}jMϠ> &gPӄZ3iB4gd jsiZ 4Pkr?&jM'POBjPkv5H5{n$Ԛ]@Mj>@ f' PӭZ\-KK6?.ol(?,v endstream endobj 2711 0 obj << /Type /XRef /Index [0 2712] /Size 2712 /W [1 3 1] /Root 2709 0 R /Info 2710 0 R /ID [ ] /Length 5569 /Filter /FlateDecode >> stream xw&{ZNOns9==&T,AE,bM֢˲P&Qw wuaL?X?_{~y=M3mrbz<)3dĬ2[Ȕ̕_71XkF2)3d̒2GdLO,>ħX|b'X|b'X|b'gF8Lѳ/M,*a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#Y+;FF`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H L `H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H`H  `H" "c "ێn¹DtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDt#0 0݀a~̔Y2[Ȕ̕i'e,EXRY&ea ~넫UN(dlM)d쑽O9(kepù%:wtHq9!'唜3rVy \+rUu!7 "nC[rOy(]D|fC"*"*"*"*"*"*"*"*"*"*"*_EDEDEDEDEDEDEDEDED5r 0 ⛾mEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDED5xp.0o0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0T0TϭPFX b )d̑)+2OY(d,L Y)DT pJz el-Uv!;e=W~9 e ">kղF19.'䤜rF99/\rE5.7Aķ5LpX-'z6V:Y/dlͲE6.;dݲG>/䠬ApjY#rRNi9#g圜 rQ.e"W\rSʀg~?LpX-'fM (Ȁa2Sfl#S2We̗PbY"Ke,R`(>34Lѳ6ݜrA6&,[dlCv.-{dr@Zr8wFrBN)9-g䬜rA.%,W\rCnQDarDn=/&-wtÏ~1LC:`?v7 C^a"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"R"Ay "%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"%"D/ HHHD/>z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"z"Aį ~000000000000000000000000G0>AfL%eL\y2_BY$e,e\VJ!DƗ=D?Ξ*Y'elMYV&e][^'2WղF19.'䤜rF99/\rE5.7AW?LpX-']D^&-~FL1uL5azLxho9ɯ]x[8Ʊ5qlck[8Ʊ5qlck[8ƱE}jc9T,olyc[Ɩ7-olyc[Ɩ7-olyc[؎ƷG#wb8 endstream endobj startxref 169469 %%EOF dspdfviewer-1.15/testing/pdfs/many-many-pages.tex000066400000000000000000000007151270070303100221140ustar00rootroot00000000000000\documentclass[aspectratio=169]{beamer} \usepackage{color} \usepackage{pgfpages} \usepackage{pgffor} \setbeameroption{show notes on second screen} \title{Many, Many, Many pages.} \begin{document} \definecolor{8f8}{HTML}{88FF88} \definecolor{f8f}{HTML}{FF88FF} \foreach \n in {1,...,100}{ \begingroup \setbeamercolor{background canvas}{bg=8f8} \setbeamercolor{note page}{bg=f8f} \begin{frame} Page \thepage \end{frame} \endgroup } \end{document} dspdfviewer-1.15/testing/pdfs/png-image.png000066400000000000000000000026331270070303100207420ustar00rootroot00000000000000PNG  IHDRFrbKGD pHYs  tIME !}tEXtCommentTarget Color 20 99 180HIDATx10R 8"q%~  00``  00``  00``  00``  00``  00``  00``  00``  00``  00``   00``   00``   00``   00``   00``   00``   00``   00``   00``  000``  000``  000``  000``  000``  000``  000``  000``  000``  00``  00```  00```  00```  00```  00```  00```  00```  00```  00```  00``  00``  00``  00``  00``  00`` w CKl1IENDB`dspdfviewer-1.15/testing/test-images.cc000066400000000000000000000014751270070303100201700ustar00rootroot00000000000000#include "testhelpers.hh" using namespace std; using namespace TestHelpers; BOOST_AUTO_TEST_CASE(image_rendering) { PDFDocumentReference pdr( TestHelpers::pdfFilename("images.pdf"), PDFCacheOption::keepPDFinMemory ); auto first = RenderUtils::renderPagePart(pdr.page(0).page, QSize(1920,1080), PagePart::LeftHalf); auto second = RenderUtils::renderPagePart(pdr.page(1).page, QSize(1920,1080), PagePart::LeftHalf); auto firstColor = QColor( 0x70, 0x1e, 0xc1); auto secondColor = QColor( 0x14, 0x63, 0xb4); /** Check sizes of rendered images **/ BOOST_CHECK_EQUAL( QSize(1920,1080), first.size()); BOOST_CHECK_EQUAL( QSize(1920,1080), second.size()); /** Check pixels in the middle */ BOOST_CHECK_EQUAL( firstColor, QColor(first.pixel(960,540))); BOOST_CHECK_EQUAL( secondColor, QColor(second.pixel(960,540))); } dspdfviewer-1.15/testing/test-thumbnail-cmdline.cc000066400000000000000000000030401270070303100223050ustar00rootroot00000000000000#include "testhelpers.hh" // Test that the commandline accepts "left", "right" and "both", // but not another string. BOOST_AUTO_TEST_CASE(thumbnail_cmdline) { // correct arguments, left { const char * const argv[] = { "dspdfviewer", "--thumbnail-page-part", "left" }; RuntimeConfiguration rc{ 3, argv }; BOOST_CHECK_EQUAL( rc.thumbnailPagePart(), PagePart::LeftHalf ); } // shorthand, two words { const char * const argv[] = { "dspdfviewer", "-T", "left" }; RuntimeConfiguration rc{ 3, argv }; BOOST_CHECK_EQUAL( rc.thumbnailPagePart(), PagePart::LeftHalf ); } // shorthand, one word { const char * const argv[] = { "dspdfviewer", "-Tleft" }; RuntimeConfiguration rc{ 2, argv }; BOOST_CHECK_EQUAL( rc.thumbnailPagePart(), PagePart::LeftHalf ); } // right { const char* const argv[] = { "dspdfviewer", "--thumbnail-page-part", "right" }; RuntimeConfiguration rc{ 3, argv }; BOOST_CHECK_EQUAL( rc.thumbnailPagePart(), PagePart::RightHalf ); } { const char* const argv[] = { "dspdfviewer", "--thumbnail-page-part", "both" }; RuntimeConfiguration rc{ 3, argv }; BOOST_CHECK_EQUAL( rc.thumbnailPagePart(), PagePart::FullPage ); } // wrong argument { const char* const argv[] = { "dspdfviewer", "--thumbnail-page-part", "something" }; BOOST_CHECK_THROW( RuntimeConfiguration( 3, argv ) , std::logic_error ); } // no argument { const char* const argv[] = { "dspdfviewer", "--thumbnail-page-part" }; BOOST_CHECK_THROW( RuntimeConfiguration( 2, argv) , std::logic_error ); } } dspdfviewer-1.15/testing/testhelpers.cc000066400000000000000000000012211270070303100202750ustar00rootroot00000000000000#include "testhelpers.hh" #include QString TestHelpers::pdfFilename(const std::string& basename) { const QString relativePart = QString::fromLocal8Bit( basename.c_str() ); const QDir myDir = QDir::current(); if ( ! myDir.exists(relativePart) ) { throw std::runtime_error("Could not find the test pdf. This is bad."); } return myDir.filePath(relativePart); } std::ostream& operator << (std::ostream& where, const QSize& what) { return where << "QSize(" << what.width() << 'x' << what.height() << ')'; } std::ostream& operator << (std::ostream& where, const QColor& what) { return where << "QColor(" << qPrintable( what.name() ) << ")"; } dspdfviewer-1.15/testing/testhelpers.hh000066400000000000000000000036461270070303100203240ustar00rootroot00000000000000#pragma once /** Include all headers to access the dspdfviewer objects */ #include "../adjustedlink.h" #include "../debug.h" #include "../dspdfviewer.h" #include "../hyperlinkarea.h" #include "../pagepart.h" #include "../pdfcacheoption.h" #include "../pdfdocumentreference.h" #include "../pdfpagereference.h" #include "../pdfrenderfactory.h" #include "../pdfviewerwindow.h" #include "../renderedpage.h" #include "../renderingidentifier.h" #include "../renderthread.h" #include "../renderutils.h" #include "../runtimeconfiguration.h" #include "../sconnect.h" #include "../windowrole.h" #include #include #include namespace TestHelpers { /** Returns full path to the specified file */ QString pdfFilename( const std::string& basename ); /** Handle to a integration test environment. * Spawns an in-memory X-Server in the background and allows to send * keystrokes and inspect a certain pixel's color. */ class IntegrationTestEnv { public: /** * Starts a dummy X server and loads dspdfviewer with the * given command line arguments. */ IntegrationTestEnv( const std::vector & args); /** Send a keystroke and wait for a specified time * * See Qt::Key for the list of keys */ void sendKeyAndWait( const int& key, const std::chrono::milliseconds& t); /** Inspect pixel color */ QColor grabPixelColor( uint x, uint y); }; template inline void check(const T1& lhs, const T2& rhs, int failcode=2) { if ( ! (lhs == rhs) ) { WARNINGOUT << "Failure:" << lhs << "!=" << rhs; QApplication::exit(failcode); } else { DEBUGOUT << "All right." << lhs << "==" << rhs; } } } /** Print a QSize to a standard output stream */ std::ostream& operator << (std::ostream& where, const QSize& what); /** Print a QColor to a standard output stream */ std::ostream& operator << (std::ostream& where, const QColor& what); dspdfviewer-1.15/testing/testrenderonepage.cc000066400000000000000000000043241270070303100214600ustar00rootroot00000000000000#include "testhelpers.hh" using namespace std; using namespace TestHelpers; BOOST_AUTO_TEST_CASE(render_one_page) { PDFDocumentReference pdr( TestHelpers::pdfFilename("colored-rectangles.pdf"), PDFCacheOption::keepPDFinMemory ); const auto pageref = pdr.page(0); const auto left = RenderUtils::renderPagePart(pageref.page, QSize(1920,1080), PagePart::LeftHalf); const auto right = RenderUtils::renderPagePart(pageref.page, QSize(1920,1200), PagePart::RightHalf); const auto both = RenderUtils::renderPagePart(pageref.page, QSize(3840,1080), PagePart::FullPage); const auto leftScreenColor = QColor( 0x88, 0xff, 0x88); auto rightScreenColor = QColor( 0xff, 0x88, 0xff); /** Older texlive installation: Note page gets rendered white */ const auto fallbackRightScreenColor = QColor( 0xff, 0xff, 0xff); BOOST_CHECK_NE(rightScreenColor, fallbackRightScreenColor); /** Check sizes of rendered images **/ BOOST_CHECK_EQUAL( QSize(1920,1080), left.size()); BOOST_CHECK_EQUAL( QSize(1920,1080), right.size()); BOOST_CHECK_EQUAL( QSize(3840,1080), both.size()); /** Check middle pixel of left screen **/ BOOST_CHECK_EQUAL( leftScreenColor, QColor(left.pixel(960,540))); BOOST_CHECK_EQUAL( leftScreenColor, QColor(both.pixel(960,540))); const auto rightScreenMiddle = QColor(right.pixel(960,540)); BOOST_WARN_EQUAL( rightScreenColor, rightScreenMiddle ); /** Check middle pixel of right screen **/ if ( rightScreenMiddle == fallbackRightScreenColor) { BOOST_TEST_MESSAGE( "Middle of note screen was peak white, " ", using workaround for older latex-beamer versions."); rightScreenColor = fallbackRightScreenColor; } BOOST_CHECK_EQUAL( rightScreenColor, QColor(right.pixel(960,540))); BOOST_CHECK_EQUAL( rightScreenColor, QColor(both.pixel(2880,540))); /** Check all-the-way-left and all-the-way-right pixel colors */ BOOST_CHECK_EQUAL( leftScreenColor, QColor(left.pixel(0,540))); BOOST_CHECK_EQUAL( leftScreenColor, QColor(left.pixel(1919,540))); BOOST_CHECK_EQUAL( rightScreenColor, QColor(right.pixel(0,540))); BOOST_CHECK_EQUAL( rightScreenColor, QColor(right.pixel(1919,540))); BOOST_CHECK_EQUAL( leftScreenColor, QColor(both.pixel(0,540))); BOOST_CHECK_EQUAL( rightScreenColor, QColor(both.pixel(3839,540))); } dspdfviewer-1.15/testing/testrunner.cc000066400000000000000000000001411270070303100201440ustar00rootroot00000000000000 #define BOOST_TEST_MAIN #include "testhelpers.hh" /** Boost will generate a main() function */ dspdfviewer-1.15/testing/testswapscreen-main.cc000066400000000000000000000007471270070303100217430ustar00rootroot00000000000000#include "testswapscreen.h" #include "testhelpers.hh" int main(int argc, char** argv) { QApplication qapp(argc,argv); qRegisterMetaType< QSharedPointer >("QSharedPointer"); RuntimeConfiguration rc{argc, argv}; DSPDFViewer dsp{rc}; SwapScreensAndCheckAlign testdriver{dsp}; sconnect(&testdriver, SIGNAL(screenSwapRequested()), &dsp, SLOT(swapScreens())); sconnect(&testdriver, SIGNAL(quitRequested()), &dsp, SLOT(exit())); return qapp.exec(); } dspdfviewer-1.15/testing/testswapscreen.cc000066400000000000000000000045351270070303100210200ustar00rootroot00000000000000#include "testswapscreen.h" #include "testhelpers.hh" #include #include using namespace std; using namespace TestHelpers; SwapScreensAndCheckAlign::SwapScreensAndCheckAlign(DSPDFViewer& d): dspdfviewer(d), screenPrimary( QApplication::desktop()->screenGeometry( 0 ) ), screenSecondary( QApplication::desktop()->screenGeometry( 1 ) ), verify(true) { DEBUGOUT << "Number of screens:" << QApplication::desktop()->numScreens() ; DEBUGOUT << "screen 0 [pri]:" << screenPrimary; DEBUGOUT << "screen 1 [sec]:" << screenSecondary; if ( QApplication::desktop()->numScreens() != 2 ) { WARNINGOUT << "Not running in a dual-screen environment"; verify = false; } else if ( screenPrimary == screenSecondary ) { WARNINGOUT << "Cannot tell the screens apart"; verify = false; } else if ( screenPrimary.width() == 0 || screenPrimary.height() == 0 ) { WARNINGOUT << "Cannot query primary screen size"; verify = false; } else if ( screenSecondary.width() == 0 || screenSecondary.height() == 0 ) { WARNINGOUT << "Cannot query secondary screen size"; verify = false; } if ( ! verify ) { WARNINGOUT << "Disabling verification."; } // 1 Second time to boot QTimer::singleShot(1000, this, SLOT(checkStartPositions())); } void SwapScreensAndCheckAlign::checkStartPositions() { DEBUGOUT << "Start positions"; if ( verify ) { check(dspdfviewer.audienceGeometry(), screenSecondary); // Audience starts on external check(dspdfviewer.secondGeometry(), screenPrimary); // Presenter starts on internal } DEBUGOUT << "Firing screen swap event"; emit screenSwapRequested(); // 0.5 sec to swap QTimer::singleShot(500, this, SLOT(checkAfterFirstSwap())); } void SwapScreensAndCheckAlign::checkAfterFirstSwap() { DEBUGOUT << "Check after first swap"; if ( verify ) { check(dspdfviewer.audienceGeometry(), screenPrimary); // Audience window on laptop check(dspdfviewer.secondGeometry(), screenSecondary); // Presenter window on beamer } emit screenSwapRequested(); // 0.5 sec time to swap QTimer::singleShot(500, this, SLOT(checkAfterSwapBack())); } void SwapScreensAndCheckAlign::checkAfterSwapBack() { DEBUGOUT << "Check after swapping back"; if ( verify ) { check(dspdfviewer.audienceGeometry(), screenSecondary); check(dspdfviewer.secondGeometry(), screenPrimary); emit quitRequested(); } else { QCoreApplication::exit(77); } } dspdfviewer-1.15/testing/testswapscreen.h000066400000000000000000000013011270070303100206460ustar00rootroot00000000000000#pragma once #include "dspdfviewer.h" /** Test driver * * If it detects two screens, it will verify that the windows switch to the * specified positions. * If running on single-screen, the commands will be executed without verification, * just to make sure the code doesn't segfault or similar. * * Rinse and repeat 3 times. * */ class SwapScreensAndCheckAlign: public QObject { Q_OBJECT DSPDFViewer& dspdfviewer; QRect screenPrimary; QRect screenSecondary; bool verify; public: SwapScreensAndCheckAlign(DSPDFViewer& app); signals: void screenSwapRequested(); void quitRequested(); public slots: void checkStartPositions(); void checkAfterFirstSwap(); void checkAfterSwapBack(); }; dspdfviewer-1.15/testing/xorg.conf000066400000000000000000000040031270070303100172530ustar00rootroot00000000000000# xorg.conf for Xdummy, to test dspdfviewer # # inspired by http://xpra.org/xorg.conf Section "ServerFlags" Option "DontVTSwitch" "true" Option "AllowMouseOpenFail" "true" Option "PciForceNone" "true" Option "AutoEnableDevices" "false" Option "AutoAddDevices" "false" EndSection Section "Module" Disable "glx" Disable "dri" Disable "dri2" Disable "dbe" Disable "record" SubSection "extmod" Option "omit XFree86-DGA" EndSubSection EndSection Section "InputDevice" Identifier "dummy_mouse" Option "CorePointer" "true" Driver "void" EndSection Section "InputDevice" Identifier "dummy_keyboard" Option "CoreKeyboard" "true" Driver "void" EndSection Section "Device" Identifier "dummy_videocard" Driver "dummy" Option "ConstantDPI" "true" VideoRam 192000 EndSection Section "Monitor" Identifier "internal_monitor" HorizSync 5.0 - 1000.0 VertRefresh 58.0 - 62.0 Option "PreferredMode" "1920x1080" # 1920x1080 59.96 Hz (CVT 2.07M9) hsync: 67.16 kHz; pclk: 173.00 MHz Modeline "1920x1080_60.00" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync EndSection Section "Monitor" Identifier "external_monitor" Option "RightOf" "internal_monitor" HorizSync 5.0 - 1000.0 VertRefresh 58.0 - 62.0 Option "PreferredMode" "1920x1200" # 1920x1200 59.88 Hz (CVT 2.30MA) hsync: 74.56 kHz; pclk: 193.25 MHz Modeline "1920x1200_60.00" 193.25 1920 2056 2256 2592 1200 1203 1209 1245 -hsync +vsync EndSection Section "Screen" Identifier "internal_screen" Device "dummy_videocard" Monitor "internal_monitor" DefaultDepth 24 SubSection "Display" Modes "1920x1080_60.00" Depth 24 EndSubSection EndSection Section "Screen" Identifier "external_screen" Device "dummy_videocard" Monitor "external_monitor" DefaultDepth 24 SubSection "Display" Modes "1920x1200_60.00" Depth 24 EndSubSection EndSection Section "ServerLayout" Identifier "TheLayout" Screen 0 "internal_screen" Screen 1 "external_screen" RightOf "internal_screen" InputDevice "dummy_mouse" InputDevice "dummy_keyboard" EndSection dspdfviewer-1.15/translations/000077500000000000000000000000001270070303100164745ustar00rootroot00000000000000dspdfviewer-1.15/translations/dspdfviewer_de.ts000066400000000000000000000503051270070303100220410ustar00rootroot00000000000000 DSPDFViewer Starting constructor Starte Konstruktor I was not able to open the PDF document. Sorry. Das PDF-Dokument konnte leider nicht geöffnet werden. Connecting audience window Verbinde Zuschauerfenster Connecting secondary window Verbinde Sekundärfenster Requesting rendering of page %1 Berechnung von Seite %1 angefordert Requested page number %1 which is out of range! Ignoring request. Die angeforderte Seitennummer %1 liegt außerhalb des gültigen Bereichs! Die Anfrage wird ignoriert. Dual-Screen PDF Viewer has encountered an error and cannot continue DS PDF Viewer hat einen Fehler festgestellt und kann nicht weiter ausgeführt werden You did not specify a PDF-File to display. Sie haben keine anzuzeigende PDF-Datei ausgewählt. Form DS PDF Viewer Programmnamen nicht übersetzen DS PDF Viewer The actual image goes here. Hier wird das eigentliche Bild angezeigt. 09:12:34 09:12:34 Next Page Nächste Seite Current Page Aktuelle Seite Previous Page Vorherige Seite Total 00:00:00 Gesamt 00:00:00 00:00 00:00 Total %1 Gesamt %1 KeybindingsDialog Keybindings Tastenkombinationen Key Taste Action Aktion Next slide Nächste Folie N or Left/Down arrow N oder Pfeil nach links/unten P or Right/Down arrow P oder Pfeil nach rechts/unten Blank/Unblank audience screen Zuschauerfenster schwärzen/anzeigen Quit Schließen Previous slide Vorige Folie Go to specific slide Zu einer bestimmten Folie gehen Toggle between notes and slides on the secondary screen Zwischen Notizen und Folien auf dem Sekundärfenster umschalten Toggle between displaying both notes and slides on the secondary screen or only notes Nur Notizen/Folien oder beides auf dem Sekundärfenster anzeigen Switch primary and secondary screens Primär- und Sekundärbildschirm vertauschen Go to first page and reset counters Zur ersten Seite gehen und Uhren zurücksetzen Show this help box Diese Hilfe anzeigen B or . B oder . G G H or Home H oder Pos1 Q or Esc Q oder Escape S or F12 S oder F12 T T D D ? or F1 ? oder F1 OK OK dspdfviewer version %1 DS PDF Viewer Version %1 PDFViewerWindow Loading page number %1 Lade Seitennummer %1 HH:mm:ss This is used by QTime::toString. See its documentation before changing this. HH:mm:ss DS PDF Viewer - Audience Window User visible Window Title Line DS PDF Viewer - Zuschauerfenster DS PDF Viewer - Secondary Window DS PDF Viewer - Sekundärfenster Select page Seite auswählen Jump to page number (%1-%2): Springe zu Seitennummer (%1-%2): QFileDialog Load PDF from disk Lade PDF von Datenträger PDF (*.pdf) PDF (*.pdf) RenderUtils RenderUtils::renderPagePart called with null page. Target size was %1x%2 RenderUtils::renderPagePart mit einer Null-Seite aufgerufen. Zielgröße war %1x%2 RuntimeConfiguration Generic options Allgemeine Optionen Print help message Hilfemeldung ausgeben Print version statement Versionsmeldung ausgeben Options affecting program behaviour Optionen, die das Programmverhalten beeinflussen Display the full slide on both screens (useful for PDFs created by presentation software other than latex-beamer) Die gesamte Folie auf beiden Bildschirmen anzeigen (nützlich für PDFs, die mit anderen Präsentationsprogrammen erstellt wurden als LaTeX-Beamer) Pre-render the preceding arg slides NOTE: If you set this to zero, you might not get a thumbnail for the previous slide unless it was loaded already. Die vorangehenden arg Folien rendern Hinweis: Wird dieser Wert auf Null gesetzt, kann eine Vorschau der vorangehenden Folien nur angezeigt werden, wenn diese bereits geladen wurden. Pre-render the next arg slides NOTE: If you set this to zero, you might not get a thumbnail for the next slide unless it was loaded already. Die nächsten arg Folien rendern Hinweis: Wird dieser Wert auf Null gesetzt, kann eine Vorschau der vorangehenden Folien nur angezeigt werden, wenn diese bereits geladen wurden. Support PDF Hyperlinks Follow hyperlinks when clicked (mouse pointer will change to a pointing hand) - set this to false if you cannot reliably control your mouse pointer position and want to always go ahead one slide on click. PDF-Hyperlinks unterstützen Hyperlinks verfolgen, wenn diese angeklickt werden (der Cursor wird sich zu einer Hand verändern) - auf false setzen, wenn die Cursorposition nicht zuverlässig kontrolliert werden kann und stattdessen bei jedem Klick stets eine Folie weitergegangen werden soll. Cache the PDF file into memory Useful if you are editing the PDF file with latex while using the presenter software. PDF-Datei im im Speicher cachen Nützlich, wenn die PDF-Datei in LaTeX bearbeitet wird, während sie präsentiert wird. Size of the cache for pre-rendered pages, in megabytes. Größe des Zwischenspeichers für im Voraus berechnete Folien, in Megabytes. Use i3 specific workaround: Execute shellcode once both windows have been created. I3-spezifische Funktionsweise verwenden: Shell-Code ausführen, sobald beide Fenster erstellt wurden. Options affecting the second screen Optionen, die nur den zweiten Bildschirm betreffen Use the second screen. If you only have one monitor and just want to use this application as a fast, pre-caching PDF viewer you might want to say 0 here. NOTE: Whatever you say on -a, -t, -w, -s or -p doesn't matter if you set this to false. NOTE: You might want to say -f if you set this to false. Zweiten Bildschirm verwenden. Wenn Sie nur einen Monitor einsetzen und dieses Programm als schnellen, im voraus ladenden PDF-Betrachter verwenden möchten, können Sie hier 0 angeben. Hinweis: Die Optionen -a, -t, -w, -s und -p sind unerheblich, wenn dieser Wert auf false gesetzt wird. Hinweis: Vermutlich möchten Sie -f verwenden, wenn dieser Wert auf false gesetzt wird. Shows or hides the complete "presenter area" on the second screen, giving you a full-screen note page. NOTE: Whatever you say on -t, -w, -s or -p doesnt matter if you set this to false. Steuert die Anzeige des gesamten Präsentationsbereichs auf dem zweiten Bildschirm, was die Anzeige einer Vollbild-Notizseite ermöglicht. Hinweis: Die Optionen -t, -w, -s und -p sind unerheblich, wenn dieser Wert auf false gesetzt wird. Duplicates the audience's screen next to the notes on the second screen. Dupliziert das Zuschauerfenster neben den Notizen auf dem zweiten Bildschirm. Show thumbnails of previous, current and next slide Zeigt Vorschauen der vorangehenden, aktuellen und nachfolgenden Folie Thumbnails show this page part. Valid values are "left", "right" or "both" Vorschaubilder zeigen diesen Seitenteil. Gültige Werte sind "left", "right" oder "both" Show the wall clock Zeigt die Uhrzeit Show the presentation clock Zeigt die vergangene Gesamtzeit Show the slide clock Zeigt die Zeit pro Folie Percentage of second screen to use for the bottom pane Prozentanteil des zweiten Bildschirms, der für den unteren Bereich verwendet wird Hidden options Weitere Optionen PDF File to display Anzuzeigende PDF-Datei Written by Danny Edel Copyright (C) 2012 Danny Edel. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Erstellt von Danny Edel Copyright (C) 2012 Danny Edel. Diese Software ist frei; Weitergabebedingungen können im Quellcode nachgelesen werden. Es wird KEINERLEI GARANTIE übernommen; nicht einmal für TAUGLICHKEIT oder die EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Usage: %1 [options] pdf-file Verwendungn: %1 [optionen] PDF-Datei Interactive Controls: Press F1 or ? during program execution to get a quick overview about available controls. Please read the manpage (man dspdfviewer) for the full list. Please try to keep line length below 70 chars and use (tab) for padding Interaktive Tasten: Drücken Sie F1 oder ? während der Programmausführung, um einen kurzen Überblick über die verfügbaren Tastenkombinationen zu erhalten. Bitte lesen Sie die Manpage (man dspdfviewer), um die vollständige Liste zu erhalten. Invalid height in specified. Please use a value from 1 to 99 (inclusive) Ungültige Höhe angegeben. Bitte verwenden Sie einen Wert von 1 bis einschließlich 99 dspdfviewer-1.15/windowrole.cpp000066400000000000000000000003621270070303100166510ustar00rootroot00000000000000#include "windowrole.h" #include QString to_QString( const WindowRole& wr) { if( wr == WindowRole::AudienceWindow ) { return QString::fromUtf8("Audience_Window"); } else { return QString::fromUtf8("Secondary_Window"); } } dspdfviewer-1.15/windowrole.h000066400000000000000000000002741270070303100163200ustar00rootroot00000000000000#pragma once #include enum class WindowRole { AudienceWindow, PresenterWindow }; // Return "Audience_Window" or "Secondary_Window" QString to_QString ( const WindowRole& );