comparepdf-1.0.1/.hg_archival.txt0000644000000000000000000000022411703070016016646 0ustar rootroot00000000000000repo: 4ae9c54ffa5abad5d4bd88026559fa03f10a1da2 node: e979a4f43c4d22bb22f071fb804038140484f366 branch: default latesttag: 1.0.1 latesttagdistance: 1 comparepdf-1.0.1/.hgignore0000644000000000000000000000031411703070016015363 0ustar rootroot00000000000000syntax: glob TODO.txt comparepdf Makefile gpl-[0-9].[0-9].txt *.[568] *.a *.o *.py[co] *.so *.sw[nop] *~ .#* [#]*# *.ld *.ldx louti[0-9]* *.li *.lix *.tmp *.bak test.* moc_* *.obj qrc_*.cpp ui_*.h .*.swp comparepdf-1.0.1/.hgtags0000644000000000000000000000072611703070016015045 0ustar rootroot000000000000004f8ae7b80137406c33385390cccdcdfc12ff02fa 1.0.0 4f8ae7b80137406c33385390cccdcdfc12ff02fa 1.0.0 03476b7f8143fc3ffb46c9a889e5102123bc61e7 1.0.0 03476b7f8143fc3ffb46c9a889e5102123bc61e7 1.0.0 f53e21a5f7d0a4141193392af59cfd310e980976 1.0.0 f53e21a5f7d0a4141193392af59cfd310e980976 1.0.0 b55fa6f9d35709be46be61b501144a2317acef51 1.0.0 3057fd60c1e3a3eb22d75ee665aec698d0c1175b 1.0.1 3057fd60c1e3a3eb22d75ee665aec698d0c1175b 1.0.1 4c4505c43788709aa2480a481e99b00a8219e565 1.0.1 comparepdf-1.0.1/CHANGES0000644000000000000000000000013011703070016014547 0ustar rootroot000000000000001.0.1 Added --version option. Requires at least Poppler 0.14.0. 1.0.0 First release. comparepdf-1.0.1/README0000644000000000000000000000446211703070016014450 0ustar rootroot00000000000000comparepdf ========== comparepdf is used to compare two PDF files. The default comparison mode is text mode where the text of each corresponding pair of pages is compared. As soon as a difference is detected the program terminates with a message (unless -v0 is set) and an indicative return code. The options are -ct or --compare=text (the default) for text mode comparisons or -ca or --compare=appearance for visual comparisons (useful if diagrams or other images have changed), and -v=1 or --verbose=1 for reporting differences (and saying nothing for matching files): use -v=0 for no reporting or -v=2 for reporting both different and matching files. (For a GUI tool for showing the detailed differences between PDF files see http://www.qtrac.eu/diffpdf.html.) Home page: http://www.qtrac.eu/comparepdf.html Compiling and Installing comparepdf =================================== Prerequisites: A C++ compiler, the Qt 4 libraries (I test with Qt 4.7 and Qt 4.6. Earlier Qt's may work although Qt 4.4 and 4.5 will at least need a compiler with tr1 support), and the Poppler libraries (at least version 0.14.0 and including Poppler's C++ and Qt 4 headers). Linux and BSD users should be able to get everything through their package management system---and some distros already include comparepdf so you don't even have to build it. Mac OS X users can get a compiler by installing Xcode; you'll need to get Qt and Poppler separately. 1. Unpack the archive file, comparepdf-XXX.tar.gz 2. Change directory to comparepdf-XXX 3. Run qmake # On some systems, e.g., Fedora or Ubuntu, run qmake-qt4 On Mac OS X use: qmake spec=macx-g++ 4. Run make 5. Copy or soft-link the comparepdf executable to somewhere on your PATH That's it! License ======= This program was written by Mark Summerfield. Copyright (c) 2011-12 Qtrac Ltd. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option), any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (in file gpl-2.0.txt) for more details. comparepdf-1.0.1/comparepdf.10000644000000000000000000000214111703070016015762 0ustar rootroot00000000000000.TH COMPAREPDF 1 "2012-01-10" "comparepdf v1.0.1" .SH NAME comparepdf \- compare two PDF files textually or visually .SH SYNOPSIS .B diffpdf .RI [ file1 ] .RI [ file2 ] .SH SYNOPSIS .B comparepdf .RI [OPTIONS] .RI file1.pdf .RI file2.pdf .SH DESCRIPTION \fBcomparepdf\fP is a command line application used to compare two PDF files. .br The default comparison mode is text mode where the text of each corresponding pair of pages is compared. As soon as a difference is detected the program terminates with a message (unless -v0 is set) and an indicative return code. .br The OPTIONS are -ct or --compare=text (the default) for text mode comparisons or -ca or --compare=appearance for visual comparisons (useful if diagrams or other images have changed), and -v=1 or --verbose=1 for reporting differences (and saying nothing for matching files): use -v=0 for no reporting or -v=2 for reporting both different and matching files. .SH "SEE ALSO" For a GUI tool for showing the detailed differences between PDF files see http://www.qtrac.eu/diffpdf.html. .SH AUTHOR comparepdf was written by Mark Summerfield . comparepdf-1.0.1/comparepdf.pro0000644000000000000000000000121711703070016016425 0ustar rootroot00000000000000SOURCES += main.cpp HEADERS += option_parser.hpp SOURCES += option_parser.cpp LIBS += -lpoppler-qt4 exists($(HOME)/opt/poppler018/) { message(Using locally built Poppler library) INCLUDEPATH += $(HOME)/opt/poppler018/include/poppler/cpp INCLUDEPATH += $(HOME)/opt/poppler018/include/poppler/qt4 LIBS += -Wl,-rpath -Wl,$(HOME)/opt/poppler018/lib -Wl,-L$(HOME)/opt/poppler018/lib } else { exists(/usr/include/poppler/qt4) { INCLUDEPATH += /usr/include/poppler/cpp INCLUDEPATH += /usr/include/poppler/qt4 } else { INCLUDEPATH += /usr/local/include/poppler/cpp INCLUDEPATH += /usr/local/include/poppler/qt4 } } comparepdf-1.0.1/gpl-2.0.txt0000644000000000000000000004310311703070016015403 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. comparepdf-1.0.1/main.cpp0000644000000000000000000001544111703070016015217 0ustar rootroot00000000000000/* Copyright (c) 2011 Qtrac Ltd. All rights reserved. This program or module is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. It is provided for educational purposes and is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #if QT_VERSION >= 0x040600 #include #else #include #endif #include #include #include #include #include #include "option_parser.hpp" #if QT_VERSION >= 0x040600 typedef QSharedPointer PdfDocument; typedef QSharedPointer PdfPage; typedef QSharedPointer PdfTextBox; #else typedef std::tr1::shared_ptr PdfDocument; typedef std::tr1::shared_ptr PdfPage; typedef std::tr1::shared_ptr PdfTextBox; #endif typedef QList TextBoxList; enum Difference{ Same, DifferentPageCount, DifferentTexts, DifferentAppearance, Error}; PdfDocument getPdf(const QString &filename, QString *error); TextBoxList getTextBoxes(PdfPage page); Difference compareFiles(const QString &file1, const QString &file2, bool compareText, QString *error); const QString Version("1.0.1"); int main(int argc, char *argv[]) { QApplication app(argc, argv); AQP::OptionParser parser(app.arguments(), QObject::tr("usage: {program} [options] \n" "\nA program to compare two PDF files.\n"), QObject::tr("\nA return value of 0 means no differences " "detected; 1 or 2 signifies an error; 10 means they differ " "visually, 13 means they differ textually, and 15 means " "they have different page counts.\n" "\nVersion %1 Copyright (c) 2011-12 Qtrac Ltd. " "All rights reserved.\nTo see detailed page by page " "differences in a GUI application, use DiffPDF: see " "http://www.qtrac.eu").arg(Version)); AQP::StringOptionPtr comparisonModeOpt = parser.addStringOption( QObject::tr("c"), QObject::tr("compare")); comparisonModeOpt->setDefaultValue("text"); comparisonModeOpt->setAcceptableValues(QStringList() << QObject::tr("appearance") << QObject::tr("a") << QObject::tr("text") << QObject::tr("t")); comparisonModeOpt->setHelp(QObject::tr("comparison mode")); AQP::IntegerOptionPtr verboseOpt = parser.addIntegerOption( QObject::tr("v"), QObject::tr("verbose")); verboseOpt->setDefaultValue(1); verboseOpt->setAcceptableValues(QSet() << 0 << 1 << 2); verboseOpt->setHelp(QObject::tr("0 = quiet; 1 = report differences; " "2 = report same and different")); AQP::BooleanOptionPtr versionOpt = parser.addBooleanOption("", QObject::tr("version")); versionOpt->setHelp(QObject::tr("show version information and " "terminate")); if (!parser.parse()) return 2; QTextStream out(stdout); if (versionOpt->boolean()) { out << QObject::tr("comparepdf ") << Version << "\n" << QObject::tr("poppler ") << POPPLER_VERSION << "\n"; return 0; } QStringList files = parser.remainder(); if (files.count() != 2 || !files[0].toLower().endsWith(".pdf") || !files[1].toLower().endsWith(".pdf")) { out << QObject::tr("Two PDF files must be specified\n"); return 1; } bool compareText = comparisonModeOpt->value()[0] == 't'; QString error; Difference difference = compareFiles(files[0], files[1], compareText, &error); if (!error.isEmpty()) { out << error << "\n"; return 1; } int result = Same; // 0 if (difference == DifferentPageCount) result = 15; else if (difference == DifferentTexts) result = 13; else if (difference == DifferentAppearance) result = 10; if (!result && verboseOpt->value() == 2) { out << QObject::tr("No differences detected.\n"); } else if (result && verboseOpt->value() > 0) { if (comparisonModeOpt->value()[0] == 't') out << QObject::tr("Files have different texts.\n"); else out << QObject::tr("Files look different.\n"); } return result; } Difference compareFiles(const QString &filename1, const QString &filename2, bool compareText, QString *error) { PdfDocument pdf1 = getPdf(filename1, error); if (!error->isEmpty()) return Error; PdfDocument pdf2 = getPdf(filename2, error); if (!error->isEmpty()) return Error; int count = pdf1->numPages(); if (count != pdf2->numPages()) return DifferentPageCount; for (int page = 0; page < count; ++page) { PdfPage page1(pdf1->page(page)); if (!page1) { *error = QObject::tr("Failed to read page %1 from '%2'.") .arg(page + 1).arg(filename1); return Error; } PdfPage page2(pdf2->page(page)); if (!page2) { *error = QObject::tr("Failed to read page %1 from '%2'.") .arg(page + 1).arg(filename2); return Error; } if (compareText) { TextBoxList list1 = getTextBoxes(page1); TextBoxList list2 = getTextBoxes(page2); QStringList words1; QStringList words2; foreach (const PdfTextBox &box, list1) words1 << box->text(); foreach (const PdfTextBox &box, list2) words2 << box->text(); if (words1.join("") != words2.join("")) return DifferentTexts; } else { QImage image1 = page1->renderToImage(); QImage image2 = page2->renderToImage(); if (image1 != image2) return DifferentAppearance; } } return Same; } PdfDocument getPdf(const QString &filename, QString *error) { PdfDocument pdf(Poppler::Document::load(filename)); if (!pdf) { *error = QObject::tr("Cannot load '%1'.").arg(filename); } else if (pdf->isLocked()) { *error = QObject::tr("Cannot read a locked PDF ('%1').").arg( filename); #if QT_VERSION >= 0x040600 pdf.clear(); #else pdf.reset(); #endif } return pdf; } TextBoxList getTextBoxes(PdfPage page) { TextBoxList boxes; foreach (Poppler::TextBox *box, page->textList()) { PdfTextBox box_ptr(box); boxes.append(box_ptr); } return boxes; } comparepdf-1.0.1/option_parser.cpp0000644000000000000000000004456311703070016017166 0ustar rootroot00000000000000/* Copyright (c) 2009-10 Qtrac Ltd. All rights reserved. This program or module is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is provided for educational purposes and is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "option_parser.hpp" #include #include #include #include namespace AQP { inline bool caseInsensitiveLessThan(const QString &a, const QString &b) { return a.toLower().localeAwareCompare(b.toLower()) < 0; } OptionParser::OptionParser(const QStringList &arguments, const QString &help, const QString ©right) : m_arguments(arguments), m_help(help), m_copyright(copyright) { if (m_arguments.isEmpty()) m_arguments = QCoreApplication::arguments(); } IntegerOptionPtr OptionParser::addIntegerOption(const QString &shortName, const QString &longName) { Q_ASSERT_X(longName != tr("help") && !m_options.contains(longName) && !m_options.contains(shortName), "addIntegerOption", "invalid long name (help) or duplicate short or long name"); IntegerOptionPtr option(new IntegerOption(shortName, longName)); addOption(option); return option; } RealOptionPtr OptionParser::addRealOption(const QString &shortName, const QString &longName) { Q_ASSERT_X(longName != tr("help") && !m_options.contains(longName) && !m_options.contains(shortName), "addRealOption", "invalid long name (help) or duplicate short or long name"); RealOptionPtr option(new RealOption(shortName, longName)); addOption(option); return option; } BooleanOptionPtr OptionParser::addBooleanOption(const QString &shortName, const QString &longName) { Q_ASSERT_X(longName != tr("help") && !m_options.contains(longName) && !m_options.contains(shortName), "addBooleanOption", "invalid long name (help) or duplicate short or long name"); BooleanOptionPtr option(new BooleanOption(shortName, longName)); addOption(option); return option; } StringOptionPtr OptionParser::addStringOption(const QString &shortName, const QString &longName) { Q_ASSERT_X(longName != tr("help") && !m_options.contains(longName) && !m_options.contains(shortName), "addStringOption", "invalid long name (help) or duplicate short or long name"); StringOptionPtr option(new StringOption(shortName, longName)); addOption(option); return option; } void OptionParser::addOption(OptionPtr option) { if (!option->longName().isEmpty()) m_options[option->longName()] = option; if (!option->shortName().isEmpty()) m_options[option->shortName()] = option; } BooleanOptionPtr OptionParser::addHelpOption() { QString shortName(m_options.contains(tr("h")) ? "" : "h"); QString longName(tr("help")); BooleanOptionPtr option(new BooleanOption(shortName, longName)); option->setHelp(tr("show this information and terminate")); addOption(option); return option; } int OptionParser::usage(const QString &error) { QTextStream err(stderr); QString program = QFileInfo(m_arguments.at(0)).baseName(); QString message; if (!m_help.isEmpty()) err << m_help.replace("{program}", program) << "\n"; else err << tr("usage: %1 [options]\n").arg(program); err << tr("options:\n"); (void) addHelpOption(); QMap lengthForType; lengthForType[QVariant::String] = QString("STRING").length(); lengthForType[QVariant::Bool] = 0; lengthForType[QVariant::Int] = QString("INTEGER").length(); lengthForType[QVariant::Double] = QString("REAL").length(); int longest = 0; int anyRequired = 0; QMapIterator i(m_options); QMap options; while (i.hasNext()) { i.next(); OptionPtr option = i.value(); const QString &name = option->longName(); int length = name.length() + lengthForType[option->type()]; if (length > longest) longest = length; if (i.value()->required()) anyRequired = 1; const QString longName = (option->longName().isEmpty() ? option->shortName() : option->longName()).toLower(); const QString shortName = (option->shortName().isEmpty() ? option->longName() : option->shortName()).toLower(); const QString key = QString("%1:%2").arg(longName).arg(shortName); options[key] = option; } longest += anyRequired; bool required = false; const int Width = QString(" -x --= ").length() + longest; QMapIterator j(options); while (j.hasNext()) { j.next(); OptionPtr option = j.value(); QString line; if (option->shortName().isEmpty()) line += " "; else line += " -" + option->shortName(); if (!option->longName().isEmpty()) line += " --" + option->longName(); bool close = false; if (option->type() == QVariant::Bool && option->longName() != tr("help")) { QString offset(Width - line.length(), ' '); line += QString("%1(default off)").arg(offset); close = true; } else if (option->type() == QVariant::Int) { IntegerOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); line += "=INTEGER"; if (opt->required()) { line += "*"; required = true; } else if (anyRequired) line += " "; QString offset(Width - line.length(), ' '); if (opt->hasMinimum() && opt->hasMaximum()) { line += QString("%1(%2-%3").arg(offset) .arg(opt->minimum()).arg(opt->maximum()); close = true; } else if (opt->hasMinimum()) { line += tr("%1(min. %2").arg(offset).arg(opt->minimum()); close = true; } else if (opt->hasMaximum()) { line += tr("%1(max. %2").arg(offset).arg(opt->maximum()); close = true; } else if (opt->hasAcceptableValues()) { line += offset + "("; QList list = QList::fromSet( opt->acceptableValues()); qSort(list); int i = 0; for (; i < list.count() - 1; ++i) line += QString::number(list.at(i)) + ", "; line += QString::number(list.at(i)); close = true; } if (opt->hasDefaultValue()) { if (close) line += tr("; default %1)").arg(opt->defaultValue()); else line += tr(" (default %1)").arg(opt->defaultValue()); } else if (close) line += ")"; } if (option->type() == QVariant::Double) { RealOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); line += "=REAL"; if (opt->required()) { line += "*"; required = true; } else if (anyRequired) line += " "; QString offset(Width - line.length(), ' '); if (opt->hasMinimum() && opt->hasMaximum()) { line += QString("%1(%2-%3").arg(offset) .arg(opt->minimum()).arg(opt->maximum()); close = true; } else if (opt->hasMinimum()) { line += tr("%1(min. %2").arg(offset) .arg(opt->minimum()); close = true; } else if (opt->hasMaximum()) { line += tr("%1(max. %2").arg(offset).arg(opt->maximum()); close = true; } if (opt->hasDefaultValue()) { if (close) line += tr("; default %1)").arg(opt->defaultValue()); else line += tr(" (default %1)").arg(opt->defaultValue()); } else if (close) line += ")"; } else if (option->type() == QVariant::String) { StringOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); line += "=STRING"; if (opt->required()) { line += "*"; required = true; } else if (anyRequired) line += " "; if (opt->hasAcceptableValues()) { line += QString(Width - line.length(), ' ') + "("; QStringList list = QStringList::fromSet( opt->acceptableValues()); qSort(list.begin(), list.end(), caseInsensitiveLessThan); int i = 0; for (; i < list.count() - 1; ++i) line += list.at(i) + ", "; line += list.at(i); close = true; } if (opt->hasDefaultValue()) { if (close) line += "; "; else { line += "("; close = true; } line += QString("default %1").arg(opt->defaultValue()); } if (close) line += ")"; } err << line; if (option->hasHelp()) { if (close) err << " "; else err << QString(Width - line.length(), ' '); err << option->help(); } err << "\n"; } if (required) err << tr("(* required)\n"); if (!m_copyright.isEmpty()) err << m_copyright << "\n"; if (!error.isEmpty()) err << tr("\nERROR: ") << error << "\n"; return 2; } bool OptionParser::parse() { int index = 1; for (; index < m_arguments.count(); ++index) { QString name = m_arguments.at(index); QString arg(name); if (!arg.startsWith("-")) // First of non-option args break; if (arg == "--") { // Skip over to first of non-option args ++index; break; } QString value; int i = arg.indexOf("="); if (i > -1) { value = arg.mid(i + 1); arg = arg.left(i); } else if (arg.startsWith("-") && arg.length() > 2 && arg.at(1) != '-') { value = arg.mid(2); arg = arg.left(2); } name = arg; if (arg.startsWith("--")) arg = arg.mid(2); else arg = arg.mid(1); if (arg == "help" || (!m_options.contains("h") && arg == "h")) return !usage(); if (!m_options.contains(arg)) return !usage(tr("unrecognized option %1") .arg(name)); OptionPtr option = m_options[arg]; if (option->type() == QVariant::Bool) { BooleanOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); if (!value.isEmpty()) return !usage(tr("unwanted value for %1").arg(name)); opt->setValue(true); continue; } if (value.isEmpty()) { if (index + 1 >= m_arguments.count()) return !usage(tr("missing value for %1").arg(name)); value = m_arguments.at(++index); } if (option->type() == QVariant::Double) { RealOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); bool ok; double v = value.toDouble(&ok); if (!ok) return !usage(tr("invalid value for %1").arg(name)); if (opt->hasMinimum()) { if (v < opt->minimum()) return !usage(tr("too small a value for %1") .arg(name)); } if (opt->hasMaximum()) { if (v > opt->maximum()) return !usage(tr("too big a value for %1").arg(name)); } opt->setValue(v); } else if (option->type() == QVariant::Int) { IntegerOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); bool ok; int v = value.toInt(&ok); if (!ok) return !usage(tr("invalid value for %1").arg(name)); if (opt->hasMinimum()) { if (v < opt->minimum()) return !usage(tr("too small a value for %1") .arg(name)); } if (opt->hasMinimum()) { if (v > opt->maximum()) return !usage(tr("too big a value for %1").arg(name)); } if (opt->hasAcceptableValues() && !opt->acceptableValues().contains(v)) return !usage(tr("invalid value for %1").arg(name)); opt->setValue(v); } else if (option->type() == QVariant::String) { StringOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); if (opt->hasAcceptableValues() && !opt->acceptableValues().contains(value)) return !usage(tr("invalid value for %1").arg(name)); opt->setValue(value); } } QMapIterator i(m_options); while (i.hasNext()) { i.next(); OptionPtr option = i.value(); if (!option->hasValue()) { if (option->required()) return !usage(tr("missing mandatory option %1") .arg(option->longName().isEmpty() ? QString("-%1").arg(option->shortName()) : QString("--%1").arg(option->longName()))); if (!option->hasDefaultValue()) continue; if (option->type() == QVariant::Int) { IntegerOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); opt->setValue(opt->defaultValue()); } else if (option->type() == QVariant::Double) { RealOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); opt->setValue(opt->defaultValue()); } else if (option->type() == QVariant::Bool) { BooleanOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); opt->setValue(opt->defaultValue()); } else if (option->type() == QVariant::String) { StringOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); opt->setValue(opt->defaultValue()); } } } m_remainder = m_arguments.mid(index); return true; } int OptionParser::integer(const QString &name) const { Q_ASSERT(m_options.contains(name)); OptionPtr option = m_options[name]; Q_ASSERT(option->type() == QVariant::Int); IntegerOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); return opt->value(); } double OptionParser::real(const QString &name) const { Q_ASSERT(m_options.contains(name)); OptionPtr option = m_options[name]; Q_ASSERT(option->type() == QVariant::Double); RealOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); return opt->value(); } bool OptionParser::boolean(const QString &name) const { Q_ASSERT(m_options.contains(name)); OptionPtr option = m_options[name]; Q_ASSERT(option->type() == QVariant::Bool); BooleanOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); return opt->value(); } QString OptionParser::string(const QString &name) const { Q_ASSERT(m_options.contains(name)); OptionPtr option = m_options[name]; Q_ASSERT(option->type() == QVariant::String); StringOption *opt = #if QT_VERSION >= 0x040500 dynamic_cast(option.data()); #else dynamic_cast(option.get()); #endif Q_ASSERT(opt); return opt->value(); } } // namespace AQP comparepdf-1.0.1/option_parser.hpp0000644000000000000000000002110711703070016017160 0ustar rootroot00000000000000#ifndef OPTION_PARSER_HPP #define OPTION_PARSER_HPP /* Copyright (c) 2009-10 Qtrac Ltd. All rights reserved. This program or module is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is provided for educational purposes and is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include #include #if QT_VERSION >= 0x040500 #include #else #include #endif #include #include #include namespace AQP { class PrivateOption { public: explicit PrivateOption(const QString &shortName=QString(), const QString &longName=QString()) : m_shortName(shortName), m_longName(longName), m_required(false) { Q_ASSERT(!(longName.isEmpty() && shortName.isEmpty())); } virtual ~PrivateOption() {} bool required() const { return m_required; } virtual void setRequired(bool on=true) { m_required = on; } QString help() const { return m_help; } void setHelp(const QString &help) { m_help = help; } bool hasHelp() const { return !m_help.isEmpty(); } QString shortName() const { return m_shortName; } QString longName() const { return m_longName; } bool hasValue() const { return m_value.isValid(); } bool hasDefaultValue() const { return m_defaultValue.isValid(); } virtual QVariant::Type type() const = 0; protected: QVariant m_defaultValue; QVariant m_value; private: const QString m_shortName; const QString m_longName; bool m_required; QString m_help; }; class IntegerOption : public PrivateOption { public: explicit IntegerOption(const QString &shortName=QString(), const QString &longName=QString()) : PrivateOption(shortName, longName) {} int defaultValue() const { return m_defaultValue.toInt(); } void setDefaultValue(int value) { m_defaultValue = value; } bool hasDefaultValue() const { return m_defaultValue.isValid(); } int minimum() const { return m_minimum.toInt(); } void setMinimum(int minimum) { m_minimum = minimum; } bool hasMinimum() const { return m_minimum.isValid(); } int maximum() const { return m_maximum.toInt(); } void setMaximum(int maximum) { m_maximum = maximum; } bool hasMaximum() const { return m_maximum.isValid(); } void setRange(int minimum, int maximum) { m_minimum = minimum; m_maximum = maximum; } QSet acceptableValues() const { return m_acceptableValues; } void setAcceptableValues(const QList &acceptableValues) { m_acceptableValues = QSet::fromList(acceptableValues); } void setAcceptableValues(const QSet &acceptableValues) { m_acceptableValues = acceptableValues; } int value() const { return m_value.toInt(); } void setValue(int value) { m_value = value; } bool hasAcceptableValues() const { return !m_acceptableValues.isEmpty(); } int integer() const { return m_value.toInt(); } QVariant::Type type() const { return QVariant::Int; } private: QVariant m_minimum; QVariant m_maximum; QSet m_acceptableValues; }; class RealOption : public PrivateOption { public: explicit RealOption(const QString &shortName=QString(), const QString &longName=QString()) : PrivateOption(shortName, longName) {} double defaultValue() const { return m_defaultValue.toInt(); } void setDefaultValue(double value) { m_defaultValue = value; } bool hasDefaultValue() const { return m_defaultValue.isValid(); } double minimum() const { return m_minimum.toInt(); } void setMinimum(double minimum) { m_minimum = minimum; } bool hasMinimum() const { return m_minimum.isValid(); } double maximum() const { return m_maximum.toInt(); } void setMaximum(double maximum) { m_maximum = maximum; } bool hasMaximum() const { return m_maximum.isValid(); } void setRange(double minimum, double maximum) { m_minimum = minimum; m_maximum = maximum; } double value() const { return m_value.toDouble(); } void setValue(double value) { m_value = value; } double real() const { return m_value.toDouble(); } QVariant::Type type() const { return QVariant::Double; } private: QVariant m_minimum; QVariant m_maximum; }; class BooleanOption : public PrivateOption { public: explicit BooleanOption(const QString &shortName=QString(), const QString &longName=QString()) : PrivateOption(shortName, longName) {} bool defaultValue() const { return m_defaultValue.toInt(); } void setDefaultValue(bool value) { m_defaultValue = value; } bool hasDefaultValue() const { return m_defaultValue.isValid(); } bool value() const { return m_value.toBool(); } void setValue(bool value) { m_value = value; } bool boolean() const { return m_value.toBool(); } QVariant::Type type() const { return QVariant::Bool; } void setRequired(bool=true) { Q_ASSERT(false); } }; class StringOption : public PrivateOption { public: explicit StringOption(const QString &shortName=QString(), const QString &longName=QString()) : PrivateOption(shortName, longName) {} QString defaultValue() const { return m_defaultValue.toString(); } void setDefaultValue(const QString &value) { m_defaultValue = value; } bool hasDefaultValue() const { return m_defaultValue.isValid(); } QSet acceptableValues() const { return m_acceptableValues; } void setAcceptableValues(const QStringList &acceptableValues) { m_acceptableValues = QSet::fromList(acceptableValues); } void setAcceptableValues(const QSet &acceptableValues) { m_acceptableValues = acceptableValues; } bool hasAcceptableValues() const { return !m_acceptableValues.isEmpty(); } QString value() const { return m_value.toString(); } void setValue(const QString &value) { m_value = value; } QString string() const { return m_value.toString(); } QVariant::Type type() const { return QVariant::String; } private: QSet m_acceptableValues; }; #if QT_VERSION >= 0x040500 typedef QSharedPointer OptionPtr; typedef QSharedPointer IntegerOptionPtr; typedef QSharedPointer RealOptionPtr; typedef QSharedPointer BooleanOptionPtr; typedef QSharedPointer StringOptionPtr; #else typedef std::tr1::shared_ptr OptionPtr; typedef std::tr1::shared_ptr IntegerOptionPtr; typedef std::tr1::shared_ptr RealOptionPtr; typedef std::tr1::shared_ptr BooleanOptionPtr; typedef std::tr1::shared_ptr StringOptionPtr; #endif class OptionParser { Q_DECLARE_TR_FUNCTIONS(OptionParser) public: explicit OptionParser(const QStringList &arguments=QStringList(), const QString &help=QString(), const QString ©right=QString()); bool parse(); int usage(const QString &error=QString()); int integer(const QString &name) const; double real(const QString &name) const; bool boolean(const QString &name) const; QString string(const QString &name) const; bool hasValue(const QString &name) const { return m_options[name]->hasValue(); } QStringList remainder() const { return m_remainder; } OptionPtr option(const QString &name) { return m_options[name]; } IntegerOptionPtr addIntegerOption(const QString &shortName=QString(), const QString &longName=QString()); RealOptionPtr addRealOption(const QString &shortName=QString(), const QString &longName=QString()); BooleanOptionPtr addBooleanOption(const QString &shortName=QString(), const QString &longName=QString()); StringOptionPtr addStringOption(const QString &shortName=QString(), const QString &longName=QString()); private: BooleanOptionPtr addHelpOption(); void addOption(OptionPtr option); QStringList m_arguments; QString m_help; QString m_copyright; QStringList m_remainder; QMap m_options; }; } // namespace AQP #endif // OPTION_PARSER_HPP