diff options
author | Michael Brüning <michael.bruning@qt.io> | 2020-01-27 14:45:07 +0100 |
---|---|---|
committer | Michal Klocek <michal.klocek@qt.io> | 2020-01-27 13:54:33 +0000 |
commit | eda1b89972f8a3c87d8929ec4bcea20aabb78ab6 (patch) | |
tree | 87d7f98634b1f072cbcfb95998dc1850d7ac7491 | |
parent | fe9de69e014457de39ddb09c5773f546613e9727 (diff) | |
parent | a06650f819ef164e6ec83d69957bd60d77574a16 (diff) |
Merge remote-tracking branch 'origin/wip/qtpdf' into 5.15
Initial merge of QtPdf into QtWebEngine.
Fixes: QTBUG-69519
Change-Id: I48dc25a59f2c161bb231bd0fa60392eb70fe4e7d
129 files changed, 9407 insertions, 51 deletions
diff --git a/LICENSE.FDL b/LICENSE.FDL new file mode 100644 index 000000000..086177c0a --- /dev/null +++ b/LICENSE.FDL @@ -0,0 +1,450 @@ + GNU Free Documentation License + Version 1.3, 3 November 2008 + + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The "publisher" means any person or entity that distributes copies of +the Document to the public. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +11. RELICENSING + +"Massive Multiauthor Collaboration Site" (or "MMC Site") means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +"Massive Multiauthor Collaboration" (or "MMC") contained in the site +means any set of copyrightable works thus published on the MMC site. + +"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +"Incorporate" means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is "eligible for relicensing" if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 new file mode 100644 index 000000000..e200fb5b2 --- /dev/null +++ b/LICENSE.LGPLv3 @@ -0,0 +1,174 @@ + GNU LESSER GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + You may use, distribute and copy the Qt Toolkit under the terms of + GNU Lesser General Public License version 3, which is displayed below. + This license makes reference to the version 3 of the GNU General + Public License, which you can find in the LICENSE.GPLv3 file. + +------------------------------------------------------------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> +Everyone is permitted to copy and distribute verbatim copies of this +licensedocument, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + + As used herein, “this License” refers to version 3 of the GNU Lesser +General Public License, and the “GNU GPL” refers to version 3 of the +GNU General Public License. + + “The Library” refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An “Application” is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A “Combined Work” is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the “Linked +Version”. + + The “Minimal Corresponding Source” for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The “Corresponding Application Code” for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort + to ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this + license document. + +4. Combined Works. + + You may convey a Combined Work under terms of your choice that, taken +together, effectively do not restrict modification of the portions of +the Library contained in the Combined Work and reverse engineering for +debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of + this License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (a) uses at run + time a copy of the Library already present on the user's + computer system, and (b) will operate properly with a modified + version of the Library that is interface-compatible with the + Linked Version. + + e) Provide Installation Information, but only if you would + otherwise be required to provide such information under section 6 + of the GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the Application + with a modified version of the Linked Version. (If you use option + 4d0, the Installation Information must accompany the Minimal + Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in + the manner specified by section 6 of the GNU GPL for conveying + Corresponding Source.) + +5. Combined Libraries. + + You may place library facilities that are a work based on the Library +side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of + it is a work based on the Library, and explaining where to find + the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 Library +as you received it specifies that a certain numbered version of the +GNU Lesser General Public License “or any later version” applies to +it, you have the option of following the terms and conditions either +of that published version or of any later version published by the +Free Software Foundation. If the Library as you received it does not +specify a version number of the GNU Lesser General Public License, +you may choose any version of the GNU Lesser General Public License +ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the Library. diff --git a/configure.json b/configure.json index 8caad24f0..a5dbad35b 100644 --- a/configure.json +++ b/configure.json @@ -3,6 +3,8 @@ "src/buildtools", "src/core", "src/webengine", - "src/webenginewidgets" + "src/webenginewidgets", + "src/pdf", + "src/pdfwidgets" ] } diff --git a/configure.pri b/configure.pri index 3a144e3f8..f89df5273 100644 --- a/configure.pri +++ b/configure.pri @@ -114,7 +114,7 @@ defineTest(qtwebengine_platformError) { defineTest(qtConfTest_detectPlatform) { QT_FOR_CONFIG += gui-private - !linux:!win32:!macos { + !linux:!win32:!macos:!ios { qtwebengine_platformError("Unknown platform. Qt WebEngine only supports Linux, Windows, and macOS.") } else { linux:qtwebengine_isLinuxPlatformSupported() { @@ -126,6 +126,9 @@ defineTest(qtConfTest_detectPlatform) { macos:qtwebengine_isMacOsPlatformSupported() { $${1}.platform = "macos" } + ios:qtwebengine_isMacOsPlatformSupported() { + $${1}.platform = "ios" + } } !isEmpty(platformError) { @@ -423,21 +426,21 @@ defineTest(qtwebengine_isWindowsPlatformSupported) { defineTest(qtwebengine_isMacOsPlatformSupported) { # FIXME: Try to get it back down to 8.2 for building on OS X 10.11 !qtwebengine_isMinXcodeVersion(8, 3, 3) { - qtwebengine_platformError("Using Xcode version $$QMAKE_XCODE_VERSION, but at least version 8.3.3 is required to build Qt WebEngine.") + qtwebengine_platformError("Using Xcode version $$QMAKE_XCODE_VERSION, but at least version 8.3.3 is required to build Qt WebEngine or Qt Pdf.") return(false) } !clang|intel_icc { - qtwebengine_platformError("Qt WebEngine on macOS requires Clang.") + qtwebengine_platformError("Qt WebEngine and Qt Pdf requires Clang.") return(false) } # We require macOS 10.12 (darwin version 16.0.0) or newer. darwin_major_version = $$section(QMAKE_HOST.version, ., 0, 0) lessThan(darwin_major_version, 16) { - qtwebengine_platformError("Building Qt WebEngine requires macOS version 10.12 or newer.") + qtwebengine_platformError("Building Qt WebEngine or Qt Pdf requires macOS version 10.12 or newer.") return(false) } !qtwebengine_isMinOSXSDKVersion(10, 12): { - qtwebengine_platformError("Building Qt WebEngine requires a macOS SDK version of 10.12 or newer. Current version is $${WEBENGINE_OSX_SDK_PRODUCT_VERSION}.") + qtwebengine_platformError("Building Qt WebEngine or Qt Pdf requires a macOS SDK version of 10.12 or newer. Current version is $${WEBENGINE_OSX_SDK_PRODUCT_VERSION}.") return(false) } return(true) diff --git a/examples/examples.pro b/examples/examples.pro index 3dac9b0b7..b4f411aeb 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -3,3 +3,7 @@ TEMPLATE=subdirs qtHaveModule(webengine): SUBDIRS += webengine qtHaveModule(webenginewidgets): SUBDIRS += webenginewidgets + +qtHaveModule(pdfwidgets): SUBDIRS += pdfwidgets + +qtHaveModule(quick): qtHaveModule(pdf): qtHaveModule(pdfwidgets): SUBDIRS += pdf diff --git a/examples/pdf/pdf.pro b/examples/pdf/pdf.pro new file mode 100644 index 000000000..45df33e46 --- /dev/null +++ b/examples/pdf/pdf.pro @@ -0,0 +1,3 @@ +TEMPLATE=subdirs + +SUBDIRS += pdfviewer diff --git a/examples/pdf/pdfviewer/main.cpp b/examples/pdf/pdfviewer/main.cpp new file mode 100644 index 000000000..6b94a3de1 --- /dev/null +++ b/examples/pdf/pdfviewer/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + QCoreApplication::setApplicationName("Qt Quick PDF Viewer Example"); + QCoreApplication::setOrganizationName("QtProject"); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:///pdfviewer/viewer.qml"))); + if (app.arguments().count() > 1) { + QUrl toLoad = QUrl::fromUserInput(app.arguments().at(1)); + engine.rootObjects().first()->setProperty("source", toLoad); + } + + return app.exec(); +} diff --git a/examples/pdf/pdfviewer/pdfviewer.pro b/examples/pdf/pdfviewer/pdfviewer.pro new file mode 100644 index 000000000..697349cee --- /dev/null +++ b/examples/pdf/pdfviewer/pdfviewer.pro @@ -0,0 +1,14 @@ +TEMPLATE = app + +QT += qml quick pdf widgets + +SOURCES += main.cpp + +RESOURCES += \ + viewer.qrc +EXAMPLE_FILES = \ + viewer.qml + +target.path = $$[QT_INSTALL_EXAMPLES]/pdf/pdfviewer +INSTALLS += target + diff --git a/examples/pdf/pdfviewer/resources/document-open.svg b/examples/pdf/pdfviewer/resources/document-open.svg new file mode 100644 index 000000000..bf23123a3 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/document-open.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="m4 4v24h24l-1-1h-22v-13h5l3-3h14v16l1 1v-21h-10l-3-3z" + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/edit-clear.svg b/examples/pdf/pdfviewer/resources/edit-clear.svg new file mode 100644 index 000000000..1c35aaf04 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/edit-clear.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 8 3 L 0.94335938 10.056641 L 0 11 L 0.94335938 11.943359 L 8 19 L 20.333984 19 L 22 19 L 22 3 L 20.333984 3 L 8 3 z M 11.320312 7 L 14 9.6796875 L 16.679688 7 L 18 8.3203125 L 15.320312 11 L 18 13.679688 L 16.679688 15 L 14 12.320312 L 11.320312 15 L 10 13.679688 L 12.679688 11 L 10 8.3203125 L 11.320312 7 z " + class="ColorScheme-Text" + transform="translate(1,1)" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/go-next-view-page.svg b/examples/pdf/pdfviewer/resources/go-next-view-page.svg new file mode 100644 index 000000000..e453ddbec --- /dev/null +++ b/examples/pdf/pdfviewer/resources/go-next-view-page.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 8.7070312 4 L 8 4.7070312 L 14.125 10.832031 L 15.292969 12 L 14.125 13.167969 L 8 19.292969 L 8.7070312 20 L 14.832031 13.875 L 16.707031 12 L 14.832031 10.125 L 8.7070312 4 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/go-previous-view-page.svg b/examples/pdf/pdfviewer/resources/go-previous-view-page.svg new file mode 100644 index 000000000..b032309e9 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/go-previous-view-page.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 15.292969 4 L 9.1679688 10.125 L 7.2929688 12 L 9.1679688 13.875 L 15.292969 20 L 16 19.292969 L 9.875 13.167969 L 8.7070312 12 L 9.875 10.832031 L 16 4.7070312 L 15.292969 4 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/rotate-left.svg b/examples/pdf/pdfviewer/resources/rotate-left.svg new file mode 100644 index 000000000..90ce53c9d --- /dev/null +++ b/examples/pdf/pdfviewer/resources/rotate-left.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"> + <g color="#000" font-weight="400" fill="#474747"> + <path d="M2 9v1c0 .265.093.53.281.719l3.72 3.719 3.718-3.72c.188-.187.281-.453.281-.718V9H9c-.265 0-.53.093-.719.281l-2.28 2.281-2.282-2.28A1.015 1.015 0 0 0 3 9z"/> + <path d="M8.5 3A3.515 3.515 0 0 0 5 6.5V12h2V6.5C7 5.66 7.66 5 8.5 5H13V3z"/> + </g> +</svg> diff --git a/examples/pdf/pdfviewer/resources/rotate-right.svg b/examples/pdf/pdfviewer/resources/rotate-right.svg new file mode 100644 index 000000000..7383d1c84 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/rotate-right.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"> + <g font-weight="400" fill="#474747"> + <path d="M3 3v2h4.5C8.34 5 9 5.66 9 6.5V12h2V6.5C11 4.579 9.421 3 7.5 3z"/> + <path d="M6 9h1c.257 0 .529.13.719.313L10 11.592l2.281-2.28C12.471 9.13 12.743 9 13 9h1v1c0 .31-.09.552-.281.75L10 14.406 6.281 10.75C6.091 10.552 6 10.31 6 10z"/> + </g> +</svg> diff --git a/examples/pdf/pdfviewer/resources/zoom-in.svg b/examples/pdf/pdfviewer/resources/zoom-in.svg new file mode 100644 index 000000000..efdc9f17d --- /dev/null +++ b/examples/pdf/pdfviewer/resources/zoom-in.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 4 4 L 4 6 L 5 6 L 5 5 L 6 5 L 6 4 L 4 4 z M 9 4 L 9 5 L 11 5 L 11 4 L 9 4 z M 13 4 L 13 5 L 15 5 L 15 4 L 13 4 z M 18 4 L 18 5 L 19 5 L 19 6 L 20 6 L 20 4 L 18 4 z M 12 8 L 12 9 L 14.292969 9 L 11 12.292969 L 11.707031 13 L 15 9.7070312 L 15 12 L 16 12 L 16 8 L 15 8 L 12 8 z M 4 9 L 4 11 L 5 11 L 5 9 L 4 9 z M 19 9 L 19 11 L 20 11 L 20 9 L 19 9 z M 19 13 L 19 15 L 20 15 L 20 13 L 19 13 z M 4 14 L 4 20 L 10 20 L 10 14 L 4 14 z M 5 15 L 9 15 L 9 19 L 5 19 L 5 15 z M 19 18 L 19 19 L 18 19 L 18 20 L 20 20 L 20 18 L 19 18 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/zoom-original.svg b/examples/pdf/pdfviewer/resources/zoom-original.svg new file mode 100644 index 000000000..1b4080a03 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/zoom-original.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 4 4 L 4 5 L 4 7 L 5 7 L 5 5 L 7 5 L 7 4 L 5 4 L 4 4 z M 17 4 L 17 5 L 19 5 L 19 7 L 20 7 L 20 5 L 20 4 L 19 4 L 17 4 z M 6 6 L 6 18 L 18 18 L 18 6 L 6 6 z M 7 7 L 17 7 L 17 17 L 7 17 L 7 7 z M 4 17 L 4 19 L 4 20 L 7 20 L 7 19 L 5 19 L 5 17 L 4 17 z M 19 17 L 19 19 L 17 19 L 17 20 L 20 20 L 20 17 L 19 17 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/resources/zoom-out.svg b/examples/pdf/pdfviewer/resources/zoom-out.svg new file mode 100644 index 000000000..fcde9e526 --- /dev/null +++ b/examples/pdf/pdfviewer/resources/zoom-out.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 4 4 L 4 11 L 5 11 L 5 5 L 19 5 L 19 19 L 13 19 L 13 20 L 20 20 L 20 19 L 20 5 L 20 4 L 5 4 L 4 4 z M 15.292969 8 L 12 11.292969 L 12 9 L 11 9 L 11 13 L 12 13 L 15 13 L 15 12 L 12.707031 12 L 16 8.7070312 L 15.292969 8 z M 4 14 L 4 16 L 5 16 L 5 15 L 6 15 L 6 14 L 4 14 z M 8 14 L 8 15 L 9 15 L 9 16 L 10 16 L 10 14 L 8 14 z M 4 18 L 4 20 L 6 20 L 6 19 L 5 19 L 5 18 L 4 18 z M 9 18 L 9 19 L 8 19 L 8 20 L 10 20 L 10 18 L 9 18 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/pdfviewer/viewer.qml b/examples/pdf/pdfviewer/viewer.qml new file mode 100644 index 000000000..adc2a4b5b --- /dev/null +++ b/examples/pdf/pdfviewer/viewer.qml @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Pdf 5.15 +import QtQuick.Shapes 1.15 +import QtQuick.Window 2.15 +import Qt.labs.platform 1.1 as Platform + +ApplicationWindow { + id: root + width: 800 + height: 640 + color: "lightgrey" + title: document.title + visible: true + property alias source: document.source // for main.cpp + property real scaleStep: Math.sqrt(2) + + header: ToolBar { + RowLayout { + anchors.fill: parent + anchors.rightMargin: 6 + ToolButton { + action: Action { + shortcut: StandardKey.Open + icon.source: "resources/document-open.svg" + onTriggered: fileDialog.open() + } + } + ToolButton { + action: Action { + shortcut: StandardKey.ZoomIn + enabled: pageView.sourceSize.width < 10000 + icon.source: "resources/zoom-in.svg" + onTriggered: pageView.renderScale *= root.scaleStep + } + } + ToolButton { + action: Action { + shortcut: StandardKey.ZoomOut + enabled: pageView.sourceSize.width > 50 + icon.source: "resources/zoom-out.svg" + onTriggered: pageView.renderScale /= root.scaleStep + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+0" + icon.source: "resources/zoom-original.svg" + onTriggered: pageView.renderScale = 1 + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+L" + icon.source: "resources/rotate-left.svg" + onTriggered: pageView.rotation -= 90 + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+R" + icon.source: "resources/rotate-right.svg" + onTriggered: pageView.rotation += 90 + } + } + ToolButton { + action: Action { + shortcut: StandardKey.MoveToPreviousPage + icon.source: "resources/go-previous-view-page.svg" + enabled: pageView.currentPage > 0 + onTriggered: pageView.currentPage-- + } + } + ToolButton { + action: Action { + shortcut: StandardKey.MoveToNextPage + icon.source: "resources/go-next-view-page.svg" + enabled: pageView.currentPage < pageView.pageCount - 1 + onTriggered: pageView.currentPage++ + } + } + TextField { + id: searchField + placeholderText: "search" + Layout.minimumWidth: 200 + Layout.fillWidth: true + Image { + visible: searchField.text !== "" + source: "resources/edit-clear.svg" + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + margins: 3 + rightMargin: 5 + } + TapHandler { + onTapped: searchField.clear() + } + } + } + Shortcut { + sequence: StandardKey.Find + onActivated: searchField.forceActiveFocus() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + } + + Platform.FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: document.source = file + } + + Dialog { + id: errorDialog + title: "Error loading " + document.source + standardButtons: Dialog.Ok + modal: true + closePolicy: Popup.CloseOnEscape + anchors.centerIn: parent + width: 300 + + Label { + id: errorField + text: document.error + } + } + + PdfPageView { + id: pageView + document: PdfDocument { + id: document + onStatusChanged: if (status === PdfDocument.Error) errorDialog.open() + } + searchString: searchField.text + } + + footer: Label { + property size implicitPointSize: document.pagePointSize(pageView.currentPage) + text: "page " + (pageView.currentPage + 1) + " of " + pageView.pageCount + + " scale " + pageView.renderScale.toFixed(2) + + " sourceSize " + pageView.sourceSize.width.toFixed(1) + "x" + pageView.sourceSize.height.toFixed(1) + + " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + visible: pageView.pageCount > 0 + } +} diff --git a/examples/pdf/pdfviewer/viewer.qrc b/examples/pdf/pdfviewer/viewer.qrc new file mode 100644 index 000000000..78f9c8d30 --- /dev/null +++ b/examples/pdf/pdfviewer/viewer.qrc @@ -0,0 +1,14 @@ +<RCC> + <qresource prefix="/pdfviewer"> + <file>viewer.qml</file> + <file>resources/edit-clear.svg</file> + <file>resources/go-next-view-page.svg</file> + <file>resources/go-previous-view-page.svg</file> + <file>resources/rotate-left.svg</file> + <file>resources/rotate-right.svg</file> + <file>resources/zoom-in.svg</file> + <file>resources/zoom-original.svg</file> + <file>resources/zoom-out.svg</file> + <file>resources/document-open.svg</file> + </qresource> +</RCC> diff --git a/examples/pdfwidgets/pdfviewer/images/busy.png b/examples/pdfwidgets/pdfviewer/images/busy.png Binary files differnew file mode 100644 index 000000000..69056c479 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/busy.png diff --git a/examples/pdfwidgets/pdfviewer/images/fileopen.png b/examples/pdfwidgets/pdfviewer/images/fileopen.png Binary files differnew file mode 100644 index 000000000..33e0d6394 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/fileopen.png diff --git a/examples/pdfwidgets/pdfviewer/images/go-next-24.png b/examples/pdfwidgets/pdfviewer/images/go-next-24.png Binary files differnew file mode 100644 index 000000000..9a55ef3d8 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/go-next-24.png diff --git a/examples/pdfwidgets/pdfviewer/images/go-previous-24.png b/examples/pdfwidgets/pdfviewer/images/go-previous-24.png Binary files differnew file mode 100644 index 000000000..2ea769eb8 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/go-previous-24.png diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png b/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png Binary files differnew file mode 100644 index 000000000..d29b142b6 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png b/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png Binary files differnew file mode 100644 index 000000000..34d70af37 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png b/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png Binary files differnew file mode 100644 index 000000000..19703474f --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png b/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png Binary files differnew file mode 100644 index 000000000..b83220661 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png diff --git a/examples/pdfwidgets/pdfviewer/main.cpp b/examples/pdfwidgets/pdfviewer/main.cpp new file mode 100644 index 000000000..20fa6f8cd --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include <QApplication> +#include <QUrl> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + QStringList args = a.arguments(); + w.show(); + if (args.length() > 1) + w.open(QUrl::fromLocalFile(args[1])); + + return a.exec(); +} diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.cpp b/examples/pdfwidgets/pdfviewer/mainwindow.cpp new file mode 100644 index 000000000..5f9bf389f --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/mainwindow.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include "pageselector.h" +#include "zoomselector.h" + +#include <QFileDialog> +#include <QMessageBox> +#include <QPdfBookmarkModel> +#include <QPdfDocument> +#include <QPdfPageNavigation> +#include <QtMath> + +const qreal zoomMultiplier = qSqrt(2.0); + +Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer") + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , m_zoomSelector(new ZoomSelector(this)) + , m_pageSelector(new PageSelector(this)) + , m_document(new QPdfDocument(this)) +{ + ui->setupUi(this); + + m_zoomSelector->setMaximumWidth(150); + ui->mainToolBar->insertWidget(ui->actionZoom_In, m_zoomSelector); + + m_pageSelector->setMaximumWidth(150); + ui->mainToolBar->addWidget(m_pageSelector); + + m_pageSelector->setPageNavigation(ui->pdfView->pageNavigation()); + + connect(m_zoomSelector, &ZoomSelector::zoomModeChanged, ui->pdfView, &QPdfView::setZoomMode); + connect(m_zoomSelector, &ZoomSelector::zoomFactorChanged, ui->pdfView, &QPdfView::setZoomFactor); + m_zoomSelector->reset(); + + QPdfBookmarkModel *bookmarkModel = new QPdfBookmarkModel(this); + bookmarkModel->setDocument(m_document); + + ui->bookmarkView->setModel(bookmarkModel); + connect(ui->bookmarkView, SIGNAL(activated(QModelIndex)), this, SLOT(bookmarkSelected(QModelIndex))); + + ui->tabWidget->setTabEnabled(1, false); // disable 'Pages' tab for now + + ui->pdfView->setDocument(m_document); + + connect(ui->pdfView, &QPdfView::zoomFactorChanged, + m_zoomSelector, &ZoomSelector::setZoomFactor); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::open(const QUrl &docLocation) +{ + if (docLocation.isLocalFile()) { + m_document->load(docLocation.toLocalFile()); + const auto documentTitle = m_document->metaData(QPdfDocument::Title).toString(); + setWindowTitle(!documentTitle.isEmpty() ? documentTitle : QStringLiteral("PDF Viewer")); + } else { + qCDebug(lcExample) << docLocation << "is not a valid local file"; + QMessageBox::critical(this, tr("Failed to open"), tr("%1 is not a valid local file").arg(docLocation.toString())); + } + qCDebug(lcExample) << docLocation; +} + +void MainWindow::bookmarkSelected(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + const int page = index.data(QPdfBookmarkModel::PageNumberRole).toInt(); + ui->pdfView->pageNavigation()->setCurrentPage(page); +} + +void MainWindow::on_actionOpen_triggered() +{ + QUrl toOpen = QFileDialog::getOpenFileUrl(this, tr("Choose a PDF"), QUrl(), "Portable Documents (*.pdf)"); + if (toOpen.isValid()) + open(toOpen); +} + +void MainWindow::on_actionQuit_triggered() +{ + QApplication::quit(); +} + +void MainWindow::on_actionAbout_triggered() +{ + QMessageBox::about(this, tr("About PdfViewer"), + tr("An example using QPdfDocument")); +} + +void MainWindow::on_actionAbout_Qt_triggered() +{ + QMessageBox::aboutQt(this); +} + +void MainWindow::on_actionZoom_In_triggered() +{ + ui->pdfView->setZoomFactor(ui->pdfView->zoomFactor() * zoomMultiplier); +} + +void MainWindow::on_actionZoom_Out_triggered() +{ + ui->pdfView->setZoomFactor(ui->pdfView->zoomFactor() / zoomMultiplier); +} + +void MainWindow::on_actionPrevious_Page_triggered() +{ + ui->pdfView->pageNavigation()->goToPreviousPage(); +} + +void MainWindow::on_actionNext_Page_triggered() +{ + ui->pdfView->pageNavigation()->goToNextPage(); +} + +void MainWindow::on_actionContinuous_triggered() +{ + ui->pdfView->setPageMode(ui->actionContinuous->isChecked() ? QPdfView::MultiPage : QPdfView::SinglePage); +} diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.h b/examples/pdfwidgets/pdfviewer/mainwindow.h new file mode 100644 index 000000000..afdfcade4 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/mainwindow.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QLoggingCategory> +#include <QMainWindow> + +Q_DECLARE_LOGGING_CATEGORY(lcExample) + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} + +class QPdfDocument; +class QPdfView; +QT_END_NAMESPACE + +class PageSelector; +class ZoomSelector; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public slots: + void open(const QUrl &docLocation); + +private slots: + void bookmarkSelected(const QModelIndex &index); + + // action handlers + void on_actionOpen_triggered(); + void on_actionQuit_triggered(); + void on_actionAbout_triggered(); + void on_actionAbout_Qt_triggered(); + void on_actionZoom_In_triggered(); + void on_actionZoom_Out_triggered(); + void on_actionPrevious_Page_triggered(); + void on_actionNext_Page_triggered(); + void on_actionContinuous_triggered(); + +private: + Ui::MainWindow *ui; + ZoomSelector *m_zoomSelector; + PageSelector *m_pageSelector; + + QPdfDocument *m_document; +}; + +#endif // MAINWINDOW_H diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.ui b/examples/pdfwidgets/pdfviewer/mainwindow.ui new file mode 100644 index 000000000..2651525a5 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/mainwindow.ui @@ -0,0 +1,281 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>700</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>PDF Viewer</string> + </property> + <property name="unifiedTitleAndToolBarOnMac"> + <bool>true</bool> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QTabWidget" name="tabWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="tabPosition"> + <enum>QTabWidget::West</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="documentMode"> + <bool>false</bool> + </property> + <widget class="QWidget" name="bookmarkTab"> + <attribute name="title"> + <string>Bookmarks</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>2</number> + </property> + <property name="topMargin"> + <number>2</number> + </property> + <property name="rightMargin"> + <number>2</number> + </property> + <property name="bottomMargin"> + <number>2</number> + </property> + <item> + <widget class="QTreeView" name="bookmarkView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="pagesTab"> + <attribute name="title"> + <string>Pages</string> + </attribute> + </widget> + </widget> + <widget class="QPdfView" name="pdfView" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>700</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionOpen"/> + <addaction name="actionQuit"/> + </widget> + <widget class="QMenu" name="menuHelp"> + <property name="title"> + <string>Help</string> + </property> + <addaction name="actionAbout"/> + <addaction name="actionAbout_Qt"/> + </widget> + <widget class="QMenu" name="menuView"> + <property name="title"> + <string>View</string> + </property> + <addaction name="actionZoom_In"/> + <addaction name="actionZoom_Out"/> + <addaction name="actionPrevious_Page"/> + <addaction name="actionNext_Page"/> + <addaction name="separator"/> + <addaction name="actionContinuous"/> + </widget> + <addaction name="menuFile"/> + <addaction name="menuView"/> + <addaction name="menuHelp"/> + </widget> + <widget class="QToolBar" name="mainToolBar"> + <property name="movable"> + <bool>false</bool> + </property> + <property name="floatable"> + <bool>false</bool> + </property> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + <addaction name="actionOpen"/> + <addaction name="separator"/> + <addaction name="actionZoom_Out"/> + <addaction name="actionZoom_In"/> + <addaction name="separator"/> + </widget> + <widget class="QStatusBar" name="statusBar"/> + <action name="actionOpen"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset> + </property> + <property name="text"> + <string>Open...</string> + </property> + <property name="shortcut"> + <string>Ctrl+O</string> + </property> + </action> + <action name="actionQuit"> + <property name="text"> + <string>Quit</string> + </property> + <property name="shortcut"> + <string>Ctrl+Q</string> + </property> + </action> + <action name="actionAbout"> + <property name="text"> + <string>About</string> + </property> + </action> + <action name="actionAbout_Qt"> + <property name="text"> + <string>About Qt</string> + </property> + </action> + <action name="actionZoom_In"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/icons/images/zoom-in-24.png</normaloff>:/icons/images/zoom-in-24.png</iconset> + </property> + <property name="text"> + <string>Zoom In</string> + </property> + <property name="shortcut"> + <string>Ctrl+=</string> + </property> + </action> + <action name="actionZoom_Out"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/icons/images/zoom-out-24.png</normaloff>:/icons/images/zoom-out-24.png</iconset> + </property> + <property name="text"> + <string>Zoom Out</string> + </property> + <property name="shortcut"> + <string>Ctrl+-</string> + </property> + </action> + <action name="actionPrevious_Page"> + <property name="text"> + <string>Previous Page</string> + </property> + <property name="shortcut"> + <string>PgUp</string> + </property> + </action> + <action name="actionNext_Page"> + <property name="text"> + <string>Next Page</string> + </property> + <property name="shortcut"> + <string>PgDown</string> + </property> + </action> + <action name="actionContinuous"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Continuous</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>QPdfView</class> + <extends>QWidget</extends> + <header location="global">qpdfview.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources> + <include location="resources.qrc"/> + </resources> + <connections/> +</ui> diff --git a/examples/pdfwidgets/pdfviewer/pageselector.cpp b/examples/pdfwidgets/pdfviewer/pageselector.cpp new file mode 100644 index 000000000..cd3c4ba0e --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/pageselector.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pageselector.h" + +#include <QHBoxLayout> +#include <QLabel> +#include <QLineEdit> +#include <QPdfPageNavigation> +#include <QToolButton> + +PageSelector::PageSelector(QWidget *parent) + : QWidget(parent) + , m_pageNavigation(nullptr) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + + m_previousPageButton = new QToolButton(this); + m_previousPageButton->setText("<"); + m_previousPageButton->setEnabled(false); + + m_pageNumberEdit = new QLineEdit(this); + m_pageNumberEdit->setAlignment(Qt::AlignRight); + + m_pageCountLabel = new QLabel(this); + m_pageCountLabel->setText("0"); + + m_nextPageButton = new QToolButton(this); + m_nextPageButton->setText(">"); + m_nextPageButton->setEnabled(false); + + layout->addWidget(m_previousPageButton); + layout->addWidget(m_pageNumberEdit); + layout->addWidget(m_pageCountLabel); + layout->addWidget(m_nextPageButton); +} + +void PageSelector::setPageNavigation(QPdfPageNavigation *pageNavigation) +{ + m_pageNavigation = pageNavigation; + + connect(m_previousPageButton, &QToolButton::clicked, m_pageNavigation, &QPdfPageNavigation::goToPreviousPage); + connect(m_pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged, m_previousPageButton, &QToolButton::setEnabled); + + connect(m_pageNavigation, &QPdfPageNavigation::currentPageChanged, this, &PageSelector::onCurrentPageChanged); + connect(m_pageNavigation, &QPdfPageNavigation::pageCountChanged, this, [this](int pageCount){ m_pageCountLabel->setText(QString::fromLatin1("/ %1").arg(pageCount)); }); + + connect(m_pageNumberEdit, &QLineEdit::editingFinished, this, &PageSelector::pageNumberEdited); + + connect(m_nextPageButton, &QToolButton::clicked, m_pageNavigation, &QPdfPageNavigation::goToNextPage); + connect(m_pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged, m_nextPageButton, &QToolButton::setEnabled); + + onCurrentPageChanged(m_pageNavigation->currentPage()); +} + +void PageSelector::onCurrentPageChanged(int page) +{ + if (m_pageNavigation->pageCount() == 0) + m_pageNumberEdit->setText(QString::number(0)); + else + m_pageNumberEdit->setText(QString::number(page + 1)); +} + +void PageSelector::pageNumberEdited() +{ + if (!m_pageNavigation) + return; + + const QString text = m_pageNumberEdit->text(); + + bool ok = false; + const int pageNumber = text.toInt(&ok); + + if (!ok) + onCurrentPageChanged(m_pageNavigation->currentPage()); + else + m_pageNavigation->setCurrentPage(qBound(0, pageNumber - 1, m_pageNavigation->pageCount() - 1)); +} diff --git a/examples/pdfwidgets/pdfviewer/pageselector.h b/examples/pdfwidgets/pdfviewer/pageselector.h new file mode 100644 index 000000000..7a7283f99 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/pageselector.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PAGESELECTOR_H +#define PAGESELECTOR_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QPdfDocument; +class QPdfPageNavigation; +class QToolButton; +QT_END_NAMESPACE + +class PageSelector : public QWidget +{ + Q_OBJECT + +public: + explicit PageSelector(QWidget *parent = nullptr); + + void setPageNavigation(QPdfPageNavigation *pageNavigation); + +private slots: + void onCurrentPageChanged(int page); + void pageNumberEdited(); + +private: + QPdfPageNavigation *m_pageNavigation; + + QLineEdit *m_pageNumberEdit; + QLabel *m_pageCountLabel; + QToolButton *m_previousPageButton; + QToolButton *m_nextPageButton; +}; + +#endif // PAGESELECTOR_H diff --git a/examples/pdfwidgets/pdfviewer/pdfviewer.pro b/examples/pdfwidgets/pdfviewer/pdfviewer.pro new file mode 100644 index 000000000..ad0607ea5 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/pdfviewer.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +TARGET = pdfviewer +QT += core gui widgets pdfwidgets + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + pageselector.cpp \ + zoomselector.cpp + +HEADERS += \ + mainwindow.h \ + pageselector.h \ + zoomselector.h + +FORMS += \ + mainwindow.ui + +RESOURCES += \ + resources.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/pdfwidgets/pdfviewer +INSTALLS += target diff --git a/examples/pdfwidgets/pdfviewer/resources.qrc b/examples/pdfwidgets/pdfviewer/resources.qrc new file mode 100644 index 000000000..02d9655b4 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/resources.qrc @@ -0,0 +1,12 @@ +<RCC> + <qresource prefix="/icons"> + <file>images/fileopen.png</file> + <file>images/go-next-24.png</file> + <file>images/go-previous-24.png</file> + <file>images/zoom-in-24.png</file> + <file>images/zoom-in-32.png</file> + <file>images/zoom-out-24.png</file> + <file>images/zoom-out-32.png</file> + <file>images/busy.png</file> + </qresource> +</RCC> diff --git a/examples/pdfwidgets/pdfviewer/zoomselector.cpp b/examples/pdfwidgets/pdfviewer/zoomselector.cpp new file mode 100644 index 000000000..0205489aa --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/zoomselector.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "zoomselector.h" + +#include <QLineEdit> + +ZoomSelector::ZoomSelector(QWidget *parent) + : QComboBox(parent) +{ + setEditable(true); + + addItem(QLatin1String("Fit Width")); + addItem(QLatin1String("Fit Page")); + addItem(QLatin1String("12%")); + addItem(QLatin1String("25%")); + addItem(QLatin1String("33%")); + addItem(QLatin1String("50%")); + addItem(QLatin1String("66%")); + addItem(QLatin1String("75%")); + addItem(QLatin1String("100%")); + addItem(QLatin1String("125%")); + addItem(QLatin1String("150%")); + addItem(QLatin1String("200%")); + addItem(QLatin1String("400%")); + + connect(this, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged), + this, &ZoomSelector::onCurrentTextChanged); + + connect(lineEdit(), &QLineEdit::editingFinished, + this, [this](){onCurrentTextChanged(lineEdit()->text()); }); +} + +void ZoomSelector::setZoomFactor(qreal zoomFactor) +{ + setCurrentText(QString::number(qRound(zoomFactor * 100)) + QLatin1String("%")); +} + +void ZoomSelector::reset() +{ + setCurrentIndex(8); // 100% +} + +void ZoomSelector::onCurrentTextChanged(const QString &text) +{ + if (text == QLatin1String("Fit Width")) { + emit zoomModeChanged(QPdfView::FitToWidth); + } else if (text == QLatin1String("Fit Page")) { + emit zoomModeChanged(QPdfView::FitInView); + } else { + qreal factor = 1.0; + + QString withoutPercent(text); + withoutPercent.remove(QLatin1Char('%')); + + bool ok = false; + const int zoomLevel = withoutPercent.toInt(&ok); + if (ok) + factor = zoomLevel / 100.0; + + emit zoomModeChanged(QPdfView::CustomZoom); + emit zoomFactorChanged(factor); + } +} diff --git a/examples/pdfwidgets/pdfviewer/zoomselector.h b/examples/pdfwidgets/pdfviewer/zoomselector.h new file mode 100644 index 000000000..c58d09970 --- /dev/null +++ b/examples/pdfwidgets/pdfviewer/zoomselector.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ZOOMSELECTOR_H +#define ZOOMSELECTOR_H + +#include <QComboBox> +#include <QPdfView> + +class ZoomSelector : public QComboBox +{ + Q_OBJECT + +public: + explicit ZoomSelector(QWidget *parent = nullptr); + +public slots: + void setZoomFactor(qreal zoomFactor); + + void reset(); + +signals: + void zoomModeChanged(QPdfView::ZoomMode zoomMode); + void zoomFactorChanged(qreal zoomFactor); + +private slots: + void onCurrentTextChanged(const QString &text); +}; + +#endif // ZOOMSELECTOR_H diff --git a/examples/pdfwidgets/pdfwidgets.pro b/examples/pdfwidgets/pdfwidgets.pro new file mode 100644 index 000000000..a12dc5b86 --- /dev/null +++ b/examples/pdfwidgets/pdfwidgets.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += pdfviewer diff --git a/mkspecs/features/functions.prf b/mkspecs/features/functions.prf index 512e2523b..db0b072a7 100644 --- a/mkspecs/features/functions.prf +++ b/mkspecs/features/functions.prf @@ -74,6 +74,7 @@ defineReplace(gnPath) { defineReplace(gnArgs) { linux: include($$QTWEBENGINE_ROOT/src/buildtools/config/linux.pri) macos: include($$QTWEBENGINE_ROOT/src/buildtools/config/mac_osx.pri) + ios: include($$QTWEBENGINE_ROOT/src/buildtools/config/ios.pri) win32: include($$QTWEBENGINE_ROOT/src/buildtools/config/windows.pri) isEmpty(gn_args): error(No gn_args found please make sure you have valid configuration.) return($$gn_args) diff --git a/mkspecs/features/gn_generator.prf b/mkspecs/features/gn_generator.prf index 863169c4d..6ff09d851 100644 --- a/mkspecs/features/gn_generator.prf +++ b/mkspecs/features/gn_generator.prf @@ -12,13 +12,18 @@ defineReplace(getTargetType) { defineReplace(filter_flag_values) { value_to_check = $$1 - macos:equals(value_to_check, "$(EXPORT_ARCH_ARGS)") { + if (macos|ios):equals(value_to_check, "$(EXPORT_ARCH_ARGS)") { # EXPORT_ARCH_ARGS comes from qtbase/mkspecs/features/mac/default_post.prf which is a way # to figure out the architectures to pass to the compiler at Makefile time. Because this # variable expansion is not supported by GN, we filter it out. GN takes care of assigning # the architecture itself. return("") } + if (ios) { + equals(value_to_check, "$(EXPORT_QMAKE_XARCH_LFLAGS)"): return("") + equals(value_to_check, "$(EXPORT_QMAKE_XARCH_CFLAGS)"): return("") + } + return($$value_to_check) } @@ -183,7 +188,7 @@ GN_CONTENTS += "$${TARGET_TYPE}(\"$$TARGET\") {" } GN_CONTENTS += " configs += [ \":$${TARGET}_config\" ]" -GN_CONTENTS += " configs += [ \"//build/config:precompiled_headers\" ]" +!isEmpty(GN_PRECOMPILED_HEADERS): GN_CONTENTS += " configs += [ \"//build/config:precompiled_headers\" ]" # Source files to compile GN_CONTENTS += " sources = [" @@ -239,11 +244,10 @@ GN_CONTENTS += " \":generate_cpp_mocs\"," GN_CONTENTS += " ]" GN_CONTENTS += " }" GN_CONTENTS += "}" -GN_CONTENTS += "" -GN_CONTENTS += "if (!defined(core_include_dirs)) {"\ - " core_include_dirs = []"\ - "}" !isEmpty(GN_CORE_INCLUDE_DIRS) { + GN_CONTENTS += " if (!defined(core_include_dirs)) {"\ + " core_include_dirs = []"\ + " }" GN_CONTENTS += "core_include_dirs += [" for (inc, GN_CORE_INCLUDE_DIRS): GN_CONTENTS += " \"$$inc\"," GN_CONTENTS += "]" diff --git a/src/buildtools/config/common.pri b/src/buildtools/config/common.pri index 97d39535c..9cd8cb089 100644 --- a/src/buildtools/config/common.pri +++ b/src/buildtools/config/common.pri @@ -39,7 +39,7 @@ greaterThan(QMAKE_JUMBO_MERGE_LIMIT,0) { gn_args += jumbo_build_excluded="[\"browser\"]" } -qtConfig(webengine-printing-and-pdf) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-printing-and-pdf) { gn_args += enable_basic_printing=true enable_print_preview=true gn_args += enable_pdf=true } else { @@ -47,27 +47,31 @@ qtConfig(webengine-printing-and-pdf) { gn_args += enable_pdf=false } -qtConfig(webengine-pepper-plugins) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-pepper-plugins) { gn_args += enable_plugins=true } else { gn_args += enable_plugins=false } -qtConfig(webengine-spellchecker) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) { gn_args += enable_spellcheck=true } else { gn_args += enable_spellcheck=false } -qtConfig(webengine-webrtc) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-webrtc) { gn_args += enable_webrtc=true } else { gn_args += enable_webrtc=false audio_processing_in_audio_service_supported=false } -qtConfig(webengine-proprietary-codecs): gn_args += proprietary_codecs=true ffmpeg_branding=\"Chrome\" +qtConfig(build-qtwebengine-core):qtConfig(webengine-proprietary-codecs) { + gn_args += proprietary_codecs=true ffmpeg_branding=\"Chrome\" +} else { + gn_args += proprietary_codecs=false +} -qtConfig(webengine-extensions) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-extensions) { gn_args += enable_extensions=true } else { gn_args += enable_extensions=false @@ -122,13 +126,13 @@ optimize_size: gn_args += optimize_for_size=true sanitize_undefined: gn_args += is_ubsan=true is_ubsan_vptr=true } -qtConfig(webengine-v8-snapshot):qtConfig(webengine-v8-snapshot-support) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-v8-snapshot):qtConfig(webengine-v8-snapshot-support) { gn_args += v8_use_snapshot=true } else { gn_args += v8_use_snapshot=false } -qtConfig(webengine-kerberos) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-kerberos) { gn_args += use_kerberos=true } else { gn_args += use_kerberos=false @@ -139,3 +143,4 @@ ccache { } qtConfig(force_asserts): gn_args += dcheck_always_on=true + diff --git a/src/buildtools/config/ios.pri b/src/buildtools/config/ios.pri new file mode 100644 index 000000000..5dc7faf9d --- /dev/null +++ b/src/buildtools/config/ios.pri @@ -0,0 +1,66 @@ +load(functions) + +include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) +include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) +QT_FOR_CONFIG += buildtools-private pdf-private + +clang_dir = $$which($${QMAKE_CXX}) +clang_dir = $$clean_path("$$dirname(clang_dir)/../") + +gn_args += \ +use_qt=true \ +closure_compile=false \ +is_component_build=false \ +is_shared=true \ +is_debug=true \ +enable_message_center=false \ +enable_nacl=false \ +enable_remoting=false \ +enable_reporting=false \ +enable_resource_whitelist_generation=false \ +enable_swiftshader=false \ +enable_web_speech=false \ +has_native_accessibility=false \ +enable_debugallocation=false \ +use_allocator_shim=false \ +use_allocator=\"none\" \ +use_custom_libcxx=false \ +v8_use_external_startup_data=false \ +v8_use_snapshot=false \ +toolkit_views=false \ +treat_warnings_as_errors=false \ +safe_browsing_mode=0 \ +optimize_webui=false \ +forbid_non_component_debug_builds=false \ +clang_use_chrome_plugins=false \ +use_xcode_clang=true \ +clang_base_path=\"$${clang_dir}\" \ +ios_enable_code_signing=false \ +target_os=\"ios\" \ +ios_deployment_target=\"$${QMAKE_IOS_DEPLOYMENT_TARGET}\" \ +enable_ios_bitcode=true \ +use_jumbo_build=false + +device:simulator { + # we do fat libray + gn_args+= \ + target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\" \ + use_qt_fat_lib=true \ + arm_use_neon=false\ + # note this adds one arch of simulator at the moment, see also additional_target_cpus + target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\" \ + additional_target_sysroot=[\"$$xcodeSDKInfo(Path, $$simulator.sdk)\"] +} else { + simulator { + equals(QMAKE_APPLE_SIMULATOR_ARCHS,"x86_64") { + gn_args+=target_cpu=\"x64\" + } else { + gn_args+=target_cpu=\"$${QMAKE_APPLE_SIMULATOR_ARCHS}\" + } + gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$simulator.sdk)\" + } + device { + gn_args+=target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\" + gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\" + } +} diff --git a/src/buildtools/config/linux.pri b/src/buildtools/config/linux.pri index ee08f81bc..add563f28 100644 --- a/src/buildtools/config/linux.pri +++ b/src/buildtools/config/linux.pri @@ -5,7 +5,7 @@ defineReplace(extractCFlag) { return($$qtwebengine_extractCFlag($$1)) } -QT_FOR_CONFIG += gui-private webenginecore-private +QT_FOR_CONFIG += gui-private webenginecore-private pdf-private gn_args += \ use_cups=false \ @@ -26,7 +26,7 @@ gn_args += \ ozone_platform=\"qt\" \ ozone_extra_path=\"$$QTWEBENGINE_ROOT/src/core/ozone/ozone_extra.gni\" -qtConfig(webengine-embedded-build) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-embedded-build) { gn_args += is_desktop_linux=false } @@ -92,7 +92,7 @@ contains(QT_ARCH, "arm") { } } - qtConfig(webengine-arm-thumb) { + qtConfig(build-qtwebengine-core):qtConfig(webengine-arm-thumb) { gn_args += arm_use_thumb=true # this adds -mthumb } else { gn_args += arm_use_thumb=false @@ -158,11 +158,15 @@ host_build { qtConfig(webengine-system-zlib) { qtConfig(webengine-system-minizip): gn_args += use_system_zlib=true use_system_minizip=true - qtConfig(webengine-printing-and-pdf): gn_args += pdfium_use_system_zlib=true + qtConfig(build-qtpdf) || qtConfig(webengine-printing-and-pdf) { + gn_args += pdfium_use_system_zlib=true + } } qtConfig(webengine-system-png) { gn_args += use_system_libpng=true - qtConfig(webengine-printing-and-pdf): gn_args += pdfium_use_system_libpng=true + qtConfig(build-qtpdf) || qtConfig(webengine-printing-and-pdf) { + gn_args += pdfium_use_system_libpng=true + } } qtConfig(webengine-system-jpeg) { gn_args += use_system_libjpeg=true @@ -180,19 +184,19 @@ host_build { gn_args += use_system_harfbuzz=false } gn_args += use_glib=false - qtConfig(webengine-pulseaudio) { + qtConfig(build-qtwebengine-core):qtConfig(webengine-pulseaudio) { gn_args += use_pulseaudio=true } else { gn_args += use_pulseaudio=false } - qtConfig(webengine-alsa) { + qtConfig(build-qtwebengine-core):qtConfig(webengine-alsa) { gn_args += use_alsa=true } else { gn_args += use_alsa=false } !packagesExist(libpci): gn_args += use_libpci=false - qtConfig(webengine-ozone-x11) { + qtConfig(build-qtwebengine-core):qtConfig(webengine-ozone-x11) { gn_args += ozone_platform_x11=true packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true qtConfig(webengine-webrtc): gn_args += rtc_use_x11=true diff --git a/src/buildtools/config/mac_osx.pri b/src/buildtools/config/mac_osx.pri index 3f2fe9c0a..a7ed61214 100644 --- a/src/buildtools/config/mac_osx.pri +++ b/src/buildtools/config/mac_osx.pri @@ -31,7 +31,7 @@ gn_args += \ mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION}\" \ use_external_popup_menu=false -qtConfig(webengine-spellchecker) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) { qtConfig(webengine-native-spellchecker): gn_args += use_browser_spellchecker=true else: gn_args += use_browser_spellchecker=false } else { diff --git a/src/buildtools/config/pdf.pri b/src/buildtools/config/pdf.pri new file mode 100644 index 000000000..4a1cf08e0 --- /dev/null +++ b/src/buildtools/config/pdf.pri @@ -0,0 +1,36 @@ +include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) +QT_FOR_CONFIG += pdf-private + +qtConfig(build-qtpdf) { + qtConfig(pdf-v8) { + gn_args += pdf_enable_v8=true + } else { + gn_args += pdf_enable_v8=false + } + qtConfig(pdf-xfa) { + gn_args += pdf_enable_xfa=true + } else { + gn_args += pdf_enable_xfa=false + } + qtConfig(pdf-xfa-bmp) { + gn_args += pdf_enable_xfa_bmp=true + } else { + gn_args += pdf_enable_xfa_bmp=false + } + qtConfig(pdf-xfa-gif) { + gn_args += pdf_enable_xfa_gif=true + } else { + gn_args += pdf_enable_xfa_gif=false + } + qtConfig(pdf-xfa-png) { + gn_args += pdf_enable_xfa_png=true + } else { + gn_args += pdf_enable_xfa_png=false + } + qtConfig(pdf-xfa-tiff) { + gn_args += pdf_enable_xfa_tiff=true + } else { + gn_args += pdf_enable_xfa_tiff=false + } +} + diff --git a/src/buildtools/config/support.pri b/src/buildtools/config/support.pri index 5bdd808d4..86ca2f80e 100644 --- a/src/buildtools/config/support.pri +++ b/src/buildtools/config/support.pri @@ -6,11 +6,6 @@ defineTest(qtwebengine_skipBuild) { defineReplace(qtwebengine_checkError) { - static { - qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.") - return(false) - } - !qtHaveModule(gui) { qtwebengine_skipBuild("QtWebEngine requires QtGui.") return(false) @@ -68,6 +63,7 @@ defineReplace(qtwebengine_checkError) { linux:!qtwebengine_checkErrorForLinux():return(false) win:!qtwebengine_checkErrorForWindows():return(false) + macos:!qtwebengine_checkErrorForMacOS():return(false) sanitizer: !qtConfig(webengine-sanitizer) { qtwebengine_skipBuild("Chosen sanitizer configuration is not supported for QtWebEngine. Check config.log for details or use -feature-webengine-sanitizer to force build with the chosen sanitizer configuration.") @@ -77,8 +73,20 @@ defineReplace(qtwebengine_checkError) { return(true) } +defineTest(qtwebengine_checkErrorFoMacOS) { + static { + qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.") + return(false) + } +} + defineTest(qtwebengine_checkErrorForLinux) { + static { + qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.") + return(false) + } + !qtConfig(pkg-config) { qtwebengine_skipBuild("A pkg-config support is required to build QtWebEngine.") return(false) @@ -114,6 +122,12 @@ defineTest(qtwebengine_checkErrorForLinux) { } defineTest(qtwebengine_checkErrorForWindows) { + + static { + qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.") + return(false) + } + !qtConfig(webengine-win-compiler64) { qtwebengine_skipBuild("64-bit cross-building or native toolchain required to build QtWebEngine could not be found.") return(false) diff --git a/src/buildtools/config/windows.pri b/src/buildtools/config/windows.pri index 5d7b7e1f2..2e5ebb876 100644 --- a/src/buildtools/config/windows.pri +++ b/src/buildtools/config/windows.pri @@ -89,7 +89,7 @@ msvc { error("Qt WebEngine for Windows can only be built with a Microsoft Visual Studio C++ compatible compiler") } -qtConfig(webengine-spellchecker) { +qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) { qtConfig(webengine-native-spellchecker): gn_args += use_browser_spellchecker=true else: gn_args += use_browser_spellchecker=false } else { diff --git a/src/buildtools/configure.json b/src/buildtools/configure.json index f04fdae81..939d689fd 100644 --- a/src/buildtools/configure.json +++ b/src/buildtools/configure.json @@ -9,6 +9,7 @@ "commandline": { "options": { "build-qtwebengine-core": "boolean", + "build-qtpdf": "boolean", "webengine-jumbo-build": { "type": "optionalString", "name": "merge_limit"} } }, @@ -389,6 +390,22 @@ && (!config.win32 || features.webengine-winversion)", "output": [ "privateFeature" ] }, + "webengine-qtpdf-support": { + "label": "Support Qt Pdf", + "condition": "module.gui + && features.webengine-python2 + && features.webengine-gperf + && features.webengine-bison + && features.webengine-flex + && features.webengine-submodule + && features.webengine-nowhitespace + && features.webengine-arch-support + && !features.webengine-no-platform-support + && (!config.static || config.ios) + && (!config.linux || features.pkg-config) + && (!config.win32 || features.webengine-winversion)", + "output": [ "privateFeature" ] + }, "build-qtwebengine-core": { "label": "Build Qt WebEngine Core", "purpose": "Provides WebEngine Core support.", @@ -681,7 +698,8 @@ "report": [ { "type": "skipBuildWarning", - "condition": "!features.webengine-core-support && (features.build-qtwebengine-core || features.build-qtpdf)", + "condition": "(!features.webengine-core-support && features.build-qtwebengine-core) || + (!features.webengine-qtpdf-support && features.build-qtpdf)", "message": "qtwebengine_confCheckError" }, { @@ -691,7 +709,7 @@ }, { "type": "note", - "condition": "features.webengine-core-support && !features.build-qtpdf", + "condition": "features.webengine-qtpdf-support && !features.build-qtpdf", "message": "QtPdf build is disabled by user." }, { @@ -701,8 +719,13 @@ }, { "type": "warning", - "condition": "!features.webengine-core-support && features.build-qtpdf", + "condition": "!features.webengine-qtpdf-support && features.build-qtpdf", "message": "QtPdf will not be built." + }, + { + "type": "warning", + "condition": "config.ios && config.simulator && config.device && features.build-qtpdf", + "message": "Building fat libray with device and simulator architectures will disable NEON." } ], "summary": [ @@ -711,14 +734,14 @@ "entries": [ "webengine-system-ninja", "webengine-system-gn", - { + { "message": "Jumbo Build Merge Limit", "type": "jumboBuild" }, "webengine-developer-build", { "section": "QtWebEngine required system libraries", - "condition": "config.unix && !config.macos", + "condition": "config.unix && !config.macos && !config.ios", "entries": [ "webengine-system-fontconfig", "webengine-system-dbus", diff --git a/src/core/configure.json b/src/core/configure.json index 9059575b9..9f05409ba 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -104,7 +104,6 @@ "webengine-embedded-build": { "label": "Embedded build", "purpose": "Enables the embedded build configuration.", - "section": "WebEngine", "condition": "config.unix", "autoDetect": "tests.webengine-embedded-build", "output": [ "privateFeature" ] @@ -139,14 +138,12 @@ "webengine-pepper-plugins": { "label": "Pepper Plugins", "purpose": "Enables use of Pepper Flash plugins.", - "section": "WebEngine", "autoDetect": "!features.webengine-embedded-build", "output": [ "privateFeature" ] }, "webengine-printing-and-pdf": { "label": "Printing and PDF", "purpose": "Provides printing and output to PDF.", - "section": "WebEngine", "condition": "module.printsupport && features.printer", "autoDetect": "!features.webengine-embedded-build", "output": [ "privateFeature" ] @@ -161,7 +158,6 @@ "webengine-proprietary-codecs": { "label": "Proprietary Codecs", "purpose": "Enables the use of proprietary codecs such as h.264/h.265 and MP3.", - "section": "WebEngine", "autoDetect": false, "output": [ "privateFeature" ] }, @@ -175,13 +171,11 @@ "webengine-spellchecker": { "label": "Spellchecker", "purpose": "Provides a spellchecker.", - "section": "WebEngine", "output": [ "publicFeature" ] }, "webengine-native-spellchecker": { "label": "Native Spellchecker", "purpose": "Use the system's native spellchecking engine.", - "section": "WebEngine", "autoDetect": false, "condition": "config.macos && features.webengine-spellchecker", "output": [ "publicFeature" ] @@ -197,7 +191,6 @@ "webengine-webrtc": { "label": "WebRTC", "purpose": "Provides WebRTC support.", - "section": "WebEngine", "autoDetect": "!features.webengine-embedded-build", "output": [ "privateFeature" ] }, diff --git a/src/core/core_gn_config.pri b/src/core/core_gn_config.pri index a089eecd0..2b8f2e18f 100644 --- a/src/core/core_gn_config.pri +++ b/src/core/core_gn_config.pri @@ -11,6 +11,7 @@ qtConfig (webengine-extensions) { } GN_CORE_INCLUDE_DIRS = $$PWD/service GN_CREATE_PRI = true +GN_PRECOMPILED_HEADERS = true QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE diff --git a/src/pdf/api/qpdfbookmarkmodel.h b/src/pdf/api/qpdfbookmarkmodel.h new file mode 100644 index 000000000..503a29100 --- /dev/null +++ b/src/pdf/api/qpdfbookmarkmodel.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFBOOKMARKMODEL_H +#define QPDFBOOKMARKMODEL_H + +#include <QtPdf/qtpdfglobal.h> +#include <QAbstractItemModel> + +QT_BEGIN_NAMESPACE + +class QPdfDocument; +class QPdfBookmarkModelPrivate; + +class Q_PDF_EXPORT QPdfBookmarkModel : public QAbstractItemModel +{ + Q_OBJECT + + Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) + Q_PROPERTY(StructureMode structureMode READ structureMode WRITE setStructureMode NOTIFY structureModeChanged) + +public: + enum StructureMode + { + TreeMode, + ListMode + }; + Q_ENUM(StructureMode) + + enum Role + { + TitleRole = Qt::DisplayRole, + LevelRole = Qt::UserRole, + PageNumberRole + }; + Q_ENUM(Role) + + explicit QPdfBookmarkModel(QObject *parent = nullptr); + + QPdfDocument* document() const; + void setDocument(QPdfDocument *document); + + StructureMode structureMode() const; + void setStructureMode(StructureMode mode); + + QVariant data(const QModelIndex &index, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; + +Q_SIGNALS: + void documentChanged(QPdfDocument *document); + void structureModeChanged(QPdfBookmarkModel::StructureMode structureMode); + +private: + Q_DECLARE_PRIVATE(QPdfBookmarkModel) + + Q_PRIVATE_SLOT(d_func(), void _q_documentStatusChanged()) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h new file mode 100644 index 000000000..1252fa6d2 --- /dev/null +++ b/src/pdf/api/qpdfdocument.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFDOCUMENT_H +#define QPDFDOCUMENT_H + +#include <QtPdf/qtpdfglobal.h> +#include <QImage> +#include <QObject> +#include <QtPdf/QPdfDocumentRenderOptions> + +QT_BEGIN_NAMESPACE + +class QPdfDocumentPrivate; +class QNetworkReply; + +class Q_PDF_EXPORT QPdfDocument : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL) + Q_PROPERTY(Status status READ status NOTIFY statusChanged FINAL) + +public: + enum Status { + Null, + Loading, + Ready, + Unloading, + Error + }; + Q_ENUM(Status) + + enum DocumentError { + NoError, + UnknownError, + DataNotYetAvailableError, + FileNotFoundError, + InvalidFileFormatError, + IncorrectPasswordError, + UnsupportedSecuritySchemeError + }; + Q_ENUM(DocumentError) + + enum MetaDataField { + Title, + Subject, + Author, + Keywords, + Producer, + Creator, + CreationDate, + ModificationDate + }; + Q_ENUM(MetaDataField) + + explicit QPdfDocument(QObject *parent = nullptr); + ~QPdfDocument(); + + DocumentError load(const QString &fileName); + + Status status() const; + + void load(QIODevice *device); + void setPassword(const QString &password); + QString password() const; + + QVariant metaData(MetaDataField field) const; + + DocumentError error() const; + + void close(); + + int pageCount() const; + + QSizeF pageSize(int page) const; + + QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions()); + +Q_SIGNALS: + void passwordChanged(); + void passwordRequired(); + void statusChanged(QPdfDocument::Status status); + void pageCountChanged(int pageCount); + +private: + friend class QPdfBookmarkModelPrivate; + friend class QPdfSearchModel; + + Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader()) + Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice()) + QScopedPointer<QPdfDocumentPrivate> d; +}; + +QT_END_NAMESPACE + +#endif // QPDFDOCUMENT_H diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h new file mode 100644 index 000000000..928754210 --- /dev/null +++ b/src/pdf/api/qpdfdocument_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFDOCUMENT_P_H +#define QPDFDOCUMENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpdfdocument.h" + +#include "third_party/pdfium/public/fpdfview.h" +#include "third_party/pdfium/public/fpdf_dataavail.h" + +#include <qbuffer.h> +#include <qmutex.h> +#include <qnetworkreply.h> +#include <qpointer.h> + +QT_BEGIN_NAMESPACE + +class QPdfMutexLocker : public QMutexLocker +{ +public: + QPdfMutexLocker(); +}; + +class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS +{ +public: + QPdfDocumentPrivate(); + ~QPdfDocumentPrivate(); + + QPdfDocument *q; + + FPDF_AVAIL avail; + FPDF_DOCUMENT doc; + bool loadComplete; + + QPointer<QIODevice> device; + QScopedPointer<QIODevice> ownDevice; + QBuffer asyncBuffer; + QPointer<QIODevice> sequentialSourceDevice; + QByteArray password; + + QPdfDocument::Status status; + QPdfDocument::DocumentError lastError; + int pageCount; + + void clear(); + + void load(QIODevice *device, bool ownDevice); + void loadAsync(QIODevice *device); + + void _q_tryLoadingWithSizeFromContentHeader(); + void initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize); + void _q_copyFromSequentialSourceDevice(); + void tryLoadDocument(); + void checkComplete(); + void setStatus(QPdfDocument::Status status); + + static FPDF_BOOL fpdf_IsDataAvail(struct _FX_FILEAVAIL* pThis, size_t offset, size_t size); + static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size); + static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size); + void updateLastError(); +}; + +QT_END_NAMESPACE + +#endif // QPDFDOCUMENT_P_H diff --git a/src/pdf/api/qpdfdocumentrenderoptions.h b/src/pdf/api/qpdfdocumentrenderoptions.h new file mode 100644 index 000000000..99a5db2e3 --- /dev/null +++ b/src/pdf/api/qpdfdocumentrenderoptions.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFDOCUMENTRENDEROPTIONS_H +#define QPDFDOCUMENTRENDEROPTIONS_H + +#include <QtPdf/qpdfnamespace.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QPdfDocumentRenderOptions +{ +public: + Q_DECL_CONSTEXPR QPdfDocumentRenderOptions() Q_DECL_NOTHROW : data(0) {} + + Q_DECL_CONSTEXPR QPdf::Rotation rotation() const Q_DECL_NOTHROW { return static_cast<QPdf::Rotation>(bits.rotation); } + Q_DECL_RELAXED_CONSTEXPR void setRotation(QPdf::Rotation _rotation) Q_DECL_NOTHROW { bits.rotation = _rotation; } + + Q_DECL_CONSTEXPR QPdf::RenderFlags renderFlags() const Q_DECL_NOTHROW { return static_cast<QPdf::RenderFlags>(bits.renderFlags); } + Q_DECL_RELAXED_CONSTEXPR void setRenderFlags(QPdf::RenderFlags _renderFlags) Q_DECL_NOTHROW { bits.renderFlags = _renderFlags; } + +private: + friend Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW; + + + struct Bits { + quint32 renderFlags : 8; + quint32 rotation : 3; + quint32 reserved : 21; + quint32 reserved2 : 32; + }; + + union { + Bits bits; + quint64 data; + }; +}; + +Q_DECLARE_TYPEINFO(QPdfDocumentRenderOptions, Q_PRIMITIVE_TYPE); + +Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW +{ + return lhs.data == rhs.data; +} + +Q_DECL_CONSTEXPR inline bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW +{ + return !operator==(lhs, rhs); +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPdfDocumentRenderOptions) + +#endif // QPDFDOCUMENTRENDEROPTIONS_H diff --git a/src/pdf/api/qpdfnamespace.h b/src/pdf/api/qpdfnamespace.h new file mode 100644 index 000000000..c8fd8a580 --- /dev/null +++ b/src/pdf/api/qpdfnamespace.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFNAMESPACE_H +#define QPDFNAMESPACE_H + +#include <QObject> + +QT_BEGIN_NAMESPACE + +namespace QPdf { + Q_NAMESPACE + + enum Rotation { + Rotate0, + Rotate90, + Rotate180, + Rotate270 + }; + Q_ENUM_NS(Rotation) + + enum RenderFlag { + NoRenderFlags = 0x000, + RenderAnnotations = 0x001, + RenderOptimizedForLcd = 0x002, + RenderGrayscale = 0x004, + RenderForceHalftone = 0x008, + RenderTextAliased = 0x010, + RenderImageAliased = 0x020, + RenderPathAliased = 0x040 + }; + Q_FLAG_NS(RenderFlag) + Q_DECLARE_FLAGS(RenderFlags, RenderFlag) +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QPdf::RenderFlags) + +QT_END_NAMESPACE +#endif diff --git a/src/pdf/api/qpdfpagenavigation.h b/src/pdf/api/qpdfpagenavigation.h new file mode 100644 index 000000000..8da809646 --- /dev/null +++ b/src/pdf/api/qpdfpagenavigation.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFPAGENAVIGATION_H +#define QPDFPAGENAVIGATION_H + +#include <QtPdf/qtpdfglobal.h> +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QPdfDocument; +class QPdfPageNavigationPrivate; + +class Q_PDF_EXPORT QPdfPageNavigation : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) + + Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged) + Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged) + Q_PROPERTY(bool canGoToPreviousPage READ canGoToPreviousPage NOTIFY canGoToPreviousPageChanged) + Q_PROPERTY(bool canGoToNextPage READ canGoToNextPage NOTIFY canGoToNextPageChanged) + +public: + explicit QPdfPageNavigation(QObject *parent = nullptr); + ~QPdfPageNavigation(); + + QPdfDocument* document() const; + void setDocument(QPdfDocument *document); + + int currentPage() const; + void setCurrentPage(int currentPage); + + int pageCount() const; + + bool canGoToPreviousPage() const; + bool canGoToNextPage() const; + +public Q_SLOTS: + void goToPreviousPage(); + void goToNextPage(); + +Q_SIGNALS: + void documentChanged(QPdfDocument *document); + void currentPageChanged(int currentPage); + void pageCountChanged(int pageCount); + void canGoToPreviousPageChanged(bool canGo); + void canGoToNextPageChanged(bool canGo); + +private: + Q_DECLARE_PRIVATE(QPdfPageNavigation) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/pdf/api/qpdfpagerenderer.h b/src/pdf/api/qpdfpagerenderer.h new file mode 100644 index 000000000..de6704480 --- /dev/null +++ b/src/pdf/api/qpdfpagerenderer.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFPAGERENDERER_H +#define QPDFPAGERENDERER_H + +#include <QtPdf/qtpdfglobal.h> +#include <QObject> +#include <QtPdf/QPdfDocumentRenderOptions> +#include <QSize> + +QT_BEGIN_NAMESPACE + +class QPdfDocument; +class QPdfPageRendererPrivate; + +class Q_PDF_EXPORT QPdfPageRenderer : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) + Q_PROPERTY(RenderMode renderMode READ renderMode WRITE setRenderMode NOTIFY renderModeChanged) + +public: + enum RenderMode + { + MultiThreadedRenderMode, + SingleThreadedRenderMode + }; + Q_ENUM(RenderMode) + + explicit QPdfPageRenderer(QObject *parent = nullptr); + ~QPdfPageRenderer(); + + RenderMode renderMode() const; + void setRenderMode(RenderMode mode); + + QPdfDocument* document() const; + void setDocument(QPdfDocument *document); + + quint64 requestPage(int pageNumber, QSize imageSize, + QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions()); + +Q_SIGNALS: + void documentChanged(QPdfDocument *document); + void renderModeChanged(RenderMode renderMode); + + void pageRendered(int pageNumber, QSize imageSize, const QImage &image, + QPdfDocumentRenderOptions options, quint64 requestId); + +private: + Q_DECLARE_PRIVATE(QPdfPageRenderer) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/pdf/api/qpdfsearchmodel.h b/src/pdf/api/qpdfsearchmodel.h new file mode 100644 index 000000000..02d2a20d5 --- /dev/null +++ b/src/pdf/api/qpdfsearchmodel.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFSEARCHMODEL_H +#define QPDFSEARCHMODEL_H + +#include "qtpdfglobal.h" +#include "qpdfdocument.h" + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QPdfSearchModelPrivate; + +class Q_PDF_EXPORT QPdfSearchModel : public QObject // TODO QAIM? +{ + Q_OBJECT + Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged) + +public: + explicit QPdfSearchModel(QObject *parent = nullptr); + ~QPdfSearchModel(); + + QVector<QRectF> matches(int page, const QString &searchString); + + QPdfDocument *document() const; + +public Q_SLOTS: + void setDocument(QPdfDocument *document); + +Q_SIGNALS: + void documentChanged(); + +private: + QScopedPointer<QPdfSearchModelPrivate> d; +}; + +QT_END_NAMESPACE + +#endif // QPDFSEARCHMODEL_H diff --git a/src/pdf/api/qtpdfglobal.h b/src/pdf/api/qtpdfglobal.h new file mode 100644 index 000000000..34cfc46d9 --- /dev/null +++ b/src/pdf/api/qtpdfglobal.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTPDFGLOBAL_H +#define QTPDFGLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef Q_PDF_EXPORT +# ifndef QT_STATIC +# if defined(QT_BUILD_PDF_LIB) +# define Q_PDF_EXPORT Q_DECL_EXPORT +# else +# define Q_PDF_EXPORT Q_DECL_IMPORT +# endif +# else +# define Q_PDF_EXPORT +# endif +#endif + +QT_END_NAMESPACE + +#endif // QTPDFGLOBAL_H + diff --git a/src/pdf/configure.json b/src/pdf/configure.json new file mode 100644 index 000000000..1f21183a2 --- /dev/null +++ b/src/pdf/configure.json @@ -0,0 +1,64 @@ +{ + "module": "pdf", + "depends" : [ "buildtools-private" ], + "testDir": "../../config.tests", + "condition": "features.build-qtpdf", + "libraries": { + }, + "tests": { + }, + "features": { + "pdf-v8": { + "label": "Support V8", + "purpose": "Enables javascript support.", + "autoDetect": "false", + "condition": "!config.ios", + "output": ["privateFeature" ] + }, + "pdf-xfa": { + "label": "Support XFA", + "purpose": "Enables XFA support.", + "condition": "features.pdf-v8", + "output": ["privateFeature" ] + }, + "pdf-xfa-bmp": { + "label": "Support XFA-BMP", + "purpose": "Enables XFA-BMP support.", + "condition": "features.pdf-xfa", + "output": ["privateFeature" ] + }, + "pdf-xfa-gif": { + "label": "Support XFA-GIF", + "purpose": "Enables XFA-GIF support.", + "condition": "features.pdf-xfa", + "output": ["privateFeature" ] + }, + "pdf-xfa-png": { + "label": "Support XFA-PNG", + "purpose": "Enables XFA-PNG support.", + "condition": "features.pdf-xfa", + "output": ["privateFeature" ] + }, + "pdf-xfa-tiff": { + "label": "Support XFA-TIFF", + "purpose": "Enables XFA-TIFF support.", + "condition": "features.pdf-xfa", + "output": ["privateFeature" ] + } + }, + "report": [ + ], + "summary": [ + { + "section": "Qt PDF", + "entries": [ + "pdf-v8", + "pdf-xfa", + "pdf-xfa-bmp", + "pdf-xfa-gif", + "pdf-xfa-png", + "pdf-xfa-tiff" + ] + } + ] +} diff --git a/src/pdf/doc/qtpdf.qdocconf b/src/pdf/doc/qtpdf.qdocconf new file mode 100644 index 000000000..b55b25327 --- /dev/null +++ b/src/pdf/doc/qtpdf.qdocconf @@ -0,0 +1,55 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtPdf +description = Qt Pdf Reference Documentation +version = $QT_VERSION + +qhp.projects = QtPdf + +qhp.QtPdf.file = qtpdf.qhp +qhp.QtPdf.namespace = org.qt-project.qtpdf.$QT_VERSION_TAG +qhp.QtPdf.virtualFolder = qtpdf +qhp.QtPdf.indexTitle = Qt PDF +qhp.QtPdf.indexRoot = + +qhp.QtPdf.filterAttributes = qtpdf $QT_VERSION qtrefdoc +qhp.QtPdf.customFilters.Qt.name = QtPdf $QT_VERSION +qhp.QtPdf.customFilters.Qt.filterAttributes = qtpdf $QT_VERSION + +qhp.QtPdf.subprojects = classes qmltypes examples + +qhp.QtPdf.subprojects.classes.title = C++ Classes +qhp.QtPdf.subprojects.classes.indexTitle = Qt PDF C++ Classes +qhp.QtPdf.subprojects.classes.selectors = class fake:headerfile +qhp.QtPdf.subprojects.classes.sortPages = true + +qhp.QtPdf.subprojects.qmltypes.title = QML Types +qhp.QtPdf.subprojects.qmltypes.indexTitle = Qt Quick PDF QML Types +qhp.QtPdf.subprojects.qmltypes.selectors = qmltype +qhp.QtPdf.subprojects.qmltypes.sortPages = true + +qhp.QtPdf.subprojects.examples.title = Examples +qhp.QtPdf.subprojects.examples.indexTitle = Qt PDF Examples +qhp.QtPdf.subprojects.examples.selectors = doc:example +qhp.QtPdf.subprojects.examples.sortPages = true + +depends += qtcore \ + qtwidgets \ + qtgui \ + qtdoc \ + qmake + +headerdirs += ../api \ + ../quick + +sourcedirs += .. \ + ../quick + +exampledirs += ../../../examples/pdfwidgets \ + snippets/ + +imagedirs += images + +navigation.landingpage = "Qt PDF" +navigation.cppclassespage = "Qt PDF C++ Classes" +navigation.qmltypespage = "Qt WebEngine QML Types" diff --git a/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc new file mode 100644 index 000000000..25593b1ee --- /dev/null +++ b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +QT += pdf +//! [0] + + +//! [1] +#include <QtPdf> +//! [1] diff --git a/src/pdf/doc/src/qtpdf-index.qdoc b/src/pdf/doc/src/qtpdf-index.qdoc new file mode 100644 index 000000000..6893272ac --- /dev/null +++ b/src/pdf/doc/src/qtpdf-index.qdoc @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtpdf-index.html + \title Qt PDF + + \brief Renders pages from PDF documents. + + The Qt PDF module contains classes and functions for rendering + PDF documents. The \l QPdfDocument class loads a PDF document + and renders pages from it according to the options provided by + the \l QPdfDocumentRenderOptions class. The \l QPdfPageRenderer + class manages a queue that collects all render requests. The + \l QPdfPageNavigation class handles the navigation through a + PDF document. + + \section1 Getting Started + + To include the definitions of the module's classes, use the + following directive: + + \snippet qtpdf_build_snippet.qdoc 1 + + To link against the module, add this line to your qmake project file: + + \snippet qtpdf_build_snippet.qdoc 0 + + \section1 Articles and Guides + + \list + \li \l{Qt PDF Overview} + \endlist + + \section1 API Reference + + \list + \li \l{Qt PDF C++ Classes} + \li \l{Qt Quick PDF QML Types} + \endlist +*/ diff --git a/src/pdf/doc/src/qtpdf-module.qdoc b/src/pdf/doc/src/qtpdf-module.qdoc new file mode 100644 index 000000000..4170deb38 --- /dev/null +++ b/src/pdf/doc/src/qtpdf-module.qdoc @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \module QtPdf + \title Qt PDF C++ Classes + \brief Renders pages from PDF documents. + \since 5.14 + \ingroup qtwebengine-modules + \ingroup modules + + The Qt PDF module contains classes and functions for rendering + PDF documents. + + To include the definitions of the module's classes, use the + following directive: + + \snippet qtpdf_build_snippet.qdoc 1 + + \if !defined(qtforpython) + To link against the module, add this line to your qmake project file: + + \snippet qtpdf_build_snippet.qdoc 0 + \endif +*/ diff --git a/src/pdf/gn_run.pro b/src/pdf/gn_run.pro new file mode 100644 index 000000000..7fd4d4ffc --- /dev/null +++ b/src/pdf/gn_run.pro @@ -0,0 +1,70 @@ +include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) +QT_FOR_CONFIG += buildtools-private + +TEMPLATE = aux + +qtConfig(debug_and_release): CONFIG += debug_and_release build_all + +qtConfig(webengine-system-ninja) { + QT_TOOL.ninja.binary = ninja +} else { + QT_TOOL.ninja.binary = $$shell_quote($$shell_path($$ninjaPath())) +} + +win32 { + # Add the gnuwin32/bin subdir of qt5.git to PATH. Needed for calling bison and friends. + gnuwin32path.name = PATH + gnuwin32path.value = $$shell_path($$clean_path($$QTWEBENGINE_ROOT/../gnuwin32/bin)) + gnuwin32path.CONFIG += prepend + exists($$gnuwin32path.value): QT_TOOL_ENV = gnuwin32path +} + +qtPrepareTool(NINJA, ninja) +QT_TOOL_ENV = + +build_pass|!debug_and_release { + gn_binary = gn + + runninja.target = run_ninja + + # fixme: refine args + gn_args = $$gnArgs() + + include($$QTWEBENGINE_ROOT/src/buildtools/config/pdf.pri) + + # fixme: qtwebengine_target + gn_args += "qtwebengine_target=\"$$system_path($$OUT_PWD/$$getConfigDir()):QtPdf\"" + + # fixme: + !qtConfig(webengine-system-gn) { + gn_binary = $$system_quote($$system_path($$gnPath())) + } + + gn_args = $$system_quote($$gn_args) + gn_src_root = $$system_quote($$system_path($$QTWEBENGINE_ROOT/$$getChromiumSrcDir())) + gn_build_root = $$system_quote($$system_path($$OUT_PWD/$$getConfigDir())) + gn_python = "--script-executable=$$pythonPathForSystem()" + gn_run = $$gn_binary gen $$gn_build_root $$gn_python --args=$$gn_args --root=$$gn_src_root + + message("Running: $$gn_run ") + !system($$gn_run) { + error("GN run error!") + } + + ninjaflags = $$(NINJAFLAGS) + isEmpty(ninjaflags):!silent: ninjaflags = "-v" + + runninja.commands = $$NINJA $$ninjaflags -C $$gn_build_root QtPdf + QMAKE_EXTRA_TARGETS += runninja + + build_pass:build_all: default_target.target = all + else: default_target.target = first + default_target.depends = runninja + QMAKE_EXTRA_TARGETS += default_target +} + +!build_pass:debug_and_release { + # Special GNU make target for the meta Makefile that ensures that our debug and release Makefiles won't both run ninja in parallel. + notParallel.target = .NOTPARALLEL + QMAKE_EXTRA_TARGETS += notParallel +} diff --git a/src/pdf/jsbridge.cpp b/src/pdf/jsbridge.cpp new file mode 100644 index 000000000..95b813929 --- /dev/null +++ b/src/pdf/jsbridge.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fpdfsdk/javascript/JS_Runtime_Stub.cpp" + diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro new file mode 100644 index 000000000..c5513ce93 --- /dev/null +++ b/src/pdf/pdf.pro @@ -0,0 +1,16 @@ +TEMPLATE = subdirs +pdfcore.file = pdfcore.pro +pdfcore_generator.file = pdfcore_generator.pro +gn_run.file = gn_run.pro + +gn_run.depends = pdfcore_generator +pdfcore.depends = gn_run +quick.depends = pdfcore + +SUBDIRS += \ + pdfcore_generator \ + gn_run \ + pdfcore \ + quick + + diff --git a/src/pdf/pdfcore.pro b/src/pdf/pdfcore.pro new file mode 100644 index 000000000..1f9f21e0a --- /dev/null +++ b/src/pdf/pdfcore.pro @@ -0,0 +1,83 @@ +TARGET = QtPdf +MODULE = pdf + +QT += gui core core-private +QT_PRIVATE += network + +TEMPLATE = lib + +CONFIG += c++11 +CONFIG -= precompile_header # Not supported by upstream header files + +INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf +CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir() +CHROMIUM_GEN_DIR = $$OUT_PWD/../$$getConfigDir()/gen +INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf \ + $$CHROMIUM_GEN_DIR \ + $$CHROMIUM_SRC_DIR \ + api + +DEFINES += QT_BUILD_PDF_LIB +win32: DEFINES += NOMINMAX + +linking_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}.pri +!include($$linking_pri) { + error("Could not find the linking information that gn should have generated.") +} + +isEmpty(NINJA_OBJECTS): error("Missing object files from QtPdf linking pri.") +isEmpty(NINJA_LFLAGS): error("Missing linker flags from QtPdf linking pri") +isEmpty(NINJA_ARCHIVES): error("Missing archive files from QtPdf linking pri") +isEmpty(NINJA_LIBS): error("Missing library files from QtPdf linking pri") + +NINJA_OBJECTS = $$eval($$list($$NINJA_OBJECTS)) +RSP_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}.rsp +for(object, NINJA_OBJECTS): RSP_CONTENT += $$object +write_file($$RSP_FILE, RSP_CONTENT) + +macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($$RSP_FILE) +linux:LIBS_PRIVATE += @$$RSP_FILE + +# QTBUG-58710 add main rsp file on windows +win32:QMAKE_LFLAGS += @$$RSP_FILE + +linux: LIBS_PRIVATE += -Wl,--start-group $$NINJA_ARCHIVES -Wl,--end-group +else: LIBS_PRIVATE += $$NINJA_ARCHIVES + +LIBS_PRIVATE += $$NINJA_LIB_DIRS $$NINJA_LIBS + +QMAKE_DOCS = $$PWD/doc/qtpdf.qdocconf + +gcc { + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter +} + +msvc { + QMAKE_CXXFLAGS_WARN_ON += -wd"4100" +} + +ios: OBJECTS += $$NINJA_OBJECTS + +SOURCES += \ + qpdfbookmarkmodel.cpp \ + qpdfdocument.cpp \ + qpdfpagenavigation.cpp \ + qpdfpagerenderer.cpp \ + qpdfsearchmodel.cpp \ + +# all "public" headers must be in "api" for sync script and to hide auto generated headers +# by Chromium in case of in-source build + +HEADERS += \ + api/qpdfbookmarkmodel.h \ + api/qpdfdocument.h \ + api/qpdfdocument_p.h \ + api/qpdfdocumentrenderoptions.h \ + api/qtpdfglobal.h \ + api/qpdfnamespace.h \ + api/qpdfpagenavigation.h \ + api/qpdfpagerenderer.h \ + api/qpdfsearchmodel.h \ + qpdfsearchmodel_p.h \ + +load(qt_module) diff --git a/src/pdf/pdfcore_generator.pro b/src/pdf/pdfcore_generator.pro new file mode 100644 index 000000000..c8eb13b81 --- /dev/null +++ b/src/pdf/pdfcore_generator.pro @@ -0,0 +1,15 @@ +qtConfig(debug_and_release): CONFIG += debug_and_release + +TARGET = QtPdf +TEMPLATE = lib +CONFIG = gn_generator $$CONFIG +ios: CONFIG -=static # note we still do static on ios when linking +GN_SRC_DIR = $$PWD +GN_FILE = $$OUT_PWD/$$getConfigDir()/BUILD.gn +GN_FIND_MOCABLES_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_find_mocables.py) +GN_RUN_BINARY_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_run_binary.py) +GN_IMPORTS = $$PWD/qtpdf.gni +GN_CREATE_PRI = true +QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE + + diff --git a/src/pdf/qpdfbookmarkmodel.cpp b/src/pdf/qpdfbookmarkmodel.cpp new file mode 100644 index 000000000..e648657ba --- /dev/null +++ b/src/pdf/qpdfbookmarkmodel.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfbookmarkmodel.h" + +#include "qpdfdocument.h" +#include "qpdfdocument_p.h" + +#include "third_party/pdfium/public/fpdf_doc.h" +#include "third_party/pdfium/public/fpdfview.h" + +#include <QPointer> +#include <QScopedPointer> +#include <private/qabstractitemmodel_p.h> + +QT_BEGIN_NAMESPACE + +class BookmarkNode +{ +public: + explicit BookmarkNode(BookmarkNode *parentNode = nullptr) + : m_parentNode(parentNode) + , m_level(0) + , m_pageNumber(0) + { + } + + ~BookmarkNode() + { + clear(); + } + + void clear() + { + qDeleteAll(m_childNodes); + m_childNodes.clear(); + } + + void appendChild(BookmarkNode *child) + { + m_childNodes.append(child); + } + + BookmarkNode *child(int row) const + { + return m_childNodes.at(row); + } + + int childCount() const + { + return m_childNodes.count(); + } + + int row() const + { + if (m_parentNode) + return m_parentNode->m_childNodes.indexOf(const_cast<BookmarkNode*>(this)); + + return 0; + } + + BookmarkNode *parentNode() const + { + return m_parentNode; + } + + QString title() const + { + return m_title; + } + + void setTitle(const QString &title) + { + m_title = title; + } + + int level() const + { + return m_level; + } + + void setLevel(int level) + { + m_level = level; + } + + int pageNumber() const + { + return m_pageNumber; + } + + void setPageNumber(int pageNumber) + { + m_pageNumber = pageNumber; + } + +private: + QVector<BookmarkNode*> m_childNodes; + BookmarkNode *m_parentNode; + + QString m_title; + int m_level; + int m_pageNumber; +}; + + +class QPdfBookmarkModelPrivate : public QAbstractItemModelPrivate +{ +public: + QPdfBookmarkModelPrivate() + : QAbstractItemModelPrivate() + , m_rootNode(new BookmarkNode(nullptr)) + , m_document(nullptr) + , m_structureMode(QPdfBookmarkModel::TreeMode) + { + } + + void rebuild() + { + Q_Q(QPdfBookmarkModel); + + const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Ready); + + if (documentAvailable) { + q->beginResetModel(); + m_rootNode->clear(); + QPdfMutexLocker lock; + appendChildNode(m_rootNode.data(), nullptr, 0, m_document->d->doc); + lock.unlock(); + q->endResetModel(); + } else { + if (m_rootNode->childCount() == 0) { + return; + } else { + q->beginResetModel(); + m_rootNode->clear(); + q->endResetModel(); + } + } + } + + void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document) + { + FPDF_BOOKMARK bookmark = FPDFBookmark_GetFirstChild(document, parentBookmark); + + while (bookmark) { + BookmarkNode *childBookmarkNode = nullptr; + + if (m_structureMode == QPdfBookmarkModel::TreeMode) { + childBookmarkNode = new BookmarkNode(parentBookmarkNode); + parentBookmarkNode->appendChild(childBookmarkNode); + } else if (m_structureMode == QPdfBookmarkModel::ListMode) { + childBookmarkNode = new BookmarkNode(m_rootNode.data()); + m_rootNode->appendChild(childBookmarkNode); + } + + const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0)); + + QVector<ushort> titleBuffer(titleLength); + FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.length())); + + const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark); + const int pageNumber = FPDFDest_GetDestPageIndex(document, dest); + + childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data())); + childBookmarkNode->setLevel(level); + childBookmarkNode->setPageNumber(pageNumber); + + // recurse down + appendChildNode(childBookmarkNode, bookmark, level + 1, document); + + bookmark = FPDFBookmark_GetNextSibling(document, bookmark); + } + } + + void _q_documentStatusChanged() + { + rebuild(); + } + + Q_DECLARE_PUBLIC(QPdfBookmarkModel) + + QScopedPointer<BookmarkNode> m_rootNode; + QPointer<QPdfDocument> m_document; + QPdfBookmarkModel::StructureMode m_structureMode; +}; + + +QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent) + : QAbstractItemModel(*new QPdfBookmarkModelPrivate, parent) +{ +} + +QPdfDocument* QPdfBookmarkModel::document() const +{ + Q_D(const QPdfBookmarkModel); + + return d->m_document; +} + +void QPdfBookmarkModel::setDocument(QPdfDocument *document) +{ + Q_D(QPdfBookmarkModel); + + if (d->m_document == document) + return; + + if (d->m_document) + disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged())); + + d->m_document = document; + emit documentChanged(d->m_document); + + if (d->m_document) + connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged())); + + d->rebuild(); +} + +QPdfBookmarkModel::StructureMode QPdfBookmarkModel::structureMode() const +{ + Q_D(const QPdfBookmarkModel); + + return d->m_structureMode; +} + +void QPdfBookmarkModel::setStructureMode(StructureMode mode) +{ + Q_D(QPdfBookmarkModel); + + if (d->m_structureMode == mode) + return; + + d->m_structureMode = mode; + emit structureModeChanged(d->m_structureMode); + + d->rebuild(); +} + +int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const +{ + QHash<int, QByteArray> names; + + names[TitleRole] = "title"; + names[LevelRole] = "level"; + names[PageNumberRole] = "pageNumber"; + + return names; +} + +QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer()); + switch (role) { + case TitleRole: + return node->title(); + case LevelRole: + return node->level(); + case PageNumberRole: + return node->pageNumber(); + default: + return QVariant(); + } +} + +QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_D(const QPdfBookmarkModel); + + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + BookmarkNode *parentNode; + + if (!parent.isValid()) + parentNode = d->m_rootNode.data(); + else + parentNode = static_cast<BookmarkNode*>(parent.internalPointer()); + + BookmarkNode *childNode = parentNode->child(row); + if (childNode) + return createIndex(row, column, childNode); + else + return QModelIndex(); +} + +QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const +{ + Q_D(const QPdfBookmarkModel); + + if (!index.isValid()) + return QModelIndex(); + + const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer()); + BookmarkNode *parentNode = childNode->parentNode(); + + if (parentNode == d->m_rootNode.data()) + return QModelIndex(); + + return createIndex(parentNode->row(), 0, parentNode); +} + +int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const QPdfBookmarkModel); + + if (parent.column() > 0) + return 0; + + BookmarkNode *parentNode = nullptr; + + if (!parent.isValid()) + parentNode = d->m_rootNode.data(); + else + parentNode = static_cast<BookmarkNode*>(parent.internalPointer()); + + return parentNode->childCount(); +} + +QT_END_NAMESPACE + +#include "moc_qpdfbookmarkmodel.cpp" diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp new file mode 100644 index 000000000..690953691 --- /dev/null +++ b/src/pdf/qpdfdocument.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfdocument.h" +#include "qpdfdocument_p.h" + +#include "third_party/pdfium/public/fpdf_doc.h" + +#include <QDateTime> +#include <QDebug> +#include <QElapsedTimer> +#include <QFile> +#include <QHash> +#include <QLoggingCategory> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +// The library is not thread-safe at all, it has a lot of global variables. +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive)); +static int libraryRefCount; +Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document") + +QPdfMutexLocker::QPdfMutexLocker() + : QMutexLocker(pdfMutex()) +{ +} + +QPdfDocumentPrivate::QPdfDocumentPrivate() + : avail(nullptr) + , doc(nullptr) + , loadComplete(false) + , status(QPdfDocument::Null) + , lastError(QPdfDocument::NoError) + , pageCount(0) +{ + asyncBuffer.setData(QByteArray()); + asyncBuffer.open(QIODevice::ReadWrite); + + const QPdfMutexLocker lock; + + if (libraryRefCount == 0) + FPDF_InitLibrary(); + ++libraryRefCount; + + // FPDF_FILEACCESS setup + m_Param = this; + m_GetBlock = fpdf_GetBlock; + + // FX_FILEAVAIL setup + FX_FILEAVAIL::version = 1; + IsDataAvail = fpdf_IsDataAvail; + + // FX_DOWNLOADHINTS setup + FX_DOWNLOADHINTS::version = 1; + AddSegment = fpdf_AddSegment; +} + +QPdfDocumentPrivate::~QPdfDocumentPrivate() +{ + q->close(); + + const QPdfMutexLocker lock; + + if (!--libraryRefCount) + FPDF_DestroyLibrary(); +} + +void QPdfDocumentPrivate::clear() +{ + QPdfMutexLocker lock; + + if (doc) + FPDF_CloseDocument(doc); + doc = nullptr; + + if (avail) + FPDFAvail_Destroy(avail); + avail = nullptr; + lock.unlock(); + + if (pageCount != 0) { + pageCount = 0; + emit q->pageCountChanged(pageCount); + } + + loadComplete = false; + + asyncBuffer.close(); + asyncBuffer.setData(QByteArray()); + asyncBuffer.open(QIODevice::ReadWrite); + + if (sequentialSourceDevice) + sequentialSourceDevice->disconnect(q); +} + +void QPdfDocumentPrivate::updateLastError() +{ + if (doc) { + lastError = QPdfDocument::NoError; + return; + } + + QPdfMutexLocker lock; + const unsigned long error = FPDF_GetLastError(); + lock.unlock(); + + switch (error) { + case FPDF_ERR_SUCCESS: lastError = QPdfDocument::NoError; break; + case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::UnknownError; break; + case FPDF_ERR_FILE: lastError = QPdfDocument::FileNotFoundError; break; + case FPDF_ERR_FORMAT: lastError = QPdfDocument::InvalidFileFormatError; break; + case FPDF_ERR_PASSWORD: lastError = QPdfDocument::IncorrectPasswordError; break; + case FPDF_ERR_SECURITY: lastError = QPdfDocument::UnsupportedSecuritySchemeError; break; + default: + Q_UNREACHABLE(); + } +} + +void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership) +{ + if (transferDeviceOwnership) + ownDevice.reset(newDevice); + else + ownDevice.reset(); + + if (newDevice->isSequential()) { + sequentialSourceDevice = newDevice; + device = &asyncBuffer; + QNetworkReply *reply = qobject_cast<QNetworkReply*>(sequentialSourceDevice); + + if (!reply) { + setStatus(QPdfDocument::Error); + qWarning() << "QPdfDocument: Loading from sequential devices only supported with QNetworkAccessManager."; + return; + } + + if (reply->isFinished() && reply->error() != QNetworkReply::NoError) { + setStatus(QPdfDocument::Error); + return; + } + + QObject::connect(reply, &QNetworkReply::finished, q, [this, reply](){ + if (reply->error() != QNetworkReply::NoError || reply->bytesAvailable() == 0) { + this->setStatus(QPdfDocument::Error); + } + }); + + if (reply->header(QNetworkRequest::ContentLengthHeader).isValid()) + _q_tryLoadingWithSizeFromContentHeader(); + else + QObject::connect(reply, SIGNAL(metaDataChanged()), q, SLOT(_q_tryLoadingWithSizeFromContentHeader())); + } else { + device = newDevice; + initiateAsyncLoadWithTotalSizeKnown(device->size()); + checkComplete(); + } +} + +void QPdfDocumentPrivate::_q_tryLoadingWithSizeFromContentHeader() +{ + if (avail) + return; + + const QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sequentialSourceDevice); + if (!networkReply) { + setStatus(QPdfDocument::Error); + return; + } + + const QVariant contentLength = networkReply->header(QNetworkRequest::ContentLengthHeader); + if (!contentLength.isValid()) { + setStatus(QPdfDocument::Error); + return; + } + + QObject::connect(sequentialSourceDevice, SIGNAL(readyRead()), q, SLOT(_q_copyFromSequentialSourceDevice())); + + initiateAsyncLoadWithTotalSizeKnown(contentLength.toULongLong()); + + if (sequentialSourceDevice->bytesAvailable()) + _q_copyFromSequentialSourceDevice(); +} + +void QPdfDocumentPrivate::initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize) +{ + // FPDF_FILEACCESS setup + m_FileLen = totalSize; + + const QPdfMutexLocker lock; + + avail = FPDFAvail_Create(this, this); +} + +void QPdfDocumentPrivate::_q_copyFromSequentialSourceDevice() +{ + if (loadComplete) + return; + + const QByteArray data = sequentialSourceDevice->read(sequentialSourceDevice->bytesAvailable()); + if (data.isEmpty()) + return; + + asyncBuffer.seek(asyncBuffer.size()); + asyncBuffer.write(data); + + checkComplete(); +} + +void QPdfDocumentPrivate::tryLoadDocument() +{ + QPdfMutexLocker lock; + switch (FPDFAvail_IsDocAvail(avail, this)) { + case PDF_DATA_ERROR: + qCDebug(qLcDoc) << "error loading"; + break; + case PDF_DATA_NOTAVAIL: + qCDebug(qLcDoc) << "data not yet available"; + lastError = QPdfDocument::DataNotYetAvailableError; + setStatus(QPdfDocument::Error); + break; + case PDF_DATA_AVAIL: + // all good + break; + } + + Q_ASSERT(!doc); + + doc = FPDFAvail_GetDocument(avail, password); + lock.unlock(); + + updateLastError(); + + if (lastError == QPdfDocument::IncorrectPasswordError) { + FPDF_CloseDocument(doc); + doc = nullptr; + + setStatus(QPdfDocument::Error); + emit q->passwordRequired(); + } +} + +void QPdfDocumentPrivate::checkComplete() +{ + if (!avail || loadComplete) + return; + + if (!doc) + tryLoadDocument(); + + if (!doc) + return; + + loadComplete = true; + + QPdfMutexLocker lock; + + const int newPageCount = FPDF_GetPageCount(doc); + for (int i = 0; i < newPageCount; ++i) { + int result = PDF_DATA_NOTAVAIL; + while (result == PDF_DATA_NOTAVAIL) { + result = FPDFAvail_IsPageAvail(avail, i, this); + } + + if (result == PDF_DATA_ERROR) + loadComplete = false; + } + + lock.unlock(); + + if (loadComplete) { + if (newPageCount != pageCount) { + pageCount = newPageCount; + emit q->pageCountChanged(pageCount); + } + + setStatus(QPdfDocument::Ready); + } +} + +void QPdfDocumentPrivate::setStatus(QPdfDocument::Status documentStatus) +{ + if (status == documentStatus) + return; + + status = documentStatus; + emit q->statusChanged(status); +} + +FPDF_BOOL QPdfDocumentPrivate::fpdf_IsDataAvail(_FX_FILEAVAIL *pThis, size_t offset, size_t size) +{ + QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(pThis); + return offset + size <= static_cast<quint64>(d->device->size()); +} + +int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size) +{ + QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(reinterpret_cast<FPDF_FILEACCESS*>(param)); + d->device->seek(position); + return qMax(qint64(0), d->device->read(reinterpret_cast<char *>(pBuf), size)); + +} + +void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offset, size_t size) +{ + Q_UNUSED(pThis); + Q_UNUSED(offset); + Q_UNUSED(size); +} + +/*! + \class QPdfDocument + \since 5.10 + \inmodule QtPdf + + \brief The QPdfDocument class loads a PDF document and renders pages from it. +*/ + +/*! + Constructs a new document with parent object \a parent. +*/ +QPdfDocument::QPdfDocument(QObject *parent) + : QObject(parent) + , d(new QPdfDocumentPrivate) +{ + d->q = this; +} + +/*! + Destroys the document. +*/ +QPdfDocument::~QPdfDocument() +{ +} + +QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName) +{ + close(); + + d->setStatus(QPdfDocument::Loading); + + QScopedPointer<QFile> f(new QFile(fileName)); + if (!f->open(QIODevice::ReadOnly)) { + d->lastError = FileNotFoundError; + d->setStatus(QPdfDocument::Error); + } else { + d->load(f.take(), /*transfer ownership*/true); + } + return d->lastError; +} + +/*! + \enum QPdfDocument::Status + + This enum describes the current status of the document. + + \value Null The initial status after the document has been created or after it has been closed. + \value Loading The status after load() has been called and before the document is fully loaded. + \value Ready The status when the document is fully loaded and its data can be accessed. + \value Unloading The status after close() has been called on an open document. + At this point the document is still valid and all its data can be accessed. + \value Error The status after Loading, if loading has failed. + + \sa QPdfDocument::status() +*/ + +/*! + Returns the current status of the document. +*/ +QPdfDocument::Status QPdfDocument::status() const +{ + return d->status; +} + +void QPdfDocument::load(QIODevice *device) +{ + close(); + + d->setStatus(QPdfDocument::Loading); + + d->load(device, /*transfer ownership*/false); +} + +void QPdfDocument::setPassword(const QString &password) +{ + const QByteArray newPassword = password.toUtf8(); + + if (d->password == newPassword) + return; + + d->password = newPassword; + emit passwordChanged(); +} + +QString QPdfDocument::password() const +{ + return QString::fromUtf8(d->password); +} + +/*! + \enum QPdfDocument::MetaDataField + + This enum describes the available fields of meta data. + + \value Title The document's title as QString. + \value Author The name of the person who created the document as QString. + \value Subject The subject of the document as QString. + \value Keywords Keywords associated with the document as QString. + \value Creator If the document was converted to PDF from another format, + the name of the conforming product that created the original document + from which it was converted as QString. + \value Producer If the document was converted to PDF from another format, + the name of the conforming product that converted it to PDF as QString. + \value CreationDate The date and time the document was created as QDateTime. + \value ModificationDate The date and time the document was most recently modified as QDateTime. + + \sa QPdfDocument::metaData() +*/ + +/*! + Returns the meta data of the document for the given \a field. +*/ +QVariant QPdfDocument::metaData(MetaDataField field) const +{ + if (!d->doc) + return QString(); + + QByteArray fieldName; + switch (field) { + case Title: + fieldName = "Title"; + break; + case Subject: + fieldName = "Subject"; + break; + case Author: + fieldName = "Author"; + break; + case Keywords: + fieldName = "Keywords"; + break; + case Producer: + fieldName = "Producer"; + break; + case Creator: + fieldName = "Creator"; + break; + case CreationDate: + fieldName = "CreationDate"; + break; + case ModificationDate: + fieldName = "ModDate"; + break; + } + + QPdfMutexLocker lock; + const unsigned long len = FPDF_GetMetaText(d->doc, fieldName.constData(), nullptr, 0); + + QVector<ushort> buf(len); + FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.length()); + lock.unlock(); + + QString text = QString::fromUtf16(buf.data()); + + switch (field) { + case Title: // fall through + case Subject: + case Author: + case Keywords: + case Producer: + case Creator: + return text; + case CreationDate: // fall through + case ModificationDate: + // convert a "D:YYYYMMDDHHmmSSOHH'mm'" into "YYYY-MM-DDTHH:mm:ss+HH:mm" + if (text.startsWith(QLatin1String("D:"))) + text = text.mid(2); + text.insert(4, QLatin1Char('-')); + text.insert(7, QLatin1Char('-')); + text.insert(10, QLatin1Char('T')); + text.insert(13, QLatin1Char(':')); + text.insert(16, QLatin1Char(':')); + text.replace(QLatin1Char('\''), QLatin1Char(':')); + if (text.endsWith(QLatin1Char(':'))) + text.chop(1); + + return QDateTime::fromString(text, Qt::ISODate); + } + + return QVariant(); +} + +QPdfDocument::DocumentError QPdfDocument::error() const +{ + return d->lastError; +} + +/*! + Closes the document. +*/ +void QPdfDocument::close() +{ + if (!d->doc) + return; + + d->setStatus(Unloading); + + d->clear(); + + if (!d->password.isEmpty()) { + d->password.clear(); + emit passwordChanged(); + } + + d->setStatus(Null); +} + +/*! + Returns the amount of pages for the loaded document or \c 0 if + no document is loaded. +*/ +int QPdfDocument::pageCount() const +{ + return d->pageCount; +} + +QSizeF QPdfDocument::pageSize(int page) const +{ + QSizeF result; + if (!d->doc) + return result; + + const QPdfMutexLocker lock; + + FPDF_GetPageSizeByIndex(d->doc, page, &result.rwidth(), &result.rheight()); + return result; +} + +/*! + Renders the \a page into a QImage of size \a imageSize according to the + provided \a renderOptions. + + Returns the rendered page or an empty image in case of an error. + + Note: If the \a imageSize does not match the aspect ratio of the page in the + PDF document, the page is rendered scaled, so that it covers the + complete \a imageSize. +*/ +QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions renderOptions) +{ + if (!d->doc) + return QImage(); + + const QPdfMutexLocker lock; + + QElapsedTimer timer; + if (Q_UNLIKELY(qLcDoc().isDebugEnabled())) + timer.start(); + FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); + if (!pdfPage) + return QImage(); + + QImage result(imageSize, QImage::Format_ARGB32); + result.fill(Qt::transparent); + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(result.width(), result.height(), FPDFBitmap_BGRA, result.bits(), result.bytesPerLine()); + + int rotation = 0; + switch (renderOptions.rotation()) { + case QPdf::Rotate0: + rotation = 0; + break; + case QPdf::Rotate90: + rotation = 1; + break; + case QPdf::Rotate180: + rotation = 2; + break; + case QPdf::Rotate270: + rotation = 3; + break; + } + + const QPdf::RenderFlags renderFlags = renderOptions.renderFlags(); + int flags = 0; + if (renderFlags & QPdf::RenderAnnotations) + flags |= FPDF_ANNOT; + if (renderFlags & QPdf::RenderOptimizedForLcd) + flags |= FPDF_LCD_TEXT; + if (renderFlags & QPdf::RenderGrayscale) + flags |= FPDF_GRAYSCALE; + if (renderFlags & QPdf::RenderForceHalftone) + flags |= FPDF_RENDER_FORCEHALFTONE; + if (renderFlags & QPdf::RenderTextAliased) + flags |= FPDF_RENDER_NO_SMOOTHTEXT; + if (renderFlags & QPdf::RenderImageAliased) + flags |= FPDF_RENDER_NO_SMOOTHIMAGE; + if (renderFlags & QPdf::RenderPathAliased) + flags |= FPDF_RENDER_NO_SMOOTHPATH; + + FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags); + + FPDFBitmap_Destroy(bitmap); + + FPDF_ClosePage(pdfPage); + qCDebug(qLcDoc) << "page" << page << imageSize << "took" << timer.elapsed() << "ms"; + return result; +} + +QT_END_NAMESPACE + +#include "moc_qpdfdocument.cpp" diff --git a/src/pdf/qpdfdocumentrenderoptions.qdoc b/src/pdf/qpdfdocumentrenderoptions.qdoc new file mode 100644 index 000000000..cafb0afc1 --- /dev/null +++ b/src/pdf/qpdfdocumentrenderoptions.qdoc @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfdocumentrenderoptions.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QPdfDocumentRenderOptions + \since 5.10 + \inmodule QtPdf + + \brief The QPdfDocumentRenderOptions class holds the options to render a page from a PDF document. + + \sa QPdfDocument +*/ + +/*! + \fn QPdfDocumentRenderOptions::QPdfDocumentRenderOptions() + + Constructs a QPdfDocumentRenderOptions object. +*/ + +/*! + \fn QPdf::Rotation QPdfDocumentRenderOptions::rotation() const + + Returns the rotation used for rendering a page from a PDF document. + + \sa setRotation() +*/ + +/*! + \fn void QPdfDocumentRenderOptions::setRotation(QPdf::Rotation rotation) + + Sets the \a rotation used for rendering a page from a PDF document. + + \sa rotation() +*/ + +/*! + \fn QPdf::RenderFlags QPdfDocumentRenderOptions::renderFlags() const + + Returns the special flags used for rendering a page from a PDF document. + + \sa setRenderFlags() +*/ + +/*! + \fn void QPdfDocumentRenderOptions::setRenderFlags(QPdf::RenderFlags flags) + + Sets the special \a flags used for rendering a page from a PDF document. + + \sa renderFlags() +*/ + +/*! + \fn bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) + \relates QPdfDocumentRenderOptions + + Returns \c true if the options \a lhs and \a rhs are different, otherwise + returns \c false. +*/ + +/*! + \fn bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) + \relates QPdfDocumentRenderOptions + + Returns \c true if the options \a lhs and \a rhs are equal, + otherwise returns \c false. +*/ + +QT_END_NAMESPACE diff --git a/src/pdf/qpdfnamespace.qdoc b/src/pdf/qpdfnamespace.qdoc new file mode 100644 index 000000000..96bb090e9 --- /dev/null +++ b/src/pdf/qpdfnamespace.qdoc @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \namespace QPdf + \inmodule QtPdf + \keyword QPdf Namespace + + \brief The QPdf namespace contains miscellaneous identifiers + used throughout the QtPdf module. +*/ + +/*! + \enum QPdf::Rotation + + This enum describes the rotation of the page for rendering. + + \value Rotate0 Do not rotate (the default) + \value Rotate90 Rotate 90 degrees clockwise + \value Rotate180 Rotate 180 degrees + \value Rotate270 Rotate 270 degrees clockwise + + \sa QPdfDocument::render() +*/ +/*! + \enum QPdf::RenderFlag + + This enum is used to describe how a page should be rendered. + + \value NoRenderFlags The default value, representing no flags. + \value RenderAnnotations The page is rendered with annotations. + \value RenderOptimizedForLcd The text of the page is rendered optimized for LCD display. + \value RenderGrayscale The page is rendered grayscale. + \value RenderForceHalftone Always use halftones for rendering if the output image is stretched. + \value RenderTextAliased Anti-aliasing is disabled for rendering text. + \value RenderImageAliased Anti-aliasing is disabled for rendering images. + \value RenderPathAliased Anti-aliasing is disabled for rendering paths. + + \sa QPdfDocument::render() +*/ + diff --git a/src/pdf/qpdfpagenavigation.cpp b/src/pdf/qpdfpagenavigation.cpp new file mode 100644 index 000000000..497c1c2eb --- /dev/null +++ b/src/pdf/qpdfpagenavigation.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfpagenavigation.h" + +#include "qpdfdocument.h" + +#include <private/qobject_p.h> + +#include <QPointer> + +QT_BEGIN_NAMESPACE + +class QPdfPageNavigationPrivate : public QObjectPrivate +{ +public: + QPdfPageNavigationPrivate() + : QObjectPrivate() + { + } + + void update() + { + Q_Q(QPdfPageNavigation); + + const bool documentAvailable = m_document && m_document->status() == QPdfDocument::Ready; + + if (documentAvailable) { + const int newPageCount = m_document->pageCount(); + if (m_pageCount != newPageCount) { + m_pageCount = newPageCount; + emit q->pageCountChanged(m_pageCount); + } + } else { + if (m_pageCount != 0) { + m_pageCount = 0; + emit q->pageCountChanged(m_pageCount); + } + } + + if (m_currentPage != 0) { + m_currentPage = 0; + emit q->currentPageChanged(m_currentPage); + } + + updatePrevNext(); + } + + void updatePrevNext() + { + Q_Q(QPdfPageNavigation); + + const bool hasPreviousPage = m_currentPage > 0; + const bool hasNextPage = m_currentPage < (m_pageCount - 1); + + if (m_canGoToPreviousPage != hasPreviousPage) { + m_canGoToPreviousPage = hasPreviousPage; + emit q->canGoToPreviousPageChanged(m_canGoToPreviousPage); + } + + if (m_canGoToNextPage != hasNextPage) { + m_canGoToNextPage = hasNextPage; + emit q->canGoToNextPageChanged(m_canGoToNextPage); + } + } + + void documentStatusChanged() + { + update(); + } + + Q_DECLARE_PUBLIC(QPdfPageNavigation) + + QPointer<QPdfDocument> m_document = nullptr; + int m_currentPage = 0; + int m_pageCount = 0; + bool m_canGoToPreviousPage = false; + bool m_canGoToNextPage = false; + + QMetaObject::Connection m_documentStatusChangedConnection; +}; + +/*! + \class QPdfPageNavigation + \since 5.10 + \inmodule QtPdf + + \brief The QPdfPageNavigation class handles the navigation through a PDF document. + + \sa QPdfDocument +*/ + + +/*! + Constructs a page navigation object with parent object \a parent. +*/ +QPdfPageNavigation::QPdfPageNavigation(QObject *parent) + : QObject(*new QPdfPageNavigationPrivate, parent) +{ +} + +/*! + Destroys the page navigation object. +*/ +QPdfPageNavigation::~QPdfPageNavigation() +{ +} + +/*! + \property QPdfPageNavigation::document + \brief The document instance on which this object navigates. + + By default, this property is \c nullptr. + + \sa document(), setDocument(), QPdfDocument +*/ + +/*! + Returns the document on which this object navigates, or a \c nullptr + if none has set before. + + \sa QPdfDocument +*/ +QPdfDocument* QPdfPageNavigation::document() const +{ + Q_D(const QPdfPageNavigation); + + return d->m_document; +} + +/*! + Sets the \a document this object navigates on. + + After a new document has been set, the currentPage will be \c 0. + + \sa QPdfDocument +*/ +void QPdfPageNavigation::setDocument(QPdfDocument *document) +{ + Q_D(QPdfPageNavigation); + + if (d->m_document == document) + return; + + if (d->m_document) + disconnect(d->m_documentStatusChangedConnection); + + d->m_document = document; + emit documentChanged(d->m_document); + + if (d->m_document) + d->m_documentStatusChangedConnection = connect(d->m_document.data(), &QPdfDocument::statusChanged, this, [d](){ d->documentStatusChanged(); }); + + d->update(); +} + +/*! + \property QPdfPageNavigation::currentPage + \brief The current page number in the document. + + \sa currentPage(), setCurrentPage() +*/ + +/*! + Returns the current page number or \c 0 if there is no document set. + + After a document has been loaded, the currentPage will always be \c 0. +*/ +int QPdfPageNavigation::currentPage() const +{ + Q_D(const QPdfPageNavigation); + + return d->m_currentPage; +} + +/*! + \fn void QPdfPageNavigation::setCurrentPage(int page) + + Sets the current \a page number. +*/ +void QPdfPageNavigation::setCurrentPage(int newPage) +{ + Q_D(QPdfPageNavigation); + + if (newPage < 0 || newPage >= d->m_pageCount) + return; + + if (d->m_currentPage == newPage) + return; + + d->m_currentPage = newPage; + emit currentPageChanged(d->m_currentPage); + + d->updatePrevNext(); +} + +/*! + \property QPdfPageNavigation::pageCount + \brief The number of pages in the document. + + \sa pageCount() +*/ + +/*! + Returns the number of pages in the document or \c 0 if there + is no document set. +*/ +int QPdfPageNavigation::pageCount() const +{ + Q_D(const QPdfPageNavigation); + + return d->m_pageCount; +} + +/*! + \property QPdfPageNavigation::canGoToPreviousPage + \brief Indicates whether there is a page before the current page. + + \sa canGoToPreviousPage(), goToPreviousPage() +*/ + +/*! + Returns whether there is a page before the current one. +*/ +bool QPdfPageNavigation::canGoToPreviousPage() const +{ + Q_D(const QPdfPageNavigation); + + return d->m_canGoToPreviousPage; +} + +/*! + \property QPdfPageNavigation::canGoToNextPage + \brief Indicates whether there is a page after the current page. + + \sa canGoToNextPage(), goToNextPage() +*/ + +/*! + Returns whether there is a page after the current one. +*/ +bool QPdfPageNavigation::canGoToNextPage() const +{ + Q_D(const QPdfPageNavigation); + + return d->m_canGoToNextPage; +} + +/*! + Changes the current page to the previous page. + + If there is no previous page in the document, nothing happens. + + \sa canGoToPreviousPage +*/ +void QPdfPageNavigation::goToPreviousPage() +{ + Q_D(QPdfPageNavigation); + + if (d->m_currentPage > 0) + setCurrentPage(d->m_currentPage - 1); +} + +/*! + Changes the current page to the next page. + + If there is no next page in the document, nothing happens. + + \sa canGoToNextPage +*/ +void QPdfPageNavigation::goToNextPage() +{ + Q_D(QPdfPageNavigation); + + if (d->m_currentPage < d->m_pageCount - 1) + setCurrentPage(d->m_currentPage + 1); +} + +QT_END_NAMESPACE + +#include "moc_qpdfpagenavigation.cpp" diff --git a/src/pdf/qpdfpagerenderer.cpp b/src/pdf/qpdfpagerenderer.cpp new file mode 100644 index 000000000..e90f66700 --- /dev/null +++ b/src/pdf/qpdfpagerenderer.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfpagerenderer.h" + +#include <private/qobject_p.h> +#include <QMutex> +#include <QPdfDocument> +#include <QPointer> +#include <QThread> + +QT_BEGIN_NAMESPACE + +class RenderWorker : public QObject +{ + Q_OBJECT + +public: + RenderWorker(); + ~RenderWorker(); + + void setDocument(QPdfDocument *document); + +public Q_SLOTS: + void requestPage(quint64 requestId, int page, QSize imageSize, + QPdfDocumentRenderOptions options); + +Q_SIGNALS: + void pageRendered(int page, QSize imageSize, const QImage &image, + QPdfDocumentRenderOptions options, quint64 requestId); + +private: + QPointer<QPdfDocument> m_document; + QMutex m_mutex; +}; + +class QPdfPageRendererPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QPdfPageRenderer) + +public: + QPdfPageRendererPrivate(); + ~QPdfPageRendererPrivate(); + + void handleNextRequest(); + void requestFinished(int page, QSize imageSize, const QImage &image, + QPdfDocumentRenderOptions options, quint64 requestId); + + QPdfPageRenderer::RenderMode m_renderMode = QPdfPageRenderer::SingleThreadedRenderMode; + QPointer<QPdfDocument> m_document; + + struct PageRequest + { + quint64 id; + int pageNumber; + QSize imageSize; + QPdfDocumentRenderOptions options; + }; + + QVector<PageRequest> m_requests; + QVector<PageRequest> m_pendingRequests; + quint64 m_requestIdCounter = 1; + + QThread *m_renderThread = nullptr; + QScopedPointer<RenderWorker> m_renderWorker; +}; + +Q_DECLARE_TYPEINFO(QPdfPageRendererPrivate::PageRequest, Q_PRIMITIVE_TYPE); + + +RenderWorker::RenderWorker() + : m_document(nullptr) +{ +} + +RenderWorker::~RenderWorker() +{ +} + +void RenderWorker::setDocument(QPdfDocument *document) +{ + const QMutexLocker locker(&m_mutex); + + if (m_document == document) + return; + + m_document = document; +} + +void RenderWorker::requestPage(quint64 requestId, int pageNumber, QSize imageSize, + QPdfDocumentRenderOptions options) +{ + const QMutexLocker locker(&m_mutex); + + if (!m_document || m_document->status() != QPdfDocument::Ready) + return; + + const QImage image = m_document->render(pageNumber, imageSize, options); + + emit pageRendered(pageNumber, imageSize, image, options, requestId); +} + + +QPdfPageRendererPrivate::QPdfPageRendererPrivate() + : QObjectPrivate() + , m_renderWorker(new RenderWorker) +{ +} + +QPdfPageRendererPrivate::~QPdfPageRendererPrivate() +{ + if (m_renderThread) { + m_renderThread->quit(); + m_renderThread->wait(); + } +} + +void QPdfPageRendererPrivate::handleNextRequest() +{ + if (m_requests.isEmpty()) + return; + + const PageRequest request = m_requests.takeFirst(); + m_pendingRequests.append(request); + + QMetaObject::invokeMethod(m_renderWorker.data(), "requestPage", Qt::QueuedConnection, + Q_ARG(quint64, request.id), Q_ARG(int, request.pageNumber), + Q_ARG(QSize, request.imageSize), Q_ARG(QPdfDocumentRenderOptions, + request.options)); +} + +void QPdfPageRendererPrivate::requestFinished(int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId) +{ + const auto it = std::find_if(m_pendingRequests.begin(), m_pendingRequests.end(), + [page, imageSize, options](const PageRequest &request){ return request.pageNumber == page && request.imageSize == imageSize && request.options == options; }); + + if (it != m_pendingRequests.end()) + m_pendingRequests.erase(it); +} + +/*! + \class QPdfPageRenderer + \since 5.11 + \inmodule QtPdf + + \brief The QPdfPageRenderer class encapsulates the rendering of pages of a PDF document. + + The QPdfPageRenderer contains a queue that collects all render requests that are invoked through + requestPage(). Depending on the configured RenderMode the QPdfPageRenderer processes this queue + in the main UI thread on next event loop invocation (SingleThreadedRenderMode) or in a separate worker thread + (MultiThreadedRenderMode) and emits the result through the pageRendered() signal for each request once + the rendering is done. + + \sa QPdfDocument +*/ + + +/*! + Constructs a page renderer object with parent object \a parent. +*/ +QPdfPageRenderer::QPdfPageRenderer(QObject *parent) + : QObject(*new QPdfPageRendererPrivate(), parent) +{ + Q_D(QPdfPageRenderer); + + qRegisterMetaType<QPdfDocumentRenderOptions>(); + + connect(d->m_renderWorker.data(), &RenderWorker::pageRendered, this, + [this,d](int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId) { + d->requestFinished(page, imageSize, image, options, requestId); + emit pageRendered(page, imageSize, image, options, requestId); + d->handleNextRequest(); + }); +} + +/*! + Destroys the page renderer object. +*/ +QPdfPageRenderer::~QPdfPageRenderer() +{ +} + +/*! + \enum QPdfPageRenderer::RenderMode + + This enum describes how the pages are rendered. + + \value MultiThreadedRenderMode All pages are rendered in a separate worker thread. + \value SingleThreadedRenderMode All pages are rendered in the main UI thread (default). + + \sa renderMode(), setRenderMode() +*/ + +/*! + \property QPdfPageRenderer::renderMode + \brief The mode the renderer uses to render the pages. + + By default, this property is \c QPdfPageRenderer::SingleThreaded. + + \sa setRenderMode(), RenderMode +*/ + +/*! + Returns the mode of how the pages are rendered. + + \sa RenderMode +*/ +QPdfPageRenderer::RenderMode QPdfPageRenderer::renderMode() const +{ + Q_D(const QPdfPageRenderer); + + return d->m_renderMode; +} + +/*! + Sets the mode of how the pages are rendered to \a mode. + + \sa RenderMode +*/ +void QPdfPageRenderer::setRenderMode(RenderMode mode) +{ + Q_D(QPdfPageRenderer); + + if (d->m_renderMode == mode) + return; + + d->m_renderMode = mode; + emit renderModeChanged(d->m_renderMode); + + if (d->m_renderMode == MultiThreadedRenderMode) { + d->m_renderThread = new QThread; + d->m_renderWorker->moveToThread(d->m_renderThread); + d->m_renderThread->start(); + } else { + d->m_renderThread->quit(); + d->m_renderThread->wait(); + delete d->m_renderThread; + d->m_renderThread = nullptr; + + // pulling the object from another thread should be fine, once that thread is deleted + d->m_renderWorker->moveToThread(this->thread()); + } +} + +/*! + \property QPdfPageRenderer::document + \brief The document instance this object renders the pages from. + + By default, this property is \c nullptr. + + \sa document(), setDocument(), QPdfDocument +*/ + +/*! + Returns the document this objects renders the pages from, or a \c nullptr + if none has been set before. + + \sa QPdfDocument +*/ +QPdfDocument* QPdfPageRenderer::document() const +{ + Q_D(const QPdfPageRenderer); + + return d->m_document; +} + +/*! + Sets the \a document this object renders the pages from. + + \sa QPdfDocument +*/ +void QPdfPageRenderer::setDocument(QPdfDocument *document) +{ + Q_D(QPdfPageRenderer); + + if (d->m_document == document) + return; + + d->m_document = document; + emit documentChanged(d->m_document); + + d->m_renderWorker->setDocument(d->m_document); +} + +/*! + Requests the renderer to render the page \a pageNumber into a QImage of size \a imageSize + according to the provided \a options. + + Once the rendering is done the pageRendered() signal is emitted with the result as parameters. + + The return value is an ID that uniquely identifies the render request. If a request with the + same parameters is still in the queue, the ID of that queued request is returned. +*/ +quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize, + QPdfDocumentRenderOptions options) +{ + Q_D(QPdfPageRenderer); + + if (!d->m_document || d->m_document->status() != QPdfDocument::Ready) + return 0; + + for (const auto request : qAsConst(d->m_pendingRequests)) { + if (request.pageNumber == pageNumber + && request.imageSize == imageSize + && request.options == options) + return request.id; + } + + const auto id = d->m_requestIdCounter++; + + QPdfPageRendererPrivate::PageRequest request; + request.id = id; + request.pageNumber = pageNumber; + request.imageSize = imageSize; + request.options = options; + + d->m_requests.append(request); + + d->handleNextRequest(); + + return id; +} + +QT_END_NAMESPACE + +#include "qpdfpagerenderer.moc" diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp new file mode 100644 index 000000000..9010d76d3 --- /dev/null +++ b/src/pdf/qpdfsearchmodel.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfsearchmodel.h" +#include "qpdfsearchmodel_p.h" +#include "qpdfdocument_p.h" + +#include "third_party/pdfium/public/fpdf_doc.h" +#include "third_party/pdfium/public/fpdf_text.h" + +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search") + +QPdfSearchModel::QPdfSearchModel(QObject *parent) + : QObject(parent), + d(new QPdfSearchModelPrivate()) +{ +} + +QPdfSearchModel::~QPdfSearchModel() {} + +QVector<QRectF> QPdfSearchModel::matches(int page, const QString &searchString) +{ + const QPdfMutexLocker lock; + FPDF_PAGE pdfPage = FPDF_LoadPage(d->document->d->doc, page); + if (!pdfPage) { + qWarning() << "failed to load page" << page; + return {}; + } + double pageHeight = FPDF_GetPageHeight(pdfPage); + FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); + if (!textPage) { + qWarning() << "failed to load text of page" << page; + FPDF_ClosePage(pdfPage); + return {}; + } + QVector<QRectF> ret; + if (searchString.isEmpty()) + return ret; + FPDF_SCHHANDLE sh = FPDFText_FindStart(textPage, searchString.utf16(), 0, 0); + while (FPDFText_FindNext(sh)) { + int idx = FPDFText_GetSchResultIndex(sh); + int count = FPDFText_GetSchCount(sh); + int rectCount = FPDFText_CountRects(textPage, idx, count); + qCDebug(qLcS) << searchString << ": matched" << count << "chars @" << idx << "across" << rectCount << "rects"; + for (int r = 0; r < rectCount; ++r) { + double left, top, right, bottom; + FPDFText_GetRect(textPage, r, &left, &top, &right, &bottom); + ret << QRectF(left, pageHeight - top, right - left, top - bottom); + qCDebug(qLcS) << ret.last(); + } + } + FPDFText_FindClose(sh); + FPDFText_ClosePage(textPage); + FPDF_ClosePage(pdfPage); + + return ret; +} + +QPdfDocument *QPdfSearchModel::document() const +{ + return d->document; +} + +void QPdfSearchModel::setDocument(QPdfDocument *document) +{ + if (d->document == document) + return; + d->document = document; + emit documentChanged(); +} + +QPdfSearchModelPrivate::QPdfSearchModelPrivate() +{ +} + +QT_END_NAMESPACE + +#include "moc_qpdfsearchmodel.cpp" diff --git a/src/pdf/qpdfsearchmodel_p.h b/src/pdf/qpdfsearchmodel_p.h new file mode 100644 index 000000000..90490d8e5 --- /dev/null +++ b/src/pdf/qpdfsearchmodel_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFSEARCHMODEL_P_H +#define QPDFSEARCHMODEL_P_H + +#include "qpdfsearchmodel.h" + +#include "third_party/pdfium/public/fpdfview.h" + +QT_BEGIN_NAMESPACE + +class QPdfSearchModelPrivate +{ +public: + QPdfSearchModelPrivate(); + + QPdfDocument *document = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QPDFSEARCHMODEL_P_H diff --git a/src/pdf/qtpdf.gni b/src/pdf/qtpdf.gni new file mode 100644 index 000000000..c31f3e9a0 --- /dev/null +++ b/src/pdf/qtpdf.gni @@ -0,0 +1,7 @@ +include_dirs = [ +] + +deps = [ + "//third_party/pdfium" +] + diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp new file mode 100644 index 000000000..664ba51ab --- /dev/null +++ b/src/pdf/quick/plugin.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlextensionplugin.h> +#include "qquickpdfdocument_p.h" +#include "qquickpdfsearchmodel_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQuick.Pdf 5.15 + \title Qt Quick PDF QML Types + \ingroup qmlmodules + \brief Provides QML types for handling PDF documents. + + This QML module contains types for handling PDF documents. + + To use the types in this module, import the module with the following line: + + \code + import QtQuick.Pdf 5.15 + \endcode +*/ + +class QtQuick2PdfPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QtQuick2PdfPlugin() : QQmlExtensionPlugin() { } + + void initializeEngine(QQmlEngine *engine, const char *uri) override { + Q_UNUSED(uri); +#ifndef QT_STATIC + engine->addImportPath(QStringLiteral("qrc:/")); +#endif + } + + void registerTypes(const char *uri) override { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Pdf")); + + // Register the latest version, even if there are no new types or new revisions for existing types yet. + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); + + qmlRegisterType<QQuickPdfDocument>(uri, 5, 15, "PdfDocument"); + qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel"); + + qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/pdf/quick/plugins.qmltypes b/src/pdf/quick/plugins.qmltypes new file mode 100644 index 000000000..a30361d33 --- /dev/null +++ b/src/pdf/quick/plugins.qmltypes @@ -0,0 +1,52 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable QtQuick.Pdf 5.14' + +Module { + dependencies: [ + "QtGraphicalEffects 1.12", + "QtQuick 2.14", + "QtQuick.Controls 2.14", + "QtQuick.Controls.Fusion 2.14", + "QtQuick.Controls.Fusion.impl 2.14", + "QtQuick.Controls.Imagine 2.14", + "QtQuick.Controls.Imagine.impl 2.14", + "QtQuick.Controls.Material 2.14", + "QtQuick.Controls.Material.impl 2.14", + "QtQuick.Controls.Universal 2.14", + "QtQuick.Controls.Universal.impl 2.12", + "QtQuick.Controls.impl 2.14", + "QtQuick.Shapes 1.14", + "QtQuick.Templates 2.14", + "QtQuick.Window 2.2" + ] + Component { + name: "QQuickPdfDocument" + prototype: "QObject" + exports: ["QtQuick.Pdf/PdfDocument 5.14"] + exportMetaObjectRevisions: [0] + Property { name: "source"; type: "QUrl" } + Property { name: "pageCount"; type: "int"; isReadonly: true } + Property { name: "password"; type: "string" } + Property { name: "status"; type: "QPdfDocument::Status"; isReadonly: true } + Property { name: "title"; type: "string"; isReadonly: true } + Property { name: "subject"; type: "string"; isReadonly: true } + Property { name: "author"; type: "string"; isReadonly: true } + Property { name: "keywords"; type: "string"; isReadonly: true } + Property { name: "producer"; type: "string"; isReadonly: true } + Property { name: "creator"; type: "string"; isReadonly: true } + Property { name: "creationDate"; type: "QDateTime"; isReadonly: true } + Property { name: "modificationDate"; type: "QDateTime"; isReadonly: true } + Signal { name: "passwordRequired" } + Signal { name: "metaDataLoaded" } + Method { + name: "pagePointSize" + type: "QSizeF" + Parameter { name: "page"; type: "int" } + } + } +} diff --git a/src/pdf/quick/qml/PdfPageView.qml b/src/pdf/quick/qml/PdfPageView.qml new file mode 100644 index 000000000..b7f75f4c2 --- /dev/null +++ b/src/pdf/quick/qml/PdfPageView.qml @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Pdf 5.15 +import QtQuick.Shapes 1.15 + +Rectangle { + id: paper + width: image.width + height: image.height + + // public API + // TODO 5.15: required property + property var document: null + property real renderScale: 1 + property alias sourceSize: image.sourceSize + property alias currentPage: image.currentFrame + property alias pageCount: image.frameCount + property alias searchString: searchModel.searchString + property alias status: image.status + + property real __pageScale: image.paintedWidth / document.pagePointSize(image.currentFrame).width + + PdfSearchModel { + id: searchModel + document: paper.document + page: image.currentFrame + } + + Image { + id: image + source: document.status === PdfDocument.Ready ? document.source : "" + asynchronous: true + fillMode: Image.PreserveAspectFit + } + function reRenderIfNecessary() { + var newSourceWidth = image.sourceSize.width * paper.scale + var ratio = newSourceWidth / image.sourceSize.width + if (ratio > 1.1 || ratio < 0.9) { + image.sourceSize.width = newSourceWidth + image.sourceSize.height = 1 + paper.scale = 1 + } + } + onRenderScaleChanged: { + image.sourceSize.width = document.pagePointSize(image.currentFrame).width * renderScale + image.sourceSize.height = 0 + paper.scale = 1 + } + + Shape { + anchors.fill: parent + opacity: 0.25 + visible: image.status === Image.Ready + ShapePath { + strokeWidth: 1 + strokeColor: "blue" + fillColor: "cyan" + scale: Qt.size(paper.__pageScale, paper.__pageScale) + PathMultiline { + id: searchResultBoundaries + paths: searchModel.matchGeometry + } + } + } + PinchHandler { + id: pinch + minimumScale: 0.1 + maximumScale: 10 + minimumRotation: 0 + maximumRotation: 0 + onActiveChanged: if (!active) paper.reRenderIfNecessary() + grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started + } + DragHandler { + id: pageMovingTouchDrag + acceptedDevices: PointerDevice.TouchScreen + } + DragHandler { + id: pageMovingMiddleMouseDrag + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.MiddleButton + snapMode: DragHandler.NoSnap + } +} diff --git a/src/pdf/quick/qmldir b/src/pdf/quick/qmldir new file mode 100644 index 000000000..65fa95cda --- /dev/null +++ b/src/pdf/quick/qmldir @@ -0,0 +1,4 @@ +module QtQuick.Pdf +plugin pdfplugin +classname QtQuick2PdfPlugin +typeinfo plugins.qmltypes diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp new file mode 100644 index 000000000..73b3d4537 --- /dev/null +++ b/src/pdf/quick/qquickpdfdocument.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpdfdocument_p.h" +#include <QQuickItem> +#include <QQmlEngine> +#include <QStandardPaths> +#include <private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Document + \instantiates QQuickPdfDocument + \inqmlmodule QtQuick.Pdf + \ingroup pdf + \brief A representation of a PDF document. + \since 5.15 + + A Document provides access to PDF document meta-information. + It is not necessary for rendering, as it is enough to use an + \l Image with source set to the URL of the PDF. +*/ + +/*! + Constructs a PDF document. +*/ +QQuickPdfDocument::QQuickPdfDocument(QObject *parent) + : QObject(parent) +{ + connect(&m_doc, &QPdfDocument::passwordChanged, this, &QQuickPdfDocument::passwordChanged); + connect(&m_doc, &QPdfDocument::passwordRequired, this, &QQuickPdfDocument::passwordRequired); + connect(&m_doc, &QPdfDocument::statusChanged, [=] (QPdfDocument::Status status) { + emit statusChanged(); + if (status == QPdfDocument::Ready) + emit metaDataChanged(); + }); + connect(&m_doc, &QPdfDocument::pageCountChanged, this, &QQuickPdfDocument::pageCountChanged); +} + +void QQuickPdfDocument::componentComplete() +{ + if (m_doc.error() == QPdfDocument::IncorrectPasswordError) + emit passwordRequired(); +} + +/*! + \qmlproperty url Document::source + + This property holds a URL pointing to the PDF file to be loaded. + + \note At this time, only local filesystem URLs are supported. +*/ +void QQuickPdfDocument::setSource(QUrl source) +{ + if (m_source == source) + return; + + m_source = source; + emit sourceChanged(); + m_doc.load(source.path()); +} + +/*! + \qmlproperty string Document::error + + This property holds a translated string representation of the current + error, if any. + + \sa status +*/ +QString QQuickPdfDocument::error() const +{ + switch (m_doc.error()) { + case QPdfDocument::NoError: + return tr("no error"); + break; + case QPdfDocument::UnknownError: + break; + case QPdfDocument::DataNotYetAvailableError: + return tr("data not yet available"); + break; + case QPdfDocument::FileNotFoundError: + return tr("file not found"); + break; + case QPdfDocument::InvalidFileFormatError: + return tr("invalid file format"); + break; + case QPdfDocument::IncorrectPasswordError: + return tr("incorrect password"); + break; + case QPdfDocument::UnsupportedSecuritySchemeError: + return tr("unsupported security scheme"); + break; + } + return tr("unknown error"); +} + +/*! + \qmlproperty bool Document::password + + This property holds the document password. If the passwordRequired() + signal is emitted, the UI should prompt the user and then set this + property so that document opening can continue. +*/ +void QQuickPdfDocument::setPassword(const QString &password) +{ + if (m_doc.password() == password) + return; + m_doc.setPassword(password); + if (source().isValid() && source().isLocalFile()) + m_doc.load(source().path()); +} + +/*! + \qmlproperty int Document::pageCount + + This property holds the number of pages the PDF contains. +*/ + +/*! + \qmlsignal Document::passwordRequired() + + This signal is emitted when the PDF requires a password in order to open. + The UI in a typical PDF viewer should prompt the user for the password + and then set the password property when the user has provided it. +*/ + +/*! + \qmlmethod size Document::pagePointSize(int page) + + Returns the size of the given \a page in points. +*/ +QSizeF QQuickPdfDocument::pagePointSize(int page) const +{ + return m_doc.pageSize(page); +} + +/*! + \qmlproperty string Document::title + + This property holds the document's title. A typical viewer UI can bind this + to the \c Window.title property. +*/ + +/*! + \qmlproperty string Document::author + + This property holds the name of the person who created the document. +*/ + +/*! + \qmlproperty string Document::subject + + This property holds the subject of the document. +*/ + +/*! + \qmlproperty string Document::keywords + + This property holds the keywords associated with the document. +*/ + +/*! + \qmlproperty string Document::creator + + If the document was converted to PDF from another format, this property + holds the name of the software that created the original document. +*/ + +/*! + \qmlproperty string Document::producer + + If the document was converted to PDF from another format, this property + holds the name of the software that converted it to PDF. +*/ + +/*! + \qmlproperty string Document::creationDate + + This property holds the date and time the document was created. +*/ + +/*! + \qmlproperty string Document::modificationDate + + This property holds the date and time the document was most recently + modified. +*/ + +/*! + \qmlproperty enum Document::status + + This property tells the current status of the document. The possible values are: + + \value PdfDocument.Null The initial status after the document has been created or after it has been closed. + \value PdfDocument.Loading The status after load() has been called and before the document is fully loaded. + \value PdfDocument.Ready The status when the document is fully loaded and its data can be accessed. + \value PdfDocument.Unloading The status after close() has been called on an open document. + At this point the document is still valid and all its data can be accessed. + \value PdfDocument.Error The status after Loading, if loading has failed. +*/ + +QT_END_NAMESPACE diff --git a/src/pdf/quick/qquickpdfdocument_p.h b/src/pdf/quick/qquickpdfdocument_p.h new file mode 100644 index 000000000..1ec7edb1a --- /dev/null +++ b/src/pdf/quick/qquickpdfdocument_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPDFDOCUMENT_P_H +#define QQUICKPDFDOCUMENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtPdf/QPdfDocument> +#include <QDateTime> +#include <QJSValue> +#include <QQmlParserStatus> +#include <QUrl> +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class QQuickPdfDocument : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL) + Q_PROPERTY(QPdfDocument::Status status READ status NOTIFY statusChanged FINAL) + Q_PROPERTY(QString error READ error NOTIFY statusChanged FINAL) + + Q_PROPERTY(QString title READ title NOTIFY metaDataChanged) + Q_PROPERTY(QString subject READ subject NOTIFY metaDataChanged) + Q_PROPERTY(QString author READ author NOTIFY metaDataChanged) + Q_PROPERTY(QString keywords READ keywords NOTIFY metaDataChanged) + Q_PROPERTY(QString producer READ producer NOTIFY metaDataChanged) + Q_PROPERTY(QString creator READ creator NOTIFY metaDataChanged) + Q_PROPERTY(QDateTime creationDate READ creationDate NOTIFY metaDataChanged) + Q_PROPERTY(QDateTime modificationDate READ modificationDate NOTIFY metaDataChanged) + +public: + explicit QQuickPdfDocument(QObject *parent = nullptr); + + void classBegin() override {} + void componentComplete() override; + + QUrl source() const { return m_source; } + void setSource(QUrl source); + + int pageCount() const { return m_doc.pageCount(); } + QPdfDocument::Status status() const { return m_doc.status(); } + + QString error() const; + + QString password() const { return m_doc.password(); } + void setPassword(const QString &password); + + QString title() { return m_doc.metaData(QPdfDocument::Title).toString(); } + QString author() { return m_doc.metaData(QPdfDocument::Author).toString(); } + QString subject() { return m_doc.metaData(QPdfDocument::Subject).toString(); } + QString keywords() { return m_doc.metaData(QPdfDocument::Keywords).toString(); } + QString producer() { return m_doc.metaData(QPdfDocument::Producer).toString(); } + QString creator() { return m_doc.metaData(QPdfDocument::Creator).toString(); } + QDateTime creationDate() { return m_doc.metaData(QPdfDocument::CreationDate).toDateTime(); } + QDateTime modificationDate() { return m_doc.metaData(QPdfDocument::ModificationDate).toDateTime(); } + + Q_INVOKABLE QSizeF pagePointSize(int page) const; + +Q_SIGNALS: + void sourceChanged(); + void passwordChanged(); + void passwordRequired(); + void statusChanged(); + void pageCountChanged(); + void metaDataChanged(); + +private: + QPdfDocument &document() { return m_doc; } + +private: + QUrl m_source; + QPdfDocument m_doc; + + friend class QQuickPdfSearchModel; + friend class QQuickPdfSelection; + + Q_DISABLE_COPY(QQuickPdfDocument) +}; + +QT_END_NAMESPACE + +#endif // QQUICKPDFDOCUMENT_P_H diff --git a/src/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp new file mode 100644 index 000000000..8b0e88673 --- /dev/null +++ b/src/pdf/quick/qquickpdfsearchmodel.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpdfsearchmodel_p.h" +#include <QQuickItem> +#include <QQmlEngine> +#include <QStandardPaths> +#include <private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PdfSearchModel + \instantiates QQuickPdfSearchModel + \inqmlmodule QtQuick.Pdf + \ingroup pdf + \brief A representation of text search results within a PDF Document. + \since 5.15 + + PdfSearchModel provides the ability to search for text strings within a + document and get the geometric locations of matches on each page. +*/ + +QQuickPdfSearchModel::QQuickPdfSearchModel(QObject *parent) + : QPdfSearchModel(parent) +{ +} + +QQuickPdfDocument *QQuickPdfSearchModel::document() const +{ + return m_quickDocument; +} + +void QQuickPdfSearchModel::setDocument(QQuickPdfDocument *document) +{ + if (document == m_quickDocument) + return; + m_quickDocument = document; + QPdfSearchModel::setDocument(&document->m_doc); +} + +/*! + \qmlproperty list<list<point>> PdfSearchModel::matchGeometry + + A set of paths in a form that can be bound to the \c paths property of a + \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of + rectangles around all the locations where search results are found: + + \qml + PdfDocument { + id: doc + } + PdfSearchModel { + id: searchModel + document: doc + page: doc.currentPage + } + Shape { + ShapePath { + PathMultiline { + paths: searchModel.matchGeometry + } + } + } + \endqml + + \sa PathMultiline +*/ +QVector<QPolygonF> QQuickPdfSearchModel::matchGeometry() const +{ + return m_matchGeometry; +} + +/*! + \qmlproperty string PdfSearchModel::searchString + + The string to search for. +*/ +QString QQuickPdfSearchModel::searchString() const +{ + return m_searchString; +} + +void QQuickPdfSearchModel::setSearchString(QString searchString) +{ + if (m_searchString == searchString) + return; + + m_searchString = searchString; + emit searchStringChanged(); + updateResults(); +} + +/*! + \qmlproperty int PdfSearchModel::page + + The page number on which to search. + + \sa QtQuick::Image::currentFrame +*/ +int QQuickPdfSearchModel::page() const +{ + return m_page; +} + +void QQuickPdfSearchModel::setPage(int page) +{ + if (m_page == page) + return; + + m_page = page; + emit pageChanged(); + updateResults(); +} + +void QQuickPdfSearchModel::updateResults() +{ + if (!document() || (m_searchString.isEmpty() && !m_matchGeometry.isEmpty()) || m_page < 0 || m_page > document()->pageCount()) { + m_matchGeometry.clear(); + emit matchGeometryChanged(); + } + QVector<QRectF> m = QPdfSearchModel::matches(m_page, m_searchString); + QVector<QPolygonF> matches; + for (QRectF r : m) + matches << QPolygonF(r); + if (matches != m_matchGeometry) { + m_matchGeometry = matches; + emit matchGeometryChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/pdf/quick/qquickpdfsearchmodel_p.h b/src/pdf/quick/qquickpdfsearchmodel_p.h new file mode 100644 index 000000000..799ef825f --- /dev/null +++ b/src/pdf/quick/qquickpdfsearchmodel_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPDFSEARCHMODEL_P_H +#define QQUICKPDFSEARCHMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpdfdocument_p.h" +#include "../api/qpdfsearchmodel.h" + +#include <QVariant> +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class QQuickPdfSearchModel : public QPdfSearchModel +{ + Q_OBJECT + Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged) + Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged) + Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged) + Q_PROPERTY(QVector<QPolygonF> matchGeometry READ matchGeometry NOTIFY matchGeometryChanged) + +public: + explicit QQuickPdfSearchModel(QObject *parent = nullptr); + + QQuickPdfDocument *document() const; + void setDocument(QQuickPdfDocument * document); + + int page() const; + void setPage(int page); + + QString searchString() const; + void setSearchString(QString searchString); + + QVector<QPolygonF> matchGeometry() const; + +signals: + void documentChanged(); + void pageChanged(); + void searchStringChanged(); + void matchGeometryChanged(); + +private: + void updateResults(); + +private: + QQuickPdfDocument *m_quickDocument = nullptr; + QString m_searchString; + QVector<QPolygonF> m_matchGeometry; + int m_page; + + Q_DISABLE_COPY(QQuickPdfSearchModel) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPdfSearchModel) + +#endif // QQUICKPDFSEARCHMODEL_P_H diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro new file mode 100644 index 000000000..cda768369 --- /dev/null +++ b/src/pdf/quick/quick.pro @@ -0,0 +1,26 @@ +CXX_MODULE = qml +TARGET = pdfplugin +TARGETPATH = QtQuick/Pdf +IMPORT_VERSION = 1.0 + +#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf + +PDF_QML_FILES = \ + qml/PdfPageView.qml \ + +QML_FILES += $$PDF_QML_FILES qmldir + +RESOURCES += resources.qrc + +SOURCES += \ + plugin.cpp \ + qquickpdfdocument.cpp \ + qquickpdfsearchmodel.cpp \ + +HEADERS += \ + qquickpdfdocument_p.h \ + qquickpdfsearchmodel_p.h \ + +QT += pdf quick-private gui gui-private core core-private qml qml-private + +load(qml_plugin) diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc new file mode 100644 index 000000000..a3f34189c --- /dev/null +++ b/src/pdf/quick/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/qt-project.org/qtpdf"> + <file>qml/PdfPageView.qml</file> + </qresource> +</RCC> diff --git a/src/pdfwidgets/configure.json b/src/pdfwidgets/configure.json new file mode 100644 index 000000000..b33f08555 --- /dev/null +++ b/src/pdfwidgets/configure.json @@ -0,0 +1,28 @@ +{ + "module": "pdfwidgets", + "condition": "module.pdf && features.pdf-widgets", + "depends": [ + "pdf-private" + ], + "commandline": { + "options": { + "pdf-widgets": "boolean" + } + }, + "features": { + "pdf-widgets": { + "label": "Support Qt PDF Widgets", + "purpose": "Provides Qt PDF Widgets support.", + "condition": "module.widgets", + "output": [ "privateFeature" ] + } + }, + "summary": [ + { + "section": "Qt PDF Widgets", + "entries": [ + "pdf-widgets" + ] + } + ] +} diff --git a/src/pdfwidgets/pdfwidgets.pro b/src/pdfwidgets/pdfwidgets.pro new file mode 100644 index 000000000..cf221be03 --- /dev/null +++ b/src/pdfwidgets/pdfwidgets.pro @@ -0,0 +1,12 @@ +TARGET = QtPdfWidgets +QT = core gui widgets widgets-private pdf + +SOURCES += \ + qpdfview.cpp + +HEADERS += \ + qpdfview.h \ + qpdfview_p.h \ + qtpdfwidgetsglobal.h + +load(qt_module) diff --git a/src/pdfwidgets/qpdfview.cpp b/src/pdfwidgets/qpdfview.cpp new file mode 100644 index 000000000..19e5b719a --- /dev/null +++ b/src/pdfwidgets/qpdfview.cpp @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfview.h" +#include "qpdfview_p.h" + +#include "qpdfpagerenderer.h" + +#include <QGuiApplication> +#include <QPdfDocument> +#include <QPdfPageNavigation> +#include <QScreen> +#include <QScrollBar> +#include <QScroller> + +QT_BEGIN_NAMESPACE + +QPdfViewPrivate::QPdfViewPrivate() + : QAbstractScrollAreaPrivate() + , m_document(nullptr) + , m_pageNavigation(nullptr) + , m_pageRenderer(nullptr) + , m_pageMode(QPdfView::SinglePage) + , m_zoomMode(QPdfView::CustomZoom) + , m_zoomFactor(1.0) + , m_pageSpacing(3) + , m_documentMargins(6, 6, 6, 6) + , m_blockPageScrolling(false) + , m_pageCacheLimit(20) + , m_screenResolution(QGuiApplication::primaryScreen()->logicalDotsPerInch() / 72.0) +{ +} + +void QPdfViewPrivate::init() +{ + Q_Q(QPdfView); + + m_pageNavigation = new QPdfPageNavigation(q); + m_pageRenderer = new QPdfPageRenderer(q); + m_pageRenderer->setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode); +} + +void QPdfViewPrivate::documentStatusChanged() +{ + updateDocumentLayout(); + invalidatePageCache(); +} + +void QPdfViewPrivate::currentPageChanged(int currentPage) +{ + Q_Q(QPdfView); + + if (m_blockPageScrolling) + return; + + q->verticalScrollBar()->setValue(yPositionForPage(currentPage)); + + if (m_pageMode == QPdfView::SinglePage) + invalidateDocumentLayout(); +} + +void QPdfViewPrivate::calculateViewport() +{ + Q_Q(QPdfView); + + const int x = q->horizontalScrollBar()->value(); + const int y = q->verticalScrollBar()->value(); + const int width = q->viewport()->width(); + const int height = q->viewport()->height(); + + setViewport(QRect(x, y, width, height)); +} + +void QPdfViewPrivate::setViewport(QRect viewport) +{ + if (m_viewport == viewport) + return; + + const QSize oldSize = m_viewport.size(); + + m_viewport = viewport; + + if (oldSize != m_viewport.size()) { + updateDocumentLayout(); + + if (m_zoomMode != QPdfView::CustomZoom) { + invalidatePageCache(); + } + } + + if (m_pageMode == QPdfView::MultiPage) { + // An imaginary, 2px height line at the upper half of the viewport, which is used to + // determine which page is currently located there -> we propagate that as 'current' page + // to the QPdfPageNavigation object + const QRect currentPageLine(m_viewport.x(), m_viewport.y() + m_viewport.height() * 0.4, m_viewport.width(), 2); + + int currentPage = 0; + for (auto it = m_documentLayout.pageGeometries.cbegin(); it != m_documentLayout.pageGeometries.cend(); ++it) { + const QRect pageGeometry = it.value(); + if (pageGeometry.intersects(currentPageLine)) { + currentPage = it.key(); + break; + } + } + + if (currentPage != m_pageNavigation->currentPage()) { + m_blockPageScrolling = true; + m_pageNavigation->setCurrentPage(currentPage); + m_blockPageScrolling = false; + } + } +} + +void QPdfViewPrivate::updateScrollBars() +{ + Q_Q(QPdfView); + + const QSize p = q->viewport()->size(); + const QSize v = m_documentLayout.documentSize; + + q->horizontalScrollBar()->setRange(0, v.width() - p.width()); + q->horizontalScrollBar()->setPageStep(p.width()); + q->verticalScrollBar()->setRange(0, v.height() - p.height()); + q->verticalScrollBar()->setPageStep(p.height()); +} + +void QPdfViewPrivate::pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId) +{ + Q_Q(QPdfView); + + Q_UNUSED(imageSize) + Q_UNUSED(requestId) + + if (!m_cachedPagesLRU.contains(pageNumber)) { + if (m_cachedPagesLRU.length() > m_pageCacheLimit) + m_pageCache.remove(m_cachedPagesLRU.takeFirst()); + + m_cachedPagesLRU.append(pageNumber); + } + + m_pageCache.insert(pageNumber, image); + + q->viewport()->update(); +} + +void QPdfViewPrivate::invalidateDocumentLayout() +{ + updateDocumentLayout(); + invalidatePageCache(); +} + +void QPdfViewPrivate::invalidatePageCache() +{ + Q_Q(QPdfView); + + m_pageCache.clear(); + q->viewport()->update(); +} + +QPdfViewPrivate::DocumentLayout QPdfViewPrivate::calculateDocumentLayout() const +{ + // The DocumentLayout describes a virtual layout where all pages are positioned inside + // - For SinglePage mode, this is just an area as large as the current page surrounded + // by the m_documentMargins. + // - For MultiPage mode, this is the area that is covered by all pages which are placed + // below each other, with m_pageSpacing inbetween and surrounded by m_documentMargins + + DocumentLayout documentLayout; + + if (!m_document || m_document->status() != QPdfDocument::Ready) + return documentLayout; + + QHash<int, QRect> pageGeometries; + + const int pageCount = m_document->pageCount(); + + int totalWidth = 0; + + const int startPage = (m_pageMode == QPdfView::SinglePage ? m_pageNavigation->currentPage() : 0); + const int endPage = (m_pageMode == QPdfView::SinglePage ? m_pageNavigation->currentPage() + 1 : pageCount); + + // calculate page sizes + for (int page = startPage; page < endPage; ++page) { + QSize pageSize; + if (m_zoomMode == QPdfView::CustomZoom) { + pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution * m_zoomFactor).toSize(); + } else if (m_zoomMode == QPdfView::FitToWidth) { + pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution).toSize(); + const qreal factor = (qreal(m_viewport.width() - m_documentMargins.left() - m_documentMargins.right()) / qreal(pageSize.width())); + pageSize *= factor; + } else if (m_zoomMode == QPdfView::FitInView) { + const QSize viewportSize(m_viewport.size() + QSize(-m_documentMargins.left() - m_documentMargins.right(), -m_pageSpacing)); + + pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution).toSize(); + pageSize = pageSize.scaled(viewportSize, Qt::KeepAspectRatio); + } + + totalWidth = qMax(totalWidth, pageSize.width()); + + pageGeometries[page] = QRect(QPoint(0, 0), pageSize); + } + + totalWidth += m_documentMargins.left() + m_documentMargins.right(); + + int pageY = m_documentMargins.top(); + + // calculate page positions + for (int page = startPage; page < endPage; ++page) { + const QSize pageSize = pageGeometries[page].size(); + + // center horizontal inside the viewport + const int pageX = (qMax(totalWidth, m_viewport.width()) - pageSize.width()) / 2; + + pageGeometries[page].moveTopLeft(QPoint(pageX, pageY)); + + pageY += pageSize.height() + m_pageSpacing; + } + + pageY += m_documentMargins.bottom(); + + documentLayout.pageGeometries = pageGeometries; + + // calculate overall document size + documentLayout.documentSize = QSize(totalWidth, pageY); + + return documentLayout; +} + +qreal QPdfViewPrivate::yPositionForPage(int pageNumber) const +{ + const auto it = m_documentLayout.pageGeometries.constFind(pageNumber); + if (it == m_documentLayout.pageGeometries.cend()) + return 0.0; + + return (*it).y(); +} + +void QPdfViewPrivate::updateDocumentLayout() +{ + m_documentLayout = calculateDocumentLayout(); + + updateScrollBars(); +} + + +QPdfView::QPdfView(QWidget *parent) + : QAbstractScrollArea(*new QPdfViewPrivate(), parent) +{ + Q_D(QPdfView); + + d->init(); + + connect(d->m_pageNavigation, &QPdfPageNavigation::currentPageChanged, this, [d](int page){ d->currentPageChanged(page); }); + + connect(d->m_pageRenderer, &QPdfPageRenderer::pageRendered, + this, [d](int pageNumber, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions, quint64 requestId){ d->pageRendered(pageNumber, imageSize, image, requestId); }); + + verticalScrollBar()->setSingleStep(20); + horizontalScrollBar()->setSingleStep(20); + + QScroller::grabGesture(this); + + d->calculateViewport(); +} + +/*! + \internal +*/ +QPdfView::QPdfView(QPdfViewPrivate &dd, QWidget *parent) + : QAbstractScrollArea(dd, parent) +{ +} + +QPdfView::~QPdfView() +{ +} + +void QPdfView::setDocument(QPdfDocument *document) +{ + Q_D(QPdfView); + + if (d->m_document == document) + return; + + if (d->m_document) + disconnect(d->m_documentStatusChangedConnection); + + d->m_document = document; + emit documentChanged(d->m_document); + + if (d->m_document) + d->m_documentStatusChangedConnection = connect(d->m_document.data(), &QPdfDocument::statusChanged, this, [d](){ d->documentStatusChanged(); }); + + d->m_pageNavigation->setDocument(d->m_document); + d->m_pageRenderer->setDocument(d->m_document); + + d->documentStatusChanged(); +} + +QPdfDocument *QPdfView::document() const +{ + Q_D(const QPdfView); + + return d->m_document; +} + +QPdfPageNavigation *QPdfView::pageNavigation() const +{ + Q_D(const QPdfView); + + return d->m_pageNavigation; +} + +QPdfView::PageMode QPdfView::pageMode() const +{ + Q_D(const QPdfView); + + return d->m_pageMode; +} + +void QPdfView::setPageMode(PageMode mode) +{ + Q_D(QPdfView); + + if (d->m_pageMode == mode) + return; + + d->m_pageMode = mode; + d->invalidateDocumentLayout(); + + emit pageModeChanged(d->m_pageMode); +} + +QPdfView::ZoomMode QPdfView::zoomMode() const +{ + Q_D(const QPdfView); + + return d->m_zoomMode; +} + +void QPdfView::setZoomMode(ZoomMode mode) +{ + Q_D(QPdfView); + + if (d->m_zoomMode == mode) + return; + + d->m_zoomMode = mode; + d->invalidateDocumentLayout(); + + emit zoomModeChanged(d->m_zoomMode); +} + +qreal QPdfView::zoomFactor() const +{ + Q_D(const QPdfView); + + return d->m_zoomFactor; +} + +void QPdfView::setZoomFactor(qreal factor) +{ + Q_D(QPdfView); + + if (d->m_zoomFactor == factor) + return; + + d->m_zoomFactor = factor; + d->invalidateDocumentLayout(); + + emit zoomFactorChanged(d->m_zoomFactor); +} + +int QPdfView::pageSpacing() const +{ + Q_D(const QPdfView); + + return d->m_pageSpacing; +} + +void QPdfView::setPageSpacing(int spacing) +{ + Q_D(QPdfView); + + if (d->m_pageSpacing == spacing) + return; + + d->m_pageSpacing = spacing; + d->invalidateDocumentLayout(); + + emit pageSpacingChanged(d->m_pageSpacing); +} + +QMargins QPdfView::documentMargins() const +{ + Q_D(const QPdfView); + + return d->m_documentMargins; +} + +void QPdfView::setDocumentMargins(QMargins margins) +{ + Q_D(QPdfView); + + if (d->m_documentMargins == margins) + return; + + d->m_documentMargins = margins; + d->invalidateDocumentLayout(); + + emit documentMarginsChanged(d->m_documentMargins); +} + +void QPdfView::paintEvent(QPaintEvent *event) +{ + Q_D(QPdfView); + + QPainter painter(viewport()); + painter.fillRect(event->rect(), palette().brush(QPalette::Dark)); + painter.translate(-d->m_viewport.x(), -d->m_viewport.y()); + + for (auto it = d->m_documentLayout.pageGeometries.cbegin(); it != d->m_documentLayout.pageGeometries.cend(); ++it) { + const QRect pageGeometry = it.value(); + if (pageGeometry.intersects(d->m_viewport)) { // page needs to be painted + painter.fillRect(pageGeometry, Qt::white); + + const int page = it.key(); + const auto pageIt = d->m_pageCache.constFind(page); + if (pageIt != d->m_pageCache.cend()) { + const QImage &img = pageIt.value(); + painter.drawImage(pageGeometry.topLeft(), img); + } else { + d->m_pageRenderer->requestPage(page, pageGeometry.size()); + } + } + } +} + +void QPdfView::resizeEvent(QResizeEvent *event) +{ + Q_D(QPdfView); + + QAbstractScrollArea::resizeEvent(event); + + d->updateScrollBars(); + d->calculateViewport(); +} + +void QPdfView::scrollContentsBy(int dx, int dy) +{ + Q_D(QPdfView); + + QAbstractScrollArea::scrollContentsBy(dx, dy); + + d->calculateViewport(); +} + +QT_END_NAMESPACE + +#include "moc_qpdfview.cpp" diff --git a/src/pdfwidgets/qpdfview.h b/src/pdfwidgets/qpdfview.h new file mode 100644 index 000000000..cee1cb64c --- /dev/null +++ b/src/pdfwidgets/qpdfview.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFVIEW_H +#define QPDFVIEW_H + +#include <QtPdfWidgets/qtpdfwidgetsglobal.h> +#include <QtWidgets/qabstractscrollarea.h> + +QT_BEGIN_NAMESPACE + +class QPdfDocument; +class QPdfPageNavigation; +class QPdfViewPrivate; + +class Q_PDF_WIDGETS_EXPORT QPdfView : public QAbstractScrollArea +{ + Q_OBJECT + + Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) + + Q_PROPERTY(PageMode pageMode READ pageMode WRITE setPageMode NOTIFY pageModeChanged) + Q_PROPERTY(ZoomMode zoomMode READ zoomMode WRITE setZoomMode NOTIFY zoomModeChanged) + Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged) + + Q_PROPERTY(int pageSpacing READ pageSpacing WRITE setPageSpacing NOTIFY pageSpacingChanged) + Q_PROPERTY(QMargins documentMargins READ documentMargins WRITE setDocumentMargins NOTIFY documentMarginsChanged) + +public: + enum PageMode + { + SinglePage, + MultiPage + }; + Q_ENUM(PageMode) + + enum ZoomMode + { + CustomZoom, + FitToWidth, + FitInView + }; + Q_ENUM(ZoomMode) + + explicit QPdfView(QWidget *parent = nullptr); + ~QPdfView(); + + void setDocument(QPdfDocument *document); + QPdfDocument *document() const; + + QPdfPageNavigation *pageNavigation() const; + + PageMode pageMode() const; + ZoomMode zoomMode() const; + qreal zoomFactor() const; + + int pageSpacing() const; + void setPageSpacing(int spacing); + + QMargins documentMargins() const; + void setDocumentMargins(QMargins margins); + +public Q_SLOTS: + void setPageMode(PageMode mode); + void setZoomMode(ZoomMode mode); + void setZoomFactor(qreal factor); + +Q_SIGNALS: + void documentChanged(QPdfDocument *document); + void pageModeChanged(PageMode pageMode); + void zoomModeChanged(ZoomMode zoomMode); + void zoomFactorChanged(qreal zoomFactor); + void pageSpacingChanged(int pageSpacing); + void documentMarginsChanged(QMargins documentMargins); + +protected: + explicit QPdfView(QPdfViewPrivate &, QWidget *); + + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void scrollContentsBy(int dx, int dy) override; + +private: + Q_DECLARE_PRIVATE(QPdfView) +}; + +QT_END_NAMESPACE + +#endif // QPDFVIEW_H diff --git a/src/pdfwidgets/qpdfview_p.h b/src/pdfwidgets/qpdfview_p.h new file mode 100644 index 000000000..9fd54b4b6 --- /dev/null +++ b/src/pdfwidgets/qpdfview_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFVIEW_P_H +#define QPDFVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpdfview.h" + +#include <QPointer> +#include <QtWidgets/private/qabstractscrollarea_p.h> + +QT_BEGIN_NAMESPACE + +class QPdfPageRenderer; + +class QPdfViewPrivate : public QAbstractScrollAreaPrivate +{ + Q_DECLARE_PUBLIC(QPdfView) + +public: + QPdfViewPrivate(); + void init(); + + void documentStatusChanged(); + void currentPageChanged(int currentPage); + void calculateViewport(); + void setViewport(QRect viewport); + void updateScrollBars(); + + void pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId); + void invalidateDocumentLayout(); + void invalidatePageCache(); + + qreal yPositionForPage(int page) const; + + struct DocumentLayout + { + QSize documentSize; + QHash<int, QRect> pageGeometries; + }; + + DocumentLayout calculateDocumentLayout() const; + void updateDocumentLayout(); + + QPointer<QPdfDocument> m_document; + QPdfPageNavigation* m_pageNavigation; + QPdfPageRenderer *m_pageRenderer; + + QPdfView::PageMode m_pageMode; + QPdfView::ZoomMode m_zoomMode; + qreal m_zoomFactor; + + int m_pageSpacing; + QMargins m_documentMargins; + + bool m_blockPageScrolling; + + QMetaObject::Connection m_documentStatusChangedConnection; + + QRect m_viewport; + + QHash<int, QImage> m_pageCache; + QVector<int> m_cachedPagesLRU; + int m_pageCacheLimit; + + DocumentLayout m_documentLayout; + + qreal m_screenResolution; // pixels per point +}; + +Q_DECLARE_TYPEINFO(QPdfViewPrivate::DocumentLayout, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QPDFVIEW_P_H diff --git a/src/pdfwidgets/qtpdfwidgetsglobal.h b/src/pdfwidgets/qtpdfwidgetsglobal.h new file mode 100644 index 000000000..6c73a34f6 --- /dev/null +++ b/src/pdfwidgets/qtpdfwidgetsglobal.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTPDFWIDGETSGLOBAL_H +#define QTPDFWIDGETSGLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef Q_PDF_WIDGETS_EXPORT +# ifndef QT_STATIC +# if defined(QT_BUILD_PDFWIDGETS_LIB) +# define Q_PDF_WIDGETS_EXPORT Q_DECL_EXPORT +# else +# define Q_PDF_WIDGETS_EXPORT Q_DECL_IMPORT +# endif +# else +# define Q_PDF_WIDGETS_EXPORT +# endif +#endif + +QT_END_NAMESPACE + +#endif // QTPDFWIDGETSGLOBAL_H + diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro new file mode 100644 index 000000000..c3b9cb3a4 --- /dev/null +++ b/src/plugins/imageformats/imageformats.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += pdf diff --git a/src/plugins/imageformats/pdf/main.cpp b/src/plugins/imageformats/pdf/main.cpp new file mode 100644 index 000000000..b4d59353c --- /dev/null +++ b/src/plugins/imageformats/pdf/main.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfiohandler_p.h" + +QT_BEGIN_NAMESPACE + +class QPdfPlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QImageIOHandlerFactoryInterface_iid FILE "pdf.json") + +public: + Capabilities capabilities(QIODevice *device, const QByteArray &format) const override; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override; +}; + +QImageIOPlugin::Capabilities QPdfPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "pdf") + return Capabilities(CanRead); + if (!format.isEmpty()) + return {}; + + Capabilities cap; + if (device->isReadable() && QPdfIOHandler::canRead(device)) + cap |= CanRead; + return cap; +} + +QImageIOHandler *QPdfPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QPdfIOHandler *hand = new QPdfIOHandler(); + hand->setDevice(device); + hand->setFormat(format); + return hand; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/imageformats/pdf/pdf.json b/src/plugins/imageformats/pdf/pdf.json new file mode 100644 index 000000000..1f5268ca1 --- /dev/null +++ b/src/plugins/imageformats/pdf/pdf.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "pdf" ], + "MimeTypes": [ "application/pdf" ] +} diff --git a/src/plugins/imageformats/pdf/pdf.pro b/src/plugins/imageformats/pdf/pdf.pro new file mode 100644 index 000000000..b820ae7d5 --- /dev/null +++ b/src/plugins/imageformats/pdf/pdf.pro @@ -0,0 +1,11 @@ +TARGET = qpdf + +PLUGIN_TYPE = imageformats +PLUGIN_EXTENDS = pdf +PLUGIN_CLASS_NAME = QPdfPlugin +load(qt_plugin) + +HEADERS += qpdfiohandler_p.h +SOURCES += main.cpp \ + qpdfiohandler.cpp +QT += pdf diff --git a/src/plugins/imageformats/pdf/qpdfiohandler.cpp b/src/plugins/imageformats/pdf/qpdfiohandler.cpp new file mode 100644 index 000000000..9df85cf08 --- /dev/null +++ b/src/plugins/imageformats/pdf/qpdfiohandler.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfiohandler_p.h" +#include <QLoggingCategory> +#include <QPainter> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcPdf, "qt.imageformat.pdf") + +QPdfIOHandler::QPdfIOHandler() +{ +} + +bool QPdfIOHandler::canRead() const +{ + if (!device()) + return false; + if (m_loaded) + return true; + if (QPdfIOHandler::canRead(device())) { + setFormat("pdf"); + return true; + } + return false; +} + +bool QPdfIOHandler::canRead(QIODevice *device) +{ + char buf[6]; + device->peek(buf, 6); + return (!qstrncmp(buf, "%PDF-", 5) || Q_UNLIKELY(!qstrncmp(buf, "\012%PDF-", 6))); +} + +int QPdfIOHandler::currentImageNumber() const +{ + return m_page; +} + +QRect QPdfIOHandler::currentImageRect() const +{ + return QRect(QPoint(0, 0), m_doc.pageSize(m_page).toSize()); +} + +int QPdfIOHandler::imageCount() const +{ + int ret = 0; + if (const_cast<QPdfIOHandler *>(this)->load(device())) + ret = m_doc.pageCount(); + qCDebug(qLcPdf) << "imageCount" << ret; + return ret; +} + +QByteArray QPdfIOHandler::name() const +{ + return m_doc.metaData(QPdfDocument::MetaDataField::Title).toString().toLocal8Bit(); +} + +bool QPdfIOHandler::read(QImage *image) +{ + if (load(device())) { + if (m_page >= m_doc.pageCount()) + return false; + if (m_page < 0) + m_page = 0; + const bool xform = (m_clipRect.isValid() || m_scaledSize.isValid() || m_scaledClipRect.isValid()); + QSize finalSize = m_doc.pageSize(m_page).toSize(); + QRectF bounds; + if (xform && !finalSize.isEmpty()) { + bounds = QRectF(QPointF(0,0), QSizeF(finalSize)); + QPoint tr1, tr2; + QSizeF sc(1, 1); + if (m_clipRect.isValid()) { + tr1 = -m_clipRect.topLeft(); + finalSize = m_clipRect.size(); + } + if (m_scaledSize.isValid()) { + sc = QSizeF(qreal(m_scaledSize.width()) / finalSize.width(), + qreal(m_scaledSize.height()) / finalSize.height()); + finalSize = m_scaledSize; + } + if (m_scaledClipRect.isValid()) { + tr2 = -m_scaledClipRect.topLeft(); + finalSize = m_scaledClipRect.size(); + } + QTransform t; + t.translate(tr2.x(), tr2.y()); + t.scale(sc.width(), sc.height()); + t.translate(tr1.x(), tr1.y()); + bounds = t.mapRect(bounds); + } + qCDebug(qLcPdf) << Q_FUNC_INFO << m_page << finalSize; + if (image->size() != finalSize || !image->reinterpretAsFormat(QImage::Format_ARGB32_Premultiplied)) { + *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied); + if (!finalSize.isEmpty() && image->isNull()) { + // avoid QTBUG-68229 + qWarning("QPdfIOHandler: QImage allocation failed (size %i x %i)", finalSize.width(), finalSize.height()); + return false; + } + } + if (!finalSize.isEmpty()) { + image->fill(m_backColor.rgba()); + QPainter p(image); + QImage pageImage = m_doc.render(m_page, finalSize); + p.drawImage(0, 0, pageImage); + p.end(); + } + return true; + } + + return false; +} + +QVariant QPdfIOHandler::option(ImageOption option) const +{ + switch (option) { + case ImageFormat: + return QImage::Format_ARGB32_Premultiplied; + case Size: + const_cast<QPdfIOHandler *>(this)->load(device()); + return m_doc.pageSize(qMax(0, m_page)); + case ClipRect: + return m_clipRect; + case ScaledSize: + return m_scaledSize; + case ScaledClipRect: + return m_scaledClipRect; + case BackgroundColor: + return m_backColor; + case Name: + return m_doc.metaData(QPdfDocument::Title); + default: + break; + } + return QVariant(); +} + +void QPdfIOHandler::setOption(ImageOption option, const QVariant & value) +{ + switch (option) { + case ClipRect: + m_clipRect = value.toRect(); + break; + case ScaledSize: + m_scaledSize = value.toSize(); + break; + case ScaledClipRect: + m_scaledClipRect = value.toRect(); + break; + case BackgroundColor: + m_backColor = value.value<QColor>(); + break; + default: + break; + } +} + +bool QPdfIOHandler::supportsOption(ImageOption option) const +{ + switch (option) + { + case ImageFormat: + case Size: + case ClipRect: + case ScaledSize: + case ScaledClipRect: + case BackgroundColor: + case Name: + return true; + default: + break; + } + return false; +} + +bool QPdfIOHandler::jumpToImage(int frame) +{ + qCDebug(qLcPdf) << Q_FUNC_INFO << frame; + if (frame < 0 || frame >= m_doc.pageCount()) + return false; + m_page = frame; + return true; +} + +bool QPdfIOHandler::jumpToNextImage() +{ + return jumpToImage(m_page + 1); +} + +bool QPdfIOHandler::load(QIODevice *device) +{ + if (m_loaded) + return true; + if (format().isEmpty()) + if (!canRead()) + return false; + + m_doc.load(device); + m_loaded = (m_doc.error() == QPdfDocument::DocumentError::NoError); + + return m_loaded; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/pdf/qpdfiohandler_p.h b/src/plugins/imageformats/pdf/qpdfiohandler_p.h new file mode 100644 index 000000000..ca0a27581 --- /dev/null +++ b/src/plugins/imageformats/pdf/qpdfiohandler_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPDFIOHANDLER_H +#define QPDFIOHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qimageiohandler.h> +#include <QtPdf/QPdfDocument> + +QT_BEGIN_NAMESPACE + +class QPdfIOHandler : public QImageIOHandler +{ +public: + QPdfIOHandler(); + bool canRead() const override; + static bool canRead(QIODevice *device); + int currentImageNumber() const override; + QRect currentImageRect() const override; + int imageCount() const override; + QByteArray name() const override; + bool read(QImage *image) override; + QVariant option(ImageOption option) const override; + void setOption(ImageOption option, const QVariant & value) override; + bool supportsOption(ImageOption option) const override; + bool jumpToImage(int frame) override; + bool jumpToNextImage() override; + +private: + bool load(QIODevice *device); + +private: + QPdfDocument m_doc; + int m_page = -1; + + QRect m_clipRect; + QSize m_scaledSize; + QRect m_scaledClipRect; + QColor m_backColor = Qt::transparent; + bool m_loaded = false; +}; + +QT_END_NAMESPACE + +#endif // QPDFIOHANDLER_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 6698a9736..71c24e6c2 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs -qtHaveModule(designer): SUBDIRS += qwebengineview +qtHaveModule(webengine-widgets): qtHaveModule(designer): SUBDIRS += qwebengineview +qtHaveModule(pdf): qtConfig(imageformatplugin): SUBDIRS += imageformats diff --git a/src/src.pro b/src/src.pro index de88878a6..a9c58390c 100644 --- a/src/src.pro +++ b/src/src.pro @@ -4,9 +4,11 @@ include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) include($$QTWEBENGINE_OUT_ROOT/src/webenginewidgets/qtwebenginewidgets-config.pri) +include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) +include($$QTWEBENGINE_OUT_ROOT/src/pdfwidgets/qtpdfwidgets-config.pri) QT_FOR_CONFIG += buildtools-private webenginecore webenginecore-private webengine-private \ - webenginewidgets-private + webenginewidgets-private pdf-private pdfwidgets-private TEMPLATE = subdirs @@ -38,7 +40,16 @@ qtConfig(build-qtwebengine-core):qtConfig(webengine-core-support) { } } -!qtConfig(webengine-core-support): qtConfig(build-qtwebengine-core) { +qtConfig(build-qtpdf):qtConfig(webengine-qtpdf-support) { + pdf.depends = buildtools + SUBDIRS += buildtools pdf plugins + qtConfig(pdf-widgets) { + pdfwidgets.depends = pdf + SUBDIRS += pdfwidgets + } +} + +!qtConfig(webengine-core-support):if(qtConfig(build-qtwebengine-core)|qtConfig(build-qtpdf)) { !qtwebengine_makeCheckError():!isEmpty(skipBuildReason):!build_pass { errorbuild.commands = @echo Modules will not be built. $${skipBuildReason} errorbuild.CONFIG = phony diff --git a/sync.profile b/sync.profile index 4cc549106..a1e3c8ded 100644 --- a/sync.profile +++ b/sync.profile @@ -2,11 +2,14 @@ "QtWebEngine" => "$basedir/src/webengine", "QtWebEngineWidgets" => "$basedir/src/webenginewidgets", "QtWebEngineCore" => "$basedir/src/core", + "QtPdf" => "$basedir/src/pdf", + "QtPdfWidgets" => "$basedir/src/pdfwidgets", ); %moduleheaders = ( # restrict the module headers to those found in relative path "QtWebEngine" => "api", "QtWebEngineWidgets" => "api", "QtWebEngineCore" => "api", + "QtPdf" => "api" ); %classnames = ( ); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 59bcd5aef..9b71e1183 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -7,3 +7,8 @@ qtHaveModule(webengine) { qtHaveModule(webenginewidgets) { SUBDIRS += core widgets } + +qtHaveModule(pdf) { + SUBDIRS += pdf +} + diff --git a/tests/auto/pdf/pdf.pro b/tests/auto/pdf/pdf.pro new file mode 100644 index 000000000..a2b3fcff2 --- /dev/null +++ b/tests/auto/pdf/pdf.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + qpdfbookmarkmodel \ + qpdfpagenavigation \ + qpdfpagerenderer + +qtHaveModule(printsupport): SUBDIRS += qpdfdocument diff --git a/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf Binary files differnew file mode 100644 index 000000000..bd27c18b6 --- /dev/null +++ b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf diff --git a/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf Binary files differnew file mode 100644 index 000000000..c4e1aa36e --- /dev/null +++ b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf diff --git a/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro b/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro new file mode 100644 index 000000000..11a010637 --- /dev/null +++ b/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qpdfbookmarkmodel +QT += pdf testlib network +macos:CONFIG -= app_bundle +SOURCES += tst_qpdfbookmarkmodel.cpp diff --git a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp new file mode 100644 index 000000000..fddc98011 --- /dev/null +++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QPdfDocument> +#include <QPdfBookmarkModel> + +class tst_QPdfBookmarkModel: public QObject +{ + Q_OBJECT + +public: + tst_QPdfBookmarkModel() + { + qRegisterMetaType<QPdfDocument::Status>(); + } + +private slots: + void emptyModel(); + void setEmptyDocument(); + void setEmptyDocumentAndLoad(); + void setLoadedDocument(); + void unloadDocument(); + void testTreeStructure(); + void testListStructure(); + void testPageNumberRole(); +}; + +void tst_QPdfBookmarkModel::emptyModel() +{ + QPdfBookmarkModel model; + + QVERIFY(!model.document()); + QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode); + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.index(0, 0).isValid(), false); +} + +void tst_QPdfBookmarkModel::setEmptyDocument() +{ + QPdfDocument document; + QPdfBookmarkModel model; + + model.setDocument(&document); + + QCOMPARE(model.document(), &document); + QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode); + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.index(0, 0).isValid(), false); +} + +void tst_QPdfBookmarkModel::setEmptyDocumentAndLoad() +{ + QPdfDocument document; + QPdfBookmarkModel model; + + model.setDocument(&document); + + QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + + QCOMPARE(model.rowCount(), 3); +} + +void tst_QPdfBookmarkModel::setLoadedDocument() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + + QPdfBookmarkModel model; + + QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); + + model.setDocument(&document); + + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + + QCOMPARE(model.rowCount(), 3); +} + +void tst_QPdfBookmarkModel::unloadDocument() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + + QPdfBookmarkModel model; + model.setDocument(&document); + + QCOMPARE(model.rowCount(), 3); + + QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); + + document.close(); + + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + + QCOMPARE(model.rowCount(), 0); +} + +void tst_QPdfBookmarkModel::testTreeStructure() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + + QPdfBookmarkModel model; + model.setDocument(&document); + + QCOMPARE(model.rowCount(), 3); + + const QModelIndex index1 = model.index(0, 0); + QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1")); + QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index1), 2); + + const QModelIndex index1_1 = model.index(0, 0, index1); + QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1")); + QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index1_1), 0); + + const QModelIndex index1_2 = model.index(1, 0, index1); + QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2")); + QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index1_2), 0); + + const QModelIndex index2 = model.index(1, 0); + QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2")); + QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index2), 2); + + const QModelIndex index2_1 = model.index(0, 0, index2); + QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1")); + QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index2_1), 1); + + const QModelIndex index2_1_1 = model.index(0, 0, index2_1); + QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1")); + QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2); + QCOMPARE(model.rowCount(index2_1_1), 0); + + const QModelIndex index2_2 = model.index(1, 0, index2); + QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2")); + QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index2_2), 0); + + const QModelIndex index3 = model.index(2, 0); + QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3")); + QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index3), 0); + + const QModelIndex index4 = model.index(3, 0); + QCOMPARE(index4, QModelIndex()); +} + +void tst_QPdfBookmarkModel::testListStructure() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + + QPdfBookmarkModel model; + model.setDocument(&document); + + QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); + + model.setStructureMode(QPdfBookmarkModel::ListMode); + + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + + QCOMPARE(model.rowCount(), 8); + + const QModelIndex index1 = model.index(0, 0); + QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1")); + QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index1), 0); + + const QModelIndex index1_1 = model.index(1, 0); + QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1")); + QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index1_1), 0); + + const QModelIndex index1_2 = model.index(2, 0); + QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2")); + QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index1_2), 0); + + const QModelIndex index2 = model.index(3, 0); + QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2")); + QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index2), 0); + + const QModelIndex index2_1 = model.index(4, 0); + QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1")); + QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index2_1), 0); + + const QModelIndex index2_1_1 = model.index(5, 0); + QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1")); + QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2); + QCOMPARE(model.rowCount(index2_1_1), 0); + + const QModelIndex index2_2 = model.index(6, 0); + QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2")); + QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(model.rowCount(index2_2), 0); + + const QModelIndex index3 = model.index(7, 0); + QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3")); + QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(model.rowCount(index3), 0); + + const QModelIndex index4 = model.index(8, 0); + QCOMPARE(index4, QModelIndex()); +} + +void tst_QPdfBookmarkModel::testPageNumberRole() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::NoError); + + QPdfBookmarkModel model; + model.setDocument(&document); + + QCOMPARE(model.rowCount(), 3); + + const QModelIndex index1 = model.index(0, 0); + QCOMPARE(index1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 0); + + const QModelIndex index2 = model.index(1, 0); + QCOMPARE(index2.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1); + + const QModelIndex index2_1 = model.index(0, 0, index2); + QCOMPARE(index2_1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1); + + const QModelIndex index3 = model.index(2, 0); + QCOMPARE(index3.data(QPdfBookmarkModel::PageNumberRole).toInt(), 2); +} + +QTEST_MAIN(tst_QPdfBookmarkModel) + +#include "tst_qpdfbookmarkmodel.moc" diff --git a/tests/auto/pdf/qpdfdocument/BLACKLIST b/tests/auto/pdf/qpdfdocument/BLACKLIST new file mode 100644 index 000000000..b8db556d6 --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/BLACKLIST @@ -0,0 +1,6 @@ +[password] +* + +[passwordClearedOnClose] +* + diff --git a/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf b/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf Binary files differnew file mode 100644 index 000000000..c3350ba5f --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf diff --git a/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf b/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf Binary files differnew file mode 100644 index 000000000..d76fdd1a6 --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf diff --git a/tests/auto/pdf/qpdfdocument/qpdfdocument.pro b/tests/auto/pdf/qpdfdocument/qpdfdocument.pro new file mode 100644 index 000000000..8382a25e3 --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/qpdfdocument.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +TARGET = tst_qpdfdocument +QT += pdf printsupport testlib network +macx:CONFIG -= app_bundle +SOURCES += tst_qpdfdocument.cpp + diff --git a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp new file mode 100644 index 000000000..29b85fc89 --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp @@ -0,0 +1,386 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QPainter> +#include <QPdfDocument> +#include <QPrinter> +#include <QTemporaryFile> +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QNetworkReply> + +class tst_QPdfDocument: public QObject +{ + Q_OBJECT + +public: + tst_QPdfDocument() + { + qRegisterMetaType<QPdfDocument::Status>(); + } + +private slots: + void pageCount(); + void loadFromIODevice(); + void loadAsync(); + void password(); + void close(); + void loadAfterClose(); + void closeOnDestroy(); + void status(); + void passwordClearedOnClose(); + void metaData(); +}; + +struct TemporaryPdf: public QTemporaryFile +{ + TemporaryPdf(); + QPageLayout pageLayout; +}; + + +TemporaryPdf::TemporaryPdf() +{ + open(); + pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()); + + { + QPrinter printer; + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setOutputFileName(fileName()); + printer.setPageLayout(pageLayout); + + { + QPainter painter(&printer); + painter.drawText(100, 100, QStringLiteral("Hello Page 1")); + printer.newPage(); + painter.drawText(100, 100, QStringLiteral("Hello Page 2")); + } + } + + seek(0); +} + +void tst_QPdfDocument::pageCount() +{ + TemporaryPdf tempPdf; + + QPdfDocument doc; + QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); + + QCOMPARE(doc.pageCount(), 0); + QCOMPARE(doc.load(tempPdf.fileName()), QPdfDocument::NoError); + QCOMPARE(doc.pageCount(), 2); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); + + QCOMPARE(doc.pageSize(0).toSize(), tempPdf.pageLayout.fullRectPoints().size()); +} + +void tst_QPdfDocument::loadFromIODevice() +{ + TemporaryPdf tempPdf; + QPdfDocument doc; + QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); + doc.load(&tempPdf); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + QCOMPARE(doc.error(), QPdfDocument::NoError); + QCOMPARE(doc.pageCount(), 2); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); +} + +void tst_QPdfDocument::loadAsync() +{ + TemporaryPdf tempPdf; + + QNetworkAccessManager nam; + + QUrl url = QUrl::fromLocalFile(tempPdf.fileName()); + QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(url))); + + QPdfDocument doc; + QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); + + doc.load(reply.data()); + + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + QCOMPARE(doc.pageCount(), 2); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); +} + +void tst_QPdfDocument::password() +{ + QPdfDocument doc; + QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged())); + + QCOMPARE(doc.pageCount(), 0); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError); + QCOMPARE(passwordChangedSpy.count(), 0); + doc.setPassword(QStringLiteral("WrongPassword")); + QCOMPARE(passwordChangedSpy.count(), 1); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError); + QCOMPARE(doc.status(), QPdfDocument::Error); + doc.setPassword(QStringLiteral("Qt")); + QCOMPARE(passwordChangedSpy.count(), 2); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError); + QCOMPARE(doc.pageCount(), 1); +} + +void tst_QPdfDocument::close() +{ + TemporaryPdf tempPdf; + QPdfDocument doc; + + QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); + + doc.load(&tempPdf); + + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); + + statusChangedSpy.clear(); + pageCountChangedSpy.clear(); + + doc.close(); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + QCOMPARE(doc.pageCount(), 0); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); +} + +void tst_QPdfDocument::loadAfterClose() +{ + TemporaryPdf tempPdf; + QPdfDocument doc; + + QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); + + doc.load(&tempPdf); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); + statusChangedSpy.clear(); + pageCountChangedSpy.clear(); + + doc.close(); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); + statusChangedSpy.clear(); + pageCountChangedSpy.clear(); + + doc.load(&tempPdf); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + QCOMPARE(doc.error(), QPdfDocument::NoError); + QCOMPARE(doc.pageCount(), 2); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); +} + +void tst_QPdfDocument::closeOnDestroy() +{ + TemporaryPdf tempPdf; + + // deleting an open document should automatically close it + { + QPdfDocument *doc = new QPdfDocument; + + doc->load(&tempPdf); + + QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int))); + + delete doc; + + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0); + } + + // deleting a closed document should not emit any signal + { + QPdfDocument *doc = new QPdfDocument; + doc->load(&tempPdf); + doc->close(); + + QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status))); + QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int))); + + delete doc; + + QCOMPARE(statusChangedSpy.count(), 0); + QCOMPARE(pageCountChangedSpy.count(), 0); + } +} + +void tst_QPdfDocument::status() +{ + TemporaryPdf tempPdf; + + QPdfDocument doc; + QCOMPARE(doc.status(), QPdfDocument::Null); + + QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); + + // open existing document + doc.load(&tempPdf); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + statusChangedSpy.clear(); + + QCOMPARE(doc.status(), QPdfDocument::Ready); + + // close document + doc.close(); + + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + statusChangedSpy.clear(); + + QCOMPARE(doc.status(), QPdfDocument::Null); + + // try to open non-existing document + doc.load(QFINDTESTDATA("does-not-exist.pdf")); + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error); + QCOMPARE(doc.status(), QPdfDocument::Error); + statusChangedSpy.clear(); + + // try to open non-existing document asynchronously + QNetworkAccessManager accessManager; + + const QUrl url("http://doesnotexist.qt.io"); + QScopedPointer<QNetworkReply> reply(accessManager.get(QNetworkRequest(url))); + + doc.load(reply.data()); + + QElapsedTimer stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (statusChangedSpy.count() == 2) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); + QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error); + statusChangedSpy.clear(); +} + +void tst_QPdfDocument::passwordClearedOnClose() +{ + TemporaryPdf tempPdf; + QPdfDocument doc; + + QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged())); + + doc.setPassword(QStringLiteral("Qt")); + QCOMPARE(passwordChangedSpy.count(), 1); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError); + passwordChangedSpy.clear(); + + doc.close(); // password is cleared on close + QCOMPARE(passwordChangedSpy.count(), 1); + passwordChangedSpy.clear(); + + doc.load(&tempPdf); + doc.close(); // signal is not emitted if password didn't change + QCOMPARE(passwordChangedSpy.count(), 0); +} + +void tst_QPdfDocument::metaData() +{ + QPdfDocument doc; + + // a closed document does not return any meta data + QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime()); + QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime()); + + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.metadata.pdf")), QPdfDocument::NoError); + + // check for proper meta data from sample document + QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString::fromLatin1("Qt PDF Unit Test Document")); + QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString::fromLatin1("A test for meta data access")); + QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString::fromLatin1("John Doe")); + QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString::fromLatin1("meta data keywords")); + QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString::fromLatin1("LibreOffice 5.1")); + QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString::fromLatin1("Writer")); + QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime(QDate(2016, 8, 7), QTime(7, 3, 6), Qt::UTC)); + QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), Qt::UTC)); +} + +QTEST_MAIN(tst_QPdfDocument) + +#include "tst_qpdfdocument.moc" + diff --git a/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf b/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf Binary files differnew file mode 100644 index 000000000..c4e1aa36e --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf diff --git a/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro b/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro new file mode 100644 index 000000000..8de99543f --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qpdfpagenavigation +QT += pdf testlib network +macos:CONFIG -= app_bundle +SOURCES += tst_qpdfpagenavigation.cpp diff --git a/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp b/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp new file mode 100644 index 000000000..ff6a02750 --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QPdfDocument> +#include <QPdfPageNavigation> + +class tst_QPdfPageNavigation: public QObject +{ + Q_OBJECT + +private slots: + void defaultValues(); + void setEmptyDocument(); + void setEmptyDocumentAndLoad(); + void setLoadedDocument(); + void unloadDocument(); + void navigate(); +}; + +void tst_QPdfPageNavigation::defaultValues() +{ + QPdfPageNavigation pageNavigation; + + QCOMPARE(pageNavigation.document(), nullptr); + QCOMPARE(pageNavigation.currentPage(), 0); + QCOMPARE(pageNavigation.pageCount(), 0); + QCOMPARE(pageNavigation.canGoToPreviousPage(), false); + QCOMPARE(pageNavigation.canGoToNextPage(), false); +} + +void tst_QPdfPageNavigation::setEmptyDocument() +{ + QPdfDocument document; + QPdfPageNavigation pageNavigation; + + pageNavigation.setDocument(&document); + + QCOMPARE(pageNavigation.document(), &document); + QCOMPARE(pageNavigation.currentPage(), 0); + QCOMPARE(pageNavigation.pageCount(), 0); + QCOMPARE(pageNavigation.canGoToPreviousPage(), false); + QCOMPARE(pageNavigation.canGoToNextPage(), false); +} + +void tst_QPdfPageNavigation::setEmptyDocumentAndLoad() +{ + QPdfDocument document; + QPdfPageNavigation pageNavigation; + + pageNavigation.setDocument(&document); + + QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); + QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); + QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); + QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); + + QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available + QCOMPARE(canGoToNextPageChangedSpy.count(), 1); + QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true); +} + +void tst_QPdfPageNavigation::setLoadedDocument() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); + + QPdfPageNavigation pageNavigation; + + QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); + QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); + QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); + QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); + + pageNavigation.setDocument(&document); + + QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available + QCOMPARE(canGoToNextPageChangedSpy.count(), 1); + QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true); +} + +void tst_QPdfPageNavigation::unloadDocument() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); + + QPdfPageNavigation pageNavigation; + pageNavigation.setDocument(&document); + + QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); + QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); + QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); + QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); + + document.close(); + + QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available + QCOMPARE(canGoToNextPageChangedSpy.count(), 1); + QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), false); +} + +void tst_QPdfPageNavigation::navigate() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); + + QPdfPageNavigation pageNavigation; + pageNavigation.setDocument(&document); + + QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); + QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); + QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); + + QCOMPARE(pageNavigation.currentPage(), 0); + + // try to go to previous page while there is none + QCOMPARE(pageNavigation.canGoToPreviousPage(), false); + pageNavigation.goToPreviousPage(); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); + QCOMPARE(pageNavigation.currentPage(), 0); + QCOMPARE(pageNavigation.canGoToPreviousPage(), false); + + // try to go to next page + QCOMPARE(pageNavigation.canGoToNextPage(), true); + pageNavigation.goToNextPage(); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 1); + QCOMPARE(canGoToNextPageChangedSpy.count(), 0); + QCOMPARE(currentPageChangedSpy.count(), 1); + QCOMPARE(pageNavigation.currentPage(), 1); + QCOMPARE(pageNavigation.canGoToPreviousPage(), true); + + currentPageChangedSpy.clear(); + canGoToPreviousPageChangedSpy.clear(); + canGoToNextPageChangedSpy.clear(); + + // try to go to last page + pageNavigation.setCurrentPage(2); + QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); + QCOMPARE(canGoToNextPageChangedSpy.count(), 1); + QCOMPARE(currentPageChangedSpy.count(), 1); + QCOMPARE(pageNavigation.currentPage(), 2); + QCOMPARE(pageNavigation.canGoToNextPage(), false); + + // check that invalid requests are ignored + pageNavigation.setCurrentPage(-1); + QCOMPARE(pageNavigation.currentPage(), 2); + + pageNavigation.setCurrentPage(3); + QCOMPARE(pageNavigation.currentPage(), 2); +} + +QTEST_MAIN(tst_QPdfPageNavigation) + +#include "tst_qpdfpagenavigation.moc" diff --git a/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf b/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf Binary files differnew file mode 100644 index 000000000..c4e1aa36e --- /dev/null +++ b/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf diff --git a/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro b/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro new file mode 100644 index 000000000..9ccb4e82c --- /dev/null +++ b/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qpdfpagerenderer +QT += pdf testlib network +macos:CONFIG -= app_bundle +SOURCES += tst_qpdfpagerenderer.cpp diff --git a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp new file mode 100644 index 000000000..8eaef7c6e --- /dev/null +++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QPdfDocument> +#include <QPdfPageRenderer> + +#include <QtTest/QtTest> + +class tst_QPdfPageRenderer: public QObject +{ + Q_OBJECT + +private slots: + void defaultValues(); + void withNoDocument(); + void withEmptyDocument(); + void withLoadedDocumentSingleThreaded(); + void withLoadedDocumentMultiThreaded(); + void switchingRenderMode(); +}; + +void tst_QPdfPageRenderer::defaultValues() +{ + QPdfPageRenderer pageRenderer; + + QCOMPARE(pageRenderer.document(), nullptr); + QCOMPARE(pageRenderer.renderMode(), QPdfPageRenderer::SingleThreadedRenderMode); +} + +void tst_QPdfPageRenderer::withNoDocument() +{ + QPdfPageRenderer pageRenderer; + + const QSize imageSize(100, 100); + const quint64 requestId = pageRenderer.requestPage(0, imageSize); + + QCOMPARE(requestId, quint64(0)); +} + +void tst_QPdfPageRenderer::withEmptyDocument() +{ + QPdfDocument document; + QPdfPageRenderer pageRenderer; + + pageRenderer.setDocument(&document); + + const QSize imageSize(100, 100); + const quint64 requestId = pageRenderer.requestPage(0, imageSize); + + QCOMPARE(requestId, quint64(0)); +} + +void tst_QPdfPageRenderer::withLoadedDocumentSingleThreaded() +{ + QPdfDocument document; + QPdfPageRenderer pageRenderer; + pageRenderer.setDocument(&document); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + + QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); + + const QSize imageSize(100, 100); + const quint64 requestId = pageRenderer.requestPage(0, imageSize); + + QCOMPARE(requestId, quint64(1)); + QTRY_COMPARE(pageRenderedSpy.count(), 1); + QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); + QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); + QCOMPARE(pageRenderedSpy[0][4].toULongLong(), requestId); +} + +void tst_QPdfPageRenderer::withLoadedDocumentMultiThreaded() +{ + QPdfDocument document; + + QPdfPageRenderer pageRenderer; + pageRenderer.setDocument(&document); + pageRenderer.setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + + QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); + + const QSize imageSize(100, 100); + const quint64 requestId = pageRenderer.requestPage(0, imageSize); + + QCOMPARE(requestId, quint64(1)); + QTRY_COMPARE(pageRenderedSpy.count(), 1); + QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); + QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); + QCOMPARE(pageRenderedSpy[0][4].toULongLong(), requestId); +} + +void tst_QPdfPageRenderer::switchingRenderMode() +{ + QPdfDocument document; + QPdfPageRenderer pageRenderer; + pageRenderer.setDocument(&document); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + + QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); + + // render single threaded + const QSize imageSize(100, 100); + const quint64 firstRequestId = pageRenderer.requestPage(0, imageSize); + + QTRY_COMPARE(pageRenderedSpy.count(), 1); + QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); + QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); + QCOMPARE(pageRenderedSpy[0][4].toULongLong(), firstRequestId); + + const QImage image = pageRenderedSpy[0][2].value<QImage>(); + + pageRenderedSpy.clear(); + + // switch to multi threaded + pageRenderer.setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode); + + const quint64 secondRequestId = pageRenderer.requestPage(0, imageSize); + + QVERIFY(firstRequestId != secondRequestId); + QTRY_COMPARE(pageRenderedSpy.count(), 1); + QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); + QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); + QCOMPARE(pageRenderedSpy[0][4].toULongLong(), secondRequestId); + + pageRenderedSpy.clear(); + + // switch back to single threaded + pageRenderer.setRenderMode(QPdfPageRenderer::SingleThreadedRenderMode); + + const quint64 thirdRequestId = pageRenderer.requestPage(0, imageSize); + + QTRY_COMPARE(pageRenderedSpy.count(), 1); + QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); + QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image); + QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); + QCOMPARE(pageRenderedSpy[0][4].toULongLong(), thirdRequestId); +} + +QTEST_MAIN(tst_QPdfPageRenderer) + +#include "tst_qpdfpagerenderer.moc" diff --git a/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro b/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro new file mode 100644 index 000000000..205fef175 --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qpdfsearchmodel +QT += pdf testlib network +macos:CONFIG -= app_bundle +SOURCES += tst_qpdfsearchmodel.cpp diff --git a/tests/auto/pdf/qpdfsearchmodel/test.pdf b/tests/auto/pdf/qpdfsearchmodel/test.pdf Binary files differnew file mode 100644 index 000000000..a9dc1bc29 --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/test.pdf diff --git a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp new file mode 100644 index 000000000..e690bfc11 --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QPdfDocument> +#include <QPdfSearchModel> + +class tst_QPdfSearchModel: public QObject +{ + Q_OBJECT + +public: + tst_QPdfSearchModel() {} + +private slots: + void findText(); +}; + +void tst_QPdfSearchModel::findText() +{ + QPdfDocument document; + QCOMPARE(document.load(QFINDTESTDATA("test.pdf")), QPdfDocument::NoError); + + QPdfSearchModel model; + model.setDocument(&document); + QVector<QRectF> matches = model.matches(1, "ai"); + + qDebug() << matches; + QCOMPARE(matches.count(), 3); +} + +QTEST_MAIN(tst_QPdfSearchModel) + +#include "tst_qpdfsearchmodel.moc" diff --git a/tests/manual/quick/pdf/listview.qml b/tests/manual/quick/pdf/listview.qml new file mode 100644 index 000000000..361ae7d89 --- /dev/null +++ b/tests/manual/quick/pdf/listview.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Pdf 5.15 + +Window { + width: 600 + height: 440 + color: "lightgrey" + title: doc.source + visible: true + + PdfDocument { + id: doc + source: "test.pdf" + } + + ListView { + id: listView + anchors.fill: parent + model: doc.pageCount + spacing: 6 + delegate: Column { + Rectangle { + id: paper + width: image.width + height: image.height + Image { + id: image + objectName: "PDF page " + index + source: doc.source + currentFrame: index + asynchronous: true + } + } + Text { + text: "Page " + (image.currentFrame + 1) + } + } + } + + Text { + anchors.bottom: parent.bottom + anchors.right: parent.right + text: "page " + Math.max(1, (listView.indexAt(0, listView.contentY) + 1)) + " of " + doc.pageCount + } +} diff --git a/tests/manual/quick/pdf/pessimizedListView.qml b/tests/manual/quick/pdf/pessimizedListView.qml new file mode 100644 index 000000000..4ae0edabe --- /dev/null +++ b/tests/manual/quick/pdf/pessimizedListView.qml @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Pdf 5.15 +import Qt.labs.platform 1.1 as P + +ApplicationWindow { + width: 900 + height: 1000 + color: "lightgrey" + title: doc.source + " scale " + imageScale.toFixed(2) + visible: true + property real imageScale: 1 + + header: ToolBar { + RowLayout { + anchors.fill: parent + anchors.rightMargin: 6 + ToolButton { + action: Action { + text: "🗁" + shortcut: StandardKey.Open + onTriggered: fileDialog.open() + } + } + ToolButton { + action: Action { + text: "⊕" + shortcut: StandardKey.ZoomIn + onTriggered: imageScale *= Math.sqrt(2) + } + } + ToolButton { + action: Action { + text: "⊖" + shortcut: StandardKey.ZoomOut + onTriggered: imageScale /= Math.sqrt(2) + } + } + ToolButton { + action: Action { + text: "1x" + shortcut: "Ctrl+0" + onTriggered: imageScale = 1 + } + } + + Label { + text: "Pixels/point:" + } + SpinBox { + id: oversamplingSB + from: 1; to: 8; value: 4 + } + + Label { + text: "cacheBuffer:" + } + SpinBox { + id: cacheBufferSB + from: 0; to: 1000; stepSize: 50; value: 100 + } + + CheckBox { + id: asyncCB + text: "async" + } + + CheckBox { + id: cacheCB + text: "cache" + } + } + } + + PdfDocument { + id: doc + source: "test.pdf" + } + + P.FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: doc.source = file + } + + ListView { + id: listView + anchors.fill: parent + anchors.margins: 10 + model: doc.pageCount + spacing: 6 + cacheBuffer: cacheBufferSB.value + ScrollBar.vertical: ScrollBar { } + delegate: Rectangle { + id: paper + width: Math.max(60, image.width) * imageScale + height: 100 * imageScale + BusyIndicator { + anchors.centerIn: parent + running: image.status === Image.Loading + } + Image { + id: image + scale: imageScale + anchors.centerIn: parent + sourceSize.width: doc.pagePointSize(index).width * oversamplingSB.value + height: 100 + fillMode: Image.PreserveAspectFit + objectName: "PDF page " + index + source: doc.source + currentFrame: index + asynchronous: asyncCB.checked + cache: cacheCB.checked + } + } + } + + Text { + anchors.bottom: parent.bottom + anchors.right: parent.right + text: "page " + Math.max(1, (listView.indexAt(0, listView.contentY) + 1)) + " of " + doc.pageCount + } +} diff --git a/tests/manual/quick/pdf/simplest.qml b/tests/manual/quick/pdf/simplest.qml new file mode 100644 index 000000000..0571493af --- /dev/null +++ b/tests/manual/quick/pdf/simplest.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.14 + +Image { + id: image + source: "test.pdf" + fillMode: Image.PreserveAspectFit + Shortcut { + sequence: StandardKey.MoveToNextPage + enabled: image.currentFrame < image.frameCount - 1 + onActivated: image.currentFrame++ + } + Shortcut { + sequence: StandardKey.MoveToPreviousPage + enabled: image.currentFrame > 0 + onActivated: image.currentFrame-- + } +} diff --git a/tests/manual/quick/pdf/test.pdf b/tests/manual/quick/pdf/test.pdf Binary files differnew file mode 100644 index 000000000..a9dc1bc29 --- /dev/null +++ b/tests/manual/quick/pdf/test.pdf diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml new file mode 100644 index 000000000..0fed5b16e --- /dev/null +++ b/tests/manual/quick/pdf/withdoc.qml @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import Qt.labs.platform 1.1 as Platform +import QtQuick.Pdf 5.15 +import QtQuick.Window 2.14 + +Window { + width: 800 + height: 940 + color: "lightgrey" + title: doc.source + visible: true + + PdfDocument { + id: doc + source: "test.pdf" + } + + Platform.FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: doc.source = file + } + + Column { + id: column + anchors.fill: parent + anchors.margins: 6 + spacing: 6 + Text { text: "title: " + doc.title; visible: doc.title !== "" } + Text { text: "author: " + doc.author; visible: doc.author !== "" } + Text { text: "subject: " + doc.subject; visible: doc.subject !== "" } + Text { text: "keywords: " + doc.keywords; visible: doc.keywords !== "" } + Text { text: "producer: " + doc.producer; visible: doc.producer !== "" } + Text { text: "creator: " + doc.creator; visible: doc.creator !== "" } + Text { text: "creationDate: " + doc.creationDate; visible: doc.creationDate !== "" } + Text { text: "modificationDate: " + doc.modificationDate; visible: doc.modificationDate !== "" } + Text { text: "implicit size: " + image.implicitWidth + "x" + image.implicitHeight } + Text { text: "source size: " + image.sourceSize.width + "x" + image.sourceSize.height } + Text { text: "painted size: " + image.paintedWidth + "x" + image.paintedHeight } + + Flickable { + width: column.width + height: width + contentWidth: paper.width + contentHeight: paper.height + z: -1 + Rectangle { + id: paper + width: image.width + height: image.height + Image { + id: image + source: doc.status === PdfDocument.Ready ? doc.source : "" + + property real zoomFactor: Math.sqrt(2) + + DragHandler { + id: dragHandler + target: null + } + + Shortcut { + sequence: StandardKey.MoveToNextPage + enabled: image.currentFrame < image.frameCount - 1 + onActivated: image.currentFrame++ + } + Shortcut { + sequence: StandardKey.MoveToPreviousPage + enabled: image.currentFrame > 0 + onActivated: image.currentFrame-- + } + Shortcut { + sequence: StandardKey.ZoomIn + enabled: image.sourceSize.width < 5000 + onActivated: { + image.sourceSize.width = image.implicitWidth * image.zoomFactor + image.sourceSize.height = image.implicitHeight * image.zoomFactor + } + } + Shortcut { + sequence: StandardKey.ZoomOut + enabled: image.width > 50 + onActivated: { + image.sourceSize.width = image.implicitWidth / image.zoomFactor + image.sourceSize.height = image.implicitHeight / image.zoomFactor + } + } + Shortcut { + sequence: "Ctrl+0" + onActivated: image.sourceSize = undefined + } + Shortcut { + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + } + } + } + Text { + anchors.bottom: parent.bottom + text: "page " + (image.currentFrame + 1) + " of " + doc.pageCount + } +} diff --git a/tools/scripts/take_snapshot.py b/tools/scripts/take_snapshot.py index 741979a9e..b7c2c4bd9 100755 --- a/tools/scripts/take_snapshot.py +++ b/tools/scripts/take_snapshot.py @@ -179,7 +179,6 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('third_party/icu/android') or file_path.startswith('third_party/icu/cast') or file_path.startswith('third_party/icu/chromeos') - or file_path.startswith('third_party/icu/ios') or file_path.startswith('third_party/instrumented_libraries') or file_path.startswith('third_party/jsr-305') or file_path.startswith('third_party/junit') |