├── .github └── workflows │ ├── tagview-ci-mingw.yml │ ├── tagview-ci-msvc.yml │ └── tagview-ci-ubuntu.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── _clang-format ├── docs └── screenshot.png └── src ├── CMakeLists.txt ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── optionsdialog.cpp ├── optionsdialog.h ├── optionsdialog.ui ├── tifffile.cpp └── tifffile.h /.github/workflows/tagview-ci-mingw.yml: -------------------------------------------------------------------------------- 1 | name: TagView-CI-MinGW 2 | 3 | on: [push] 4 | 5 | 6 | env: 7 | QT_VERSION: 6.5.3 8 | 9 | jobs: 10 | build: 11 | name: "Build" 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - name: Install ninja (windows) 16 | run: choco install ninja 17 | 18 | - name: Install Qt 19 | uses: jurplel/install-qt-action@v3 20 | with: 21 | version: ${{env.QT_VERSION}} 22 | arch: win64_mingw 23 | 24 | - name: Checkout source code 25 | uses: actions/checkout@v3 26 | 27 | - name: Build 28 | run: | 29 | cmake -S . -B build_dir -G "Ninja Multi-Config" 30 | cmake --build build_dir --config Release 31 | -------------------------------------------------------------------------------- /.github/workflows/tagview-ci-msvc.yml: -------------------------------------------------------------------------------- 1 | name: TagView-CI-MSVC 2 | 3 | on: [push] 4 | 5 | 6 | env: 7 | QT_VERSION: 6.5.3 8 | 9 | jobs: 10 | build: 11 | name: "Build" 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - name: Install ninja (windows) 16 | run: choco install ninja 17 | 18 | - name: Install Qt 19 | uses: jurplel/install-qt-action@v3 20 | with: 21 | version: ${{env.QT_VERSION}} 22 | 23 | - name: Call vcvarsall.bat Windows amd64 24 | uses: ilammy/msvc-dev-cmd@v1 25 | with: 26 | arch: amd64 27 | 28 | - name: Checkout source code 29 | uses: actions/checkout@v3 30 | 31 | - name: Build 32 | run: | 33 | cmake -S . -B build_dir -G "Ninja Multi-Config" 34 | cmake --build build_dir --config Release 35 | -------------------------------------------------------------------------------- /.github/workflows/tagview-ci-ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: TagView-CI-Ubuntu 2 | 3 | on: [push] 4 | 5 | env: 6 | QT_VERSION: 6.5.3 7 | 8 | jobs: 9 | build: 10 | name: "Build" 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Install ninja (linux) 15 | run: sudo apt install ninja-build 16 | 17 | - name: Install Qt 18 | uses: jurplel/install-qt-action@v3 19 | with: 20 | version: ${{env.QT_VERSION}} 21 | 22 | - name: Checkout source code 23 | uses: actions/checkout@v3 24 | 25 | - name: Build 26 | run: | 27 | cmake -S . -B build_dir -G "Ninja Multi-Config" 28 | cmake --build build_dir --config Release 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | # common 5 | *~ 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *_pch.h.cpp 15 | *_resource.rc 16 | *.qm 17 | .#* 18 | *.*# 19 | tags 20 | .DS_Store 21 | *.debug 22 | Makefile* 23 | *.prl 24 | *.app 25 | moc_*.cpp 26 | ui_*.h 27 | qrc_*.cpp 28 | Thumbs.db 29 | # KDE 30 | .directory 31 | 32 | # python temporary files 33 | *.pyc 34 | 35 | # qtcreator generated files 36 | *.pro.user* 37 | *.qmlproject.user* 38 | *.pluginspec 39 | 40 | # xemacs temporary files 41 | *.flc 42 | 43 | # Vim temporary files 44 | .*.swp 45 | 46 | # Visual Studio generated files 47 | *.ib_pdb_index 48 | *.idb 49 | *.ilk 50 | *.pdb 51 | *.sln 52 | *.suo 53 | *.vcproj 54 | *.vcxproj* 55 | *vcproj.*.*.user 56 | *vcxproj.*.*.user 57 | *.ncb 58 | *.opensdf 59 | *.sdf 60 | 61 | # MinGW generated files 62 | *.Debug 63 | *.Release 64 | 65 | # Directories to ignore 66 | # --------------------- 67 | 68 | temp 69 | debug 70 | lib/* 71 | lib64/* 72 | release 73 | doc/html/* 74 | ipch/* 75 | 76 | .rcc 77 | .pch 78 | 79 | # CMake 80 | CMakeLists.txt.user 81 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | project(QtTiffTagViewer VERSION 0.1.0 LANGUAGES CXX) 4 | 5 | set(CMAKE_AUTOUIC ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | add_subdirectory(src) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtTiffTagViewer 2 | 3 | ![ubuntu](https://github.com/dbzhang800/QtTiffTagViewer/actions/workflows/tagview-ci-ubuntu.yml/badge.svg) 4 | ![msvc](https://github.com/dbzhang800/QtTiffTagViewer/actions/workflows/tagview-ci-msvc.yml/badge.svg) 5 | ![mingw](https://github.com/dbzhang800/QtTiffTagViewer/actions/workflows/tagview-ci-mingw.yml/badge.svg) 6 | 7 | QtTiffTagViewer is not a tiff image file viewer. 8 | 9 | QtTiffTagViewer is an application for viewing TIFF **tags**, which is written in C++ Qt. It allows you to easily view the TIFF tags of each Image File Directory (IFD). 10 | 11 | This repository is used as a demonstration of how to make Qt work with GitHub Actions as well. 12 | 13 | ![screenshot](docs/screenshot.png) 14 | -------------------------------------------------------------------------------- /_clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | Standard: Cpp11 3 | ColumnLimit: 100 4 | PointerBindsToType: false 5 | BreakBeforeBinaryOperators: NonAssignment 6 | BreakBeforeBraces: Custom 7 | BraceWrapping: 8 | AfterClass: true 9 | AfterControlStatement: false 10 | AfterEnum: false 11 | AfterFunction: true 12 | AfterNamespace: false 13 | AfterObjCDeclaration: false 14 | AfterStruct: true 15 | AfterUnion: false 16 | BeforeCatch: false 17 | BeforeElse: false 18 | IndentBraces: false 19 | AlignAfterOpenBracket: true 20 | AlwaysBreakTemplateDeclarations: true 21 | AllowShortFunctionsOnASingleLine: InlineOnly 22 | NamespaceIndentation: None 23 | SortIncludes: false 24 | ForEachMacros: [ forever, foreach, Q_FOREACH, BOOST_FOREACH ] 25 | 26 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbzhang800/QtTiffTagViewer/6b1b3b4bf9b1bd44eed7a05564df58bfe7d3e68e/docs/screenshot.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | find_package(Qt6 REQUIRED COMPONENTS Widgets) 3 | 4 | file(GLOB PROJECT_SOURCES *.cpp *.h *ui *.qrc *.rc) 5 | 6 | qt_add_executable(tagviewer 7 | MANUAL_FINALIZATION 8 | ${PROJECT_SOURCES} 9 | ) 10 | 11 | set_target_properties(tagviewer PROPERTIES OUTPUT_NAME "QtTiffTagViewer") 12 | target_compile_definitions(tagviewer PRIVATE PROJECT_VERSION="${PROJECT_VERSION}") 13 | target_link_libraries(tagviewer PRIVATE Qt::Widgets) 14 | 15 | 16 | include(GNUInstallDirs) 17 | install(TARGETS tagviewer 18 | BUNDLE DESTINATION . 19 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 20 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 21 | ) 22 | 23 | qt_finalize_executable(tagviewer) 24 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #include "mainwindow.h" 26 | #include 27 | #include 28 | #include 29 | 30 | int main(int argc, char *argv[]) 31 | { 32 | QApplication a(argc, argv); 33 | a.setApplicationName("QtTiffTagViewer"); 34 | a.setOrganizationName("dbzhang800"); 35 | a.setApplicationVersion(PROJECT_VERSION); 36 | 37 | QSettings::setDefaultFormat(QSettings::IniFormat); 38 | QLoggingCategory::setFilterRules("*.debug=false"); 39 | qSetMessagePattern("[%{time yyyyMMdd h:mm:ss.zzz t} %{category} %{type}: %{message}"); 40 | 41 | MainWindow w; 42 | w.show(); 43 | 44 | return a.exec(); 45 | } 46 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #include "mainwindow.h" 26 | #include "ui_mainwindow.h" 27 | #include "optionsdialog.h" 28 | #include "tifffile.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | MainWindow::MainWindow(QWidget *parent) 39 | : QMainWindow(parent) 40 | , ui(new Ui::MainWindow) 41 | { 42 | ui->setupUi(this); 43 | 44 | // create action for recent files 45 | for (int i = 0; i < MaxRecentFiles; ++i) { 46 | auto act = new QAction(this); 47 | act->setVisible(false); 48 | act->setProperty("id", i); 49 | ui->menu_File->insertAction(ui->actionExit, act); 50 | m_actionRecentFiles[i] = act; 51 | connect(act, &QAction::triggered, this, &MainWindow::onActionRecentFileTriggered); 52 | } 53 | m_actionSeparator = ui->menu_File->insertSeparator(ui->actionExit); 54 | m_actionSeparator->setVisible(false); 55 | 56 | connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onActionOpenTriggered); 57 | connect(ui->actionExit, &QAction::triggered, qApp, &QApplication::quit); 58 | connect(ui->actionOptions, &QAction::triggered, this, &MainWindow::onActionOptionsTriggered); 59 | connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::onActionAboutTriggered); 60 | connect(ui->actionAboutQt, &QAction::triggered, this, [this]() { QMessageBox::aboutQt(this); }); 61 | 62 | loadSettings(); 63 | if (qApp->arguments().size() > 1) { 64 | auto filePath = qApp->arguments().value(1); 65 | if (QFileInfo::exists((filePath))) 66 | doOpenTiffFile(filePath); 67 | } 68 | } 69 | 70 | MainWindow::~MainWindow() 71 | { 72 | delete ui; 73 | } 74 | 75 | void MainWindow::closeEvent(QCloseEvent *evt) 76 | { 77 | saveSettings(); 78 | evt->accept(); 79 | } 80 | 81 | void MainWindow::onActionOpenTriggered() 82 | { 83 | auto filePath = QFileDialog::getOpenFileName( 84 | this, tr("Open Tiff"), m_recentFiles.value(0, QString()), tr("Tiff Image(*.tiff *.tif)")); 85 | if (filePath.isEmpty()) 86 | return; 87 | 88 | doOpenTiffFile(filePath); 89 | } 90 | 91 | void MainWindow::onActionOptionsTriggered() 92 | { 93 | OptionsDialog dlg(this); 94 | dlg.setParserOptions(m_parserOptions); 95 | 96 | if (dlg.exec() == QDialog::Accepted) 97 | m_parserOptions = dlg.parserOptions(); 98 | } 99 | 100 | void MainWindow::onActionAboutTriggered() 101 | { 102 | QString text = tr("%1 %2

" 103 | "Copyright %3 Debao Zhang <hello@debao.me>
" 104 | "All right reserved.
" 105 | "
" 106 | "Permission is hereby granted, free of charge, to any person obtaining" 107 | "a copy of this software and associated documentation files (the" 108 | "\"Software\"), to deal in the Software without restriction, including" 109 | "without limitation the rights to use, copy, modify, merge, publish," 110 | "distribute, sublicense, and/or sell copies of the Software, and to" 111 | "permit persons to whom the Software is furnished to do so, subject to" 112 | "the following conditions:
" 113 | "
" 114 | "The above copyright notice and this permission notice shall be" 115 | "included in all copies or substantial portions of the Software.
" 116 | "
" 117 | "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND," 118 | "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF" 119 | "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND" 120 | "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE" 121 | "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION" 122 | "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION" 123 | "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.") 124 | .arg(qApp->applicationName(), qApp->applicationVersion()) 125 | .arg(2023); 126 | 127 | QMessageBox::about(this, tr("About %1").arg(qApp->applicationName()), text); 128 | } 129 | 130 | void MainWindow::onActionRecentFileTriggered() 131 | { 132 | auto action = qobject_cast(sender()); 133 | auto filePath = m_recentFiles.value(action->property("id").toInt(), QString()); 134 | if (!QFileInfo::exists(filePath)) 135 | return; 136 | doOpenTiffFile(filePath); 137 | } 138 | 139 | void MainWindow::loadSettings() 140 | { 141 | QSettings settings; 142 | settings.beginGroup("geometry"); 143 | restoreGeometry(settings.value("rect").toByteArray()); 144 | restoreState(settings.value("state").toByteArray()); 145 | settings.endGroup(); 146 | 147 | settings.beginGroup("parser"); 148 | m_parserOptions.parserSubIfds = settings.value("parsersubifds", true).toBool(); 149 | settings.endGroup(); 150 | 151 | m_recentFiles = settings.value("recentfiles").toStringList(); 152 | updateActionRecentFiles(); 153 | } 154 | 155 | void MainWindow::saveSettings() 156 | { 157 | QSettings settings; 158 | settings.beginGroup("geometry"); 159 | settings.setValue("rect", saveGeometry()); 160 | settings.setValue("state", saveState()); 161 | settings.endGroup(); 162 | 163 | settings.beginGroup("parser"); 164 | settings.setValue("parsersubifds", m_parserOptions.parserSubIfds); 165 | settings.endGroup(); 166 | 167 | settings.setValue("recentfiles", m_recentFiles); 168 | } 169 | 170 | void MainWindow::doOpenTiffFile(const QString &filePath) 171 | { 172 | m_recentFiles.removeOne(filePath); 173 | m_recentFiles.insert(0, filePath); 174 | if (m_recentFiles.size() > MaxRecentFiles) 175 | m_recentFiles.removeLast(); 176 | updateActionRecentFiles(); 177 | 178 | TiffFile tiff(filePath, m_parserOptions); 179 | 180 | ui->treeWidget->clear(); 181 | 182 | if (tiff.hasError()) { 183 | ui->logEdit->appendPlainText( 184 | QString("Fail to open the tiff file: %1 [%2]").arg(filePath).arg(tiff.errorString())); 185 | return; 186 | } 187 | setWindowTitle(tr("%1 - QtTiffTagViewer").arg(filePath)); 188 | 189 | // headeritem 190 | { 191 | auto headerItem = new QTreeWidgetItem(ui->treeWidget); 192 | headerItem->setText(0, tr("Header")); 193 | headerItem->setText(1, tiff.headerBytes().toHex(' ')); 194 | headerItem->setExpanded(true); 195 | 196 | auto childItem = new QTreeWidgetItem(headerItem); 197 | 198 | childItem->setText(0, tr("ByteOrder")); 199 | childItem->setText(1, 200 | QString("%1 (%2)") 201 | .arg(tiff.headerBytes().left(2)) 202 | .arg(tiff.byteOrder() == TiffFile::BigEndian ? tr("BigEndian") 203 | : tr("LittleEndian"))); 204 | 205 | childItem = new QTreeWidgetItem(headerItem); 206 | childItem->setText(0, tr("Version")); 207 | childItem->setText(1, 208 | QString("%1 (%2)") 209 | .arg(tiff.version()) 210 | .arg(tiff.isBigTiff() ? "BigTiff" : "Classic Tiff")); 211 | 212 | childItem = new QTreeWidgetItem(headerItem); 213 | childItem->setText(0, tr("IFD0Offset")); 214 | childItem->setText(1, QString::number(tiff.ifd0Offset())); 215 | } 216 | 217 | // IfdItem 218 | foreach (const auto ifd, tiff.ifds()) 219 | fillSubIfdItem(nullptr, ifd); 220 | } 221 | 222 | void MainWindow::updateActionRecentFiles() 223 | { 224 | auto count = qMin(m_recentFiles.size(), static_cast(MaxRecentFiles)); 225 | for (int i = 0; i < count; ++i) { 226 | m_actionRecentFiles[i]->setText(QString("%1 %2").arg(i).arg(m_recentFiles[i])); 227 | m_actionRecentFiles[i]->setVisible(true); 228 | } 229 | for (int i = count; i < MaxRecentFiles; ++i) 230 | m_actionRecentFiles[i]->setVisible(false); 231 | 232 | m_actionSeparator->setVisible(count > 0); 233 | } 234 | 235 | void MainWindow::fillIfdEntryItem(QTreeWidgetItem *parentItem, const TiffIfdEntry &de) 236 | { 237 | const auto tagName = de.tagName(); 238 | QStringList valueStrings; 239 | foreach (auto v, de.values()) { 240 | auto valueString = v.toString(); 241 | if (v.typeId() == QMetaType::QString) { 242 | valueString.replace(QLatin1String("\r"), QLatin1String("\\r")); 243 | valueString.replace(QLatin1String("\n"), QLatin1String("\\n")); 244 | valueString.replace(QLatin1String("\t"), QLatin1String("\\t")); 245 | valueString.replace(QLatin1String("\v"), QLatin1String("\\v")); 246 | valueString.replace(QLatin1String("\b"), QLatin1String("\\b")); 247 | } 248 | valueStrings.append(valueString); 249 | } 250 | auto valueString = valueStrings.join(" "); 251 | valueString = 252 | QFontMetricsF(ui->treeWidget->font()).elidedText(valueString, Qt::ElideRight, 400); 253 | auto vd = de.valueDescription(); 254 | if (!vd.isEmpty()) 255 | valueString = QString("%1 [%2]").arg(valueString, vd); 256 | 257 | auto deItem = new QTreeWidgetItem(parentItem); 258 | deItem->setText(0, tr("DE %1").arg(tagName)); 259 | deItem->setText(1, 260 | QString("Type=%2, Count=%3, Values=%4") 261 | .arg(de.typeName()) 262 | .arg(de.count()) 263 | .arg(valueString)); 264 | 265 | auto item = new QTreeWidgetItem(deItem); 266 | item->setText(0, tr("Tag")); 267 | item->setText(1, QString("%1 %2").arg(tagName).arg(de.tag())); 268 | 269 | item = new QTreeWidgetItem(deItem); 270 | item->setText(0, tr("DataType")); 271 | item->setText(1, QString("%1 %2").arg(de.typeName()).arg(de.type())); 272 | 273 | item = new QTreeWidgetItem(deItem); 274 | item->setText(0, tr("Count")); 275 | item->setText(1, QString::number(de.count())); 276 | 277 | item = new QTreeWidgetItem(deItem); 278 | item->setText(0, tr("ValueOrOffset")); 279 | item->setText(1, de.valueOrOffset().toHex(' ')); 280 | 281 | item = new QTreeWidgetItem(deItem); 282 | item->setText(0, tr("Values")); 283 | item->setText(1, valueString); 284 | for (auto i = 0; i < valueStrings.size(); ++i) { 285 | auto valueItem = new QTreeWidgetItem(item); 286 | valueItem->setText(0, tr("Value[%1]").arg(i)); 287 | valueItem->setText(1, valueStrings[i]); 288 | } 289 | } 290 | 291 | void MainWindow::fillSubIfdItem(QTreeWidgetItem *parentItem, const TiffIfd &ifd) 292 | { 293 | auto ifdItem = new QTreeWidgetItem(parentItem); 294 | if (!parentItem) 295 | ui->treeWidget->addTopLevelItem(ifdItem); 296 | 297 | ifdItem->setText(0, tr("IFD")); 298 | ifdItem->setText(1, ""); 299 | ifdItem->setExpanded(true); 300 | 301 | int width = -1; 302 | int height = -1; 303 | 304 | auto childItem = new QTreeWidgetItem(ifdItem); 305 | childItem->setText(0, tr("EntriesCount")); 306 | childItem->setText(1, QString::number(ifd.ifdEntries().size())); 307 | 308 | // ifd entity items 309 | foreach (const auto de, ifd.ifdEntries()) { 310 | fillIfdEntryItem(ifdItem, de); 311 | if (de.tag() == TiffIfdEntry::T_ImageWidth) 312 | width = de.values().value(0).toInt(); 313 | if (de.tag() == TiffIfdEntry::T_ImageLength) 314 | height = de.values().value(0).toInt(); 315 | } 316 | 317 | // sub ifd items 318 | foreach (const auto subIfd, ifd.subIfds()) 319 | fillSubIfdItem(ifdItem, subIfd); 320 | 321 | childItem = new QTreeWidgetItem(ifdItem); 322 | childItem->setText(0, tr("NextIFDOffset")); 323 | childItem->setText(1, QString::number(ifd.nextIfdOffset())); 324 | 325 | if (width != -1 && height != -1) 326 | ifdItem->setText(1, tr("Image(%1x%2)").arg(width).arg(height)); 327 | } 328 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #pragma once 26 | 27 | #include "tifffile.h" 28 | #include 29 | 30 | class QTreeWidgetItem; 31 | 32 | namespace Ui { 33 | class MainWindow; 34 | } 35 | 36 | class MainWindow : public QMainWindow 37 | { 38 | Q_OBJECT 39 | 40 | public: 41 | explicit MainWindow(QWidget *parent = nullptr); 42 | ~MainWindow(); 43 | 44 | protected: 45 | void closeEvent(QCloseEvent *evt); 46 | 47 | private: 48 | void onActionOpenTriggered(); 49 | void onActionOptionsTriggered(); 50 | void onActionAboutTriggered(); 51 | void onActionRecentFileTriggered(); 52 | 53 | void loadSettings(); 54 | void saveSettings(); 55 | void doOpenTiffFile(const QString &filePath); 56 | void updateActionRecentFiles(); 57 | 58 | void fillIfdEntryItem(QTreeWidgetItem *parentItem, const TiffIfdEntry &de); 59 | void fillSubIfdItem(QTreeWidgetItem *parentItem, const TiffIfd &ifd); 60 | 61 | Ui::MainWindow *ui; 62 | 63 | TiffParserOptions m_parserOptions; 64 | 65 | enum { MaxRecentFiles = 10 }; 66 | QAction *m_actionRecentFiles[MaxRecentFiles]; 67 | QAction *m_actionSeparator; 68 | QStringList m_recentFiles; 69 | }; 70 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 975 10 | 641 11 | 12 | 13 | 14 | QtTiffTagViewer 15 | 16 | 17 | 18 | 19 | 0 20 | 21 | 22 | 0 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 32 | 33 | true 34 | 35 | 36 | 250 37 | 38 | 39 | 40 | Name 41 | 42 | 43 | 44 | 45 | Value 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 0 56 | 0 57 | 975 58 | 23 59 | 60 | 61 | 62 | 63 | &File 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | &Help 72 | 73 | 74 | 75 | 76 | 77 | 78 | &Tools 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Debug 90 | 91 | 92 | 8 93 | 94 | 95 | 96 | 97 | 0 98 | 99 | 100 | 0 101 | 102 | 103 | 0 104 | 105 | 106 | 0 107 | 108 | 109 | 110 | 111 | QPlainTextEdit::NoWrap 112 | 113 | 114 | true 115 | 116 | 117 | 8000 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | &About... 127 | 128 | 129 | 130 | 131 | &Open... 132 | 133 | 134 | 135 | 136 | E&xit 137 | 138 | 139 | 140 | 141 | About Qt... 142 | 143 | 144 | 145 | 146 | Options... 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/optionsdialog.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #include "optionsdialog.h" 26 | #include "ui_optionsdialog.h" 27 | 28 | OptionsDialog::OptionsDialog(QWidget *parent) 29 | : QDialog(parent) 30 | , ui(new Ui::OptionsDialog) 31 | { 32 | ui->setupUi(this); 33 | } 34 | 35 | OptionsDialog::~OptionsDialog() 36 | { 37 | delete ui; 38 | } 39 | 40 | TiffParserOptions OptionsDialog::parserOptions() const 41 | { 42 | TiffParserOptions options; 43 | options.parserSubIfds = ui->parser_subIfds_button->isChecked(); 44 | return options; 45 | } 46 | 47 | void OptionsDialog::setParserOptions(const TiffParserOptions &options) 48 | { 49 | ui->parser_subIfds_button->setChecked(options.parserSubIfds); 50 | } 51 | -------------------------------------------------------------------------------- /src/optionsdialog.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #ifndef OPTIONSDIALOG_H 26 | #define OPTIONSDIALOG_H 27 | 28 | #include "tifffile.h" 29 | #include 30 | 31 | namespace Ui { 32 | class OptionsDialog; 33 | } 34 | 35 | class OptionsDialog : public QDialog 36 | { 37 | Q_OBJECT 38 | 39 | public: 40 | explicit OptionsDialog(QWidget *parent = nullptr); 41 | ~OptionsDialog(); 42 | 43 | TiffParserOptions parserOptions() const; 44 | void setParserOptions(const TiffParserOptions &options); 45 | 46 | private: 47 | Ui::OptionsDialog *ui; 48 | }; 49 | 50 | #endif // OPTIONSDIALOG_H 51 | -------------------------------------------------------------------------------- /src/optionsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | OptionsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 450 10 | 276 11 | 12 | 13 | 14 | Options 15 | 16 | 17 | 18 | 19 | 20 | Parser 21 | 22 | 23 | 24 | 25 | 26 | Parser SubIFDS 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Qt::Vertical 37 | 38 | 39 | 40 | 20 41 | 172 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Qt::Horizontal 50 | 51 | 52 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | buttonBox 62 | accepted() 63 | OptionsDialog 64 | accept() 65 | 66 | 67 | 248 68 | 254 69 | 70 | 71 | 157 72 | 274 73 | 74 | 75 | 76 | 77 | buttonBox 78 | rejected() 79 | OptionsDialog 80 | reject() 81 | 82 | 83 | 316 84 | 260 85 | 86 | 87 | 286 88 | 274 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/tifffile.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #include "tifffile.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | Q_LOGGING_CATEGORY(tiffLog, "dbzhang800.tiffFile") 33 | 34 | const static char *g_dataTypeName[] = { nullptr, "BYTE", "ASCII", "SHORT", "LONG", 35 | "RATIONAL", "SBYTE", "UNDEFINED", "SSHORT", "SLONG", 36 | "SRATIONAL", "FLOAT", "DOUBLE", "IFD", "LONG8", 37 | "SLONG8", "IFD8" }; 38 | 39 | const static QMap g_tagNames = { 40 | { 254, "SUBFILETYPE" }, 41 | { 255, "OSUBFILETYPE" }, 42 | { 256, "IMAGEWIDTH" }, 43 | { 257, "IMAGELENGTH" }, 44 | { 258, "BITSPERSAMPLE" }, 45 | { 259, "COMPRESSION" }, 46 | { 262, "PHOTOMETRIC" }, 47 | { 263, "THRESHHOLDING" }, 48 | { 264, "CELLWIDTH" }, 49 | { 265, "CELLLENGTH" }, 50 | { 266, "FILLORDER" }, 51 | { 269, "DOCUMENTNAME" }, 52 | { 270, "IMAGEDESCRIPTION" }, 53 | { 271, "MAKE" }, 54 | { 272, "MODEL" }, 55 | { 273, "STRIPOFFSETS" }, 56 | { 274, "ORIENTATION" }, 57 | { 277, "SAMPLESPERPIXEL" }, 58 | { 278, "ROWSPERSTRIP" }, 59 | { 279, "STRIPBYTECOUNTS" }, 60 | { 280, "MINSAMPLEVALUE" }, 61 | { 281, "MAXSAMPLEVALUE" }, 62 | { 282, "XRESOLUTION" }, 63 | { 283, "YRESOLUTION" }, 64 | { 284, "PLANARCONFIG" }, 65 | { 285, "PAGENAME" }, 66 | { 286, "XPOSITION" }, 67 | { 287, "YPOSITION" }, 68 | { 288, "FREEOFFSETS" }, 69 | { 289, "FREEBYTECOUNTS" }, 70 | { 290, "GRAYRESPONSEUNIT" }, 71 | { 291, "GRAYRESPONSECURVE" }, 72 | { 292, "GROUP3OPTIONS" }, 73 | { 292, "T4OPTIONS" }, 74 | { 293, "GROUP4OPTIONS" }, 75 | { 293, "T6OPTIONS" }, 76 | { 296, "RESOLUTIONUNIT" }, 77 | { 297, "PAGENUMBER" }, 78 | { 300, "COLORRESPONSEUNIT" }, 79 | { 301, "TRANSFERFUNCTION" }, 80 | { 305, "SOFTWARE" }, 81 | { 306, "DATETIME" }, 82 | { 315, "ARTIST" }, 83 | { 316, "HOSTCOMPUTER" }, 84 | { 317, "PREDICTOR" }, 85 | { 318, "WHITEPOINT" }, 86 | { 319, "PRIMARYCHROMATICITIES" }, 87 | { 320, "COLORMAP" }, 88 | { 321, "HALFTONEHINTS" }, 89 | { 322, "TILEWIDTH" }, 90 | { 323, "TILELENGTH" }, 91 | { 324, "TILEOFFSETS" }, 92 | { 325, "TILEBYTECOUNTS" }, 93 | { 326, "BADFAXLINES" }, 94 | { 327, "CLEANFAXDATA" }, 95 | { 328, "CONSECUTIVEBADFAXLINES" }, 96 | { 330, "SUBIFD" }, 97 | { 332, "INKSET" }, 98 | { 333, "INKNAMES" }, 99 | { 334, "NUMBEROFINKS" }, 100 | { 336, "DOTRANGE" }, 101 | { 337, "TARGETPRINTER" }, 102 | { 338, "EXTRASAMPLES" }, 103 | { 339, "SAMPLEFORMAT" }, 104 | { 340, "SMINSAMPLEVALUE" }, 105 | { 341, "SMAXSAMPLEVALUE" }, 106 | { 343, "CLIPPATH" }, 107 | { 344, "XCLIPPATHUNITS" }, 108 | { 345, "YCLIPPATHUNITS" }, 109 | { 346, "INDEXED" }, 110 | { 347, "JPEGTABLES" }, 111 | { 351, "OPIPROXY" }, 112 | { 400, "GLOBALPARAMETERSIFD" }, 113 | { 401, "PROFILETYPE" }, 114 | { 402, "FAXPROFILE" }, 115 | { 403, "CODINGMETHODS" }, 116 | { 404, "VERSIONYEAR" }, 117 | { 405, "MODENUMBER" }, 118 | { 433, "DECODE" }, 119 | { 434, "IMAGEBASECOLOR" }, 120 | { 435, "T82OPTIONS" }, 121 | { 512, "JPEGPROC" }, 122 | { 513, "JPEGIFOFFSET" }, 123 | { 514, "JPEGIFBYTECOUNT" }, 124 | { 515, "JPEGRESTARTINTERVAL" }, 125 | { 517, "JPEGLOSSLESSPREDICTORS" }, 126 | { 518, "JPEGPOINTTRANSFORM" }, 127 | { 519, "JPEGQTABLES" }, 128 | { 520, "JPEGDCTABLES" }, 129 | { 521, "JPEGACTABLES" }, 130 | { 529, "YCBCRCOEFFICIENTS" }, 131 | { 530, "YCBCRSUBSAMPLING" }, 132 | { 531, "YCBCRPOSITIONING" }, 133 | { 532, "REFERENCEBLACKWHITE" }, 134 | { 559, "STRIPROWCOUNTS" }, 135 | { 700, "XMLPACKET" }, 136 | { 32781, "OPIIMAGEID" }, 137 | { 32932, "TIFFANNOTATIONDATA" }, 138 | { 32953, "REFPTS" }, 139 | { 32954, "REGIONTACKPOINT" }, 140 | { 32955, "REGIONWARPCORNERS" }, 141 | { 32956, "REGIONAFFINE" }, 142 | { 32995, "MATTEING" }, 143 | { 32996, "DATATYPE" }, 144 | { 32997, "IMAGEDEPTH" }, 145 | { 32998, "TILEDEPTH" }, 146 | { 33300, "PIXAR_IMAGEFULLWIDTH" }, 147 | { 33301, "PIXAR_IMAGEFULLLENGTH" }, 148 | { 33302, "PIXAR_TEXTUREFORMAT" }, 149 | { 33303, "PIXAR_WRAPMODES" }, 150 | { 33304, "PIXAR_FOVCOT" }, 151 | { 33305, "PIXAR_MATRIX_WORLDTOSCREEN" }, 152 | { 33306, "PIXAR_MATRIX_WORLDTOCAMERA" }, 153 | { 33405, "WRITERSERIALNUMBER" }, 154 | { 33421, "CFAREPEATPATTERNDIM" }, 155 | { 33422, "CFAPATTERN" }, 156 | { 33432, "COPYRIGHT" }, 157 | { 33445, "MD_FILETAG" }, 158 | { 33446, "MD_SCALEPIXEL" }, 159 | { 33447, "MD_COLORTABLE" }, 160 | { 33448, "MD_LABNAME" }, 161 | { 33449, "MD_SAMPLEINFO" }, 162 | { 33450, "MD_PREPDATE" }, 163 | { 33451, "MD_PREPTIME" }, 164 | { 33452, "MD_FILEUNITS" }, 165 | { 33723, "RICHTIFFIPTC" }, 166 | { 33918, "INGR_PACKET_DATA_TAG" }, 167 | { 33919, "INGR_FLAG_REGISTERS" }, 168 | { 33920, "IRASB_TRANSORMATION_MATRIX" }, 169 | { 33922, "MODELTIEPOINTTAG" }, 170 | { 34016, "IT8SITE" }, 171 | { 34017, "IT8COLORSEQUENCE" }, 172 | { 34018, "IT8HEADER" }, 173 | { 34019, "IT8RASTERPADDING" }, 174 | { 34020, "IT8BITSPERRUNLENGTH" }, 175 | { 34021, "IT8BITSPEREXTENDEDR" }, 176 | { 34022, "IT8COLORTABLE" }, 177 | { 34023, "IT8IMAGECOLORINDICATOR" }, 178 | { 34024, "IT8BKGCOLORINDICATOR" }, 179 | { 34025, "IT8IMAGECOLORVALUE" }, 180 | { 34026, "IT8BKGCOLORVALUE" }, 181 | { 34027, "IT8PIXELINTENSITYRANGE" }, 182 | { 34028, "IT8TRANSPARENCYINDICATOR" }, 183 | { 34029, "IT8COLORCHARACTERIZATION" }, 184 | { 34030, "IT8HCUSAGE" }, 185 | { 34031, "IT8TRAPINDICATOR" }, 186 | { 34032, "IT8CMYKEQUIVALENT" }, 187 | { 34232, "FRAMECOUNT" }, 188 | { 34264, "MODELTRANSFORMATIONTAG" }, 189 | { 34377, "PHOTOSHOP" }, 190 | { 34665, "EXIFIFD" }, 191 | { 34675, "ICCPROFILE" }, 192 | { 34732, "IMAGELAYER" }, 193 | { 34750, "JBIGOPTIONS" }, 194 | { 34853, "GPSIFD" }, 195 | { 34908, "FAXRECVPARAMS" }, 196 | { 34909, "FAXSUBADDRESS" }, 197 | { 34910, "FAXRECVTIME" }, 198 | { 34911, "FAXDCS" }, 199 | { 37439, "STONITS" }, 200 | { 34929, "FEDEX_EDR" }, 201 | { 37724, "IMAGESOURCEDATA" }, 202 | { 40965, "INTEROPERABILITYIFD" }, 203 | { 42112, "GDAL_METADATA" }, 204 | { 42113, "GDAL_NODATA" }, 205 | { 50215, "OCE_SCANJOB_DESCRIPTION" }, 206 | { 50216, "OCE_APPLICATION_SELECTOR" }, 207 | { 50217, "OCE_IDENTIFICATION_NUMBER" }, 208 | { 50218, "OCE_IMAGELOGIC_CHARACTERISTICS" }, 209 | { 50674, "LERC_PARAMETERS" }, 210 | { 50706, "DNGVERSION" }, 211 | { 50707, "DNGBACKWARDVERSION" }, 212 | { 50708, "UNIQUECAMERAMODEL" }, 213 | { 50709, "LOCALIZEDCAMERAMODEL" }, 214 | { 50710, "CFAPLANECOLOR" }, 215 | { 50711, "CFALAYOUT" }, 216 | { 50712, "LINEARIZATIONTABLE" }, 217 | { 50713, "BLACKLEVELREPEATDIM" }, 218 | { 50714, "BLACKLEVEL" }, 219 | { 50715, "BLACKLEVELDELTAH" }, 220 | { 50716, "BLACKLEVELDELTAV" }, 221 | { 50717, "WHITELEVEL" }, 222 | { 50718, "DEFAULTSCALE" }, 223 | { 50719, "DEFAULTCROPORIGIN" }, 224 | { 50720, "DEFAULTCROPSIZE" }, 225 | { 50721, "COLORMATRIX1" }, 226 | { 50722, "COLORMATRIX2" }, 227 | { 50723, "CAMERACALIBRATION1" }, 228 | { 50724, "CAMERACALIBRATION2" }, 229 | { 50725, "REDUCTIONMATRIX1" }, 230 | { 50726, "REDUCTIONMATRIX2" }, 231 | { 50727, "ANALOGBALANCE" }, 232 | { 50728, "ASSHOTNEUTRAL" }, 233 | { 50729, "ASSHOTWHITEXY" }, 234 | { 50730, "BASELINEEXPOSURE" }, 235 | { 50731, "BASELINENOISE" }, 236 | { 50732, "BASELINESHARPNESS" }, 237 | { 50733, "BAYERGREENSPLIT" }, 238 | { 50734, "LINEARRESPONSELIMIT" }, 239 | { 50735, "CAMERASERIALNUMBER" }, 240 | { 50736, "LENSINFO" }, 241 | { 50737, "CHROMABLURRADIUS" }, 242 | { 50738, "ANTIALIASSTRENGTH" }, 243 | { 50739, "SHADOWSCALE" }, 244 | { 50740, "DNGPRIVATEDATA" }, 245 | { 50741, "MAKERNOTESAFETY" }, 246 | { 50778, "CALIBRATIONILLUMINANT1" }, 247 | { 50779, "CALIBRATIONILLUMINANT2" }, 248 | { 50780, "BESTQUALITYSCALE" }, 249 | { 50781, "RAWDATAUNIQUEID" }, 250 | { 50827, "ORIGINALRAWFILENAME" }, 251 | { 50828, "ORIGINALRAWFILEDATA" }, 252 | { 50829, "ACTIVEAREA" }, 253 | { 50830, "MASKEDAREAS" }, 254 | { 50831, "ASSHOTICCPROFILE" }, 255 | { 50832, "ASSHOTPREPROFILEMATRIX" }, 256 | { 50833, "CURRENTICCPROFILE" }, 257 | { 50834, "CURRENTPREPROFILEMATRIX" }, 258 | { 50844, "RPCCOEFFICIENT" }, 259 | { 50784, "ALIAS_LAYER_METADATA" }, 260 | { 50908, "TIFF_RSID" }, 261 | { 50909, "GEO_METADATA" }, 262 | { 50933, "EXTRACAMERAPROFILES" }, 263 | { 65535, "DCSHUESHIFTVALUES" }, 264 | 265 | // TAGS missing from libtiff 266 | // GeoTIFF 267 | { 33550, "MODELPIXELSCALETAG" }, 268 | { 34735, "GEOKEYDIRECTORYTAG" }, 269 | { 34736, "GEODOUBLEPARAMSTAG" }, 270 | { 34737, "GEOASCIIPARAMSTAG" }, 271 | }; 272 | 273 | const static QMap g_compressionNames = { 274 | { 1, "NONE" }, { 2, "CCITTRLE" }, { 3, "CCITTFAX3" }, 275 | { 3, "CCITT_T4" }, { 4, "CCITTFAX4" }, { 4, "CCITT_T6" }, 276 | { 5, "LZW" }, { 6, "OJPEG" }, { 7, "JPEG" }, 277 | { 9, "T85" }, { 10, "T43" }, { 32766, "NEXT" }, 278 | { 32771, "CCITTRLEW" }, { 32773, "PACKBITS" }, { 32809, "THUNDERSCAN" }, 279 | { 32895, "IT8CTPAD" }, { 32896, "IT8LW" }, { 32897, "IT8MP" }, 280 | { 32898, "IT8BL" }, { 32908, "PIXARFILM" }, { 32909, "PIXARLOG" }, 281 | { 32946, "DEFLATE" }, { 8, "ADOBE_DEFLATE" }, { 32947, "DCS" }, 282 | { 34661, "JBIG" }, { 34676, "SGILOG" }, { 34677, "SGILOG24" }, 283 | { 34712, "JP2000" }, { 34887, "LERC" }, { 34925, "LZMA" }, 284 | { 50000, "ZSTD" }, { 50001, "WEBP" }, { 50002, "JXL" } 285 | }; 286 | 287 | template 288 | static inline T getValueFromBytes(const char *bytes, TiffFile::ByteOrder byteOrder) 289 | { 290 | if (byteOrder == TiffFile::LittleEndian) 291 | return qFromLittleEndian(reinterpret_cast(bytes)); 292 | return qFromBigEndian(reinterpret_cast(bytes)); 293 | } 294 | 295 | template 296 | static inline T fixValueByteOrder(T value, TiffFile::ByteOrder byteOrder) 297 | { 298 | if (byteOrder == TiffFile::LittleEndian) 299 | return qFromLittleEndian(value); 300 | return qFromBigEndian(value); 301 | } 302 | 303 | class TiffIfdEntryPrivate : public QSharedData 304 | { 305 | public: 306 | TiffIfdEntryPrivate() {} 307 | TiffIfdEntryPrivate(const TiffIfdEntryPrivate &other) 308 | : QSharedData(other) 309 | , tag(other.tag) 310 | , type(other.type) 311 | , count(other.count) 312 | , valueOrOffset(other.valueOrOffset) 313 | { 314 | } 315 | ~TiffIfdEntryPrivate() {} 316 | 317 | int typeSize() 318 | { 319 | switch (type) { 320 | case TiffIfdEntry::DT_Byte: 321 | case TiffIfdEntry::DT_SByte: 322 | case TiffIfdEntry::DT_Ascii: 323 | case TiffIfdEntry::DT_Undefined: 324 | return 1; 325 | case TiffIfdEntry::DT_Short: 326 | case TiffIfdEntry::DT_SShort: 327 | return 2; 328 | case TiffIfdEntry::DT_Long: 329 | case TiffIfdEntry::DT_SLong: 330 | case TiffIfdEntry::DT_Ifd: 331 | case TiffIfdEntry::DT_Float: 332 | return 4; 333 | 334 | case TiffIfdEntry::DT_Rational: 335 | case TiffIfdEntry::DT_SRational: 336 | case TiffIfdEntry::DT_Long8: 337 | case TiffIfdEntry::DT_SLong8: 338 | case TiffIfdEntry::DT_Ifd8: 339 | case TiffIfdEntry::DT_Double: 340 | return 8; 341 | default: 342 | return 0; 343 | } 344 | } 345 | 346 | void parserValues(const char *bytes, TiffFile::ByteOrder byteOrder); 347 | 348 | quint16 tag; 349 | quint16 type; 350 | quint64 count{ 0 }; 351 | QByteArray valueOrOffset; // 12 bytes for tiff or 20 bytes for bigTiff 352 | QVariantList values; 353 | }; 354 | 355 | void TiffIfdEntryPrivate::parserValues(const char *bytes, TiffFile::ByteOrder byteOrder) 356 | { 357 | if (type == TiffIfdEntry::DT_Ascii) { 358 | int start = 0; 359 | for (int i = 0; i < count; ++i) { 360 | if (bytes[i] == '\0') { 361 | values.append(QString::fromLatin1(bytes + start, i - start + 1)); 362 | start = i + 1; 363 | } 364 | } 365 | if (bytes[count - 1] != '\0') { 366 | qCDebug(tiffLog) << "ASCII value donesn't end with NUL"; 367 | values.append(QString::fromLatin1(bytes + start, count - start)); 368 | } 369 | return; 370 | } 371 | 372 | if (type == TiffIfdEntry::DT_Undefined) { 373 | values.append(QByteArray(bytes, count)); 374 | return; 375 | } 376 | 377 | // To make things simple, save normal integer as qint32 or quint32 here. 378 | for (int i = 0; i < count; ++i) { 379 | switch (type) { 380 | case TiffIfdEntry::DT_Byte: 381 | values.append(static_cast(bytes[i])); 382 | break; 383 | case TiffIfdEntry::DT_SByte: 384 | values.append(static_cast(bytes[i])); 385 | break; 386 | case TiffIfdEntry::DT_Short: 387 | values.append( 388 | static_cast(getValueFromBytes(bytes + i * 2, byteOrder))); 389 | break; 390 | case TiffIfdEntry::DT_SShort: 391 | values.append(static_cast(getValueFromBytes(bytes + i * 2, byteOrder))); 392 | break; 393 | case TiffIfdEntry::DT_Long: 394 | case TiffIfdEntry::DT_Ifd: 395 | values.append(getValueFromBytes(bytes + i * 4, byteOrder)); 396 | break; 397 | case TiffIfdEntry::DT_SLong: 398 | values.append(getValueFromBytes(bytes + i * 4, byteOrder)); 399 | break; 400 | case TiffIfdEntry::DT_Float: 401 | values.append(*(reinterpret_cast(bytes + i * 4))); 402 | break; 403 | case TiffIfdEntry::DT_Double: 404 | values.append(*(reinterpret_cast(bytes + i * 8))); 405 | break; 406 | case TiffIfdEntry::DT_Rational: 407 | values.append(getValueFromBytes(bytes + i * 4, byteOrder)); 408 | values.append(getValueFromBytes(bytes + i * 4 + 4, byteOrder)); 409 | break; 410 | case TiffIfdEntry::DT_SRational: 411 | values.append(getValueFromBytes(bytes + i * 4, byteOrder)); 412 | values.append(getValueFromBytes(bytes + i * 4 + 4, byteOrder)); 413 | break; 414 | case TiffIfdEntry::DT_Long8: 415 | case TiffIfdEntry::DT_Ifd8: 416 | values.append(getValueFromBytes(bytes + i * 8, byteOrder)); 417 | break; 418 | case TiffIfdEntry::DT_SLong8: 419 | values.append(getValueFromBytes(bytes + i * 8, byteOrder)); 420 | break; 421 | default: 422 | break; 423 | } 424 | } 425 | } 426 | 427 | /*! 428 | * \class TiffIfdEntry 429 | */ 430 | 431 | TiffIfdEntry::TiffIfdEntry() 432 | : d(new TiffIfdEntryPrivate) 433 | { 434 | } 435 | 436 | TiffIfdEntry::TiffIfdEntry(const TiffIfdEntry &other) 437 | : d(other.d) 438 | { 439 | } 440 | 441 | TiffIfdEntry::~TiffIfdEntry() 442 | { 443 | } 444 | 445 | quint16 TiffIfdEntry::tag() const 446 | { 447 | return d->tag; 448 | } 449 | 450 | QString TiffIfdEntry::tagName() const 451 | { 452 | if (g_tagNames.contains(d->tag)) 453 | return QString::fromLatin1(g_tagNames[d->tag]); 454 | 455 | return QStringLiteral("UNKNOWNTAG(%1)").arg(d->tag); 456 | } 457 | 458 | quint16 TiffIfdEntry::type() const 459 | { 460 | return d->type; 461 | } 462 | 463 | QString TiffIfdEntry::typeName() const 464 | { 465 | if (d->type > 0 && d->type < TiffIfdEntry::DT_Ifd8) 466 | return g_dataTypeName[d->type]; 467 | 468 | return QString(); 469 | } 470 | 471 | quint64 TiffIfdEntry::count() const 472 | { 473 | return d->count; 474 | } 475 | 476 | QByteArray TiffIfdEntry::valueOrOffset() const 477 | { 478 | return d->valueOrOffset; 479 | } 480 | 481 | QVariantList TiffIfdEntry::values() const 482 | { 483 | return d->values; 484 | } 485 | 486 | QString TiffIfdEntry::valueDescription() const 487 | { 488 | if (d->tag == T_Compression && d->values.size() == 1) { 489 | const int v = d->values[0].toInt(); 490 | 491 | if (g_compressionNames.contains(v)) 492 | return QString::fromLatin1(g_compressionNames[v]); 493 | } 494 | return QString(); 495 | } 496 | 497 | bool TiffIfdEntry::isValid() const 498 | { 499 | return d->count; 500 | } 501 | 502 | class TiffIfdPrivate : public QSharedData 503 | { 504 | public: 505 | TiffIfdPrivate() {} 506 | TiffIfdPrivate(const TiffIfdPrivate &other) 507 | : QSharedData(other) 508 | , ifdEntries(other.ifdEntries) 509 | , subIfds(other.subIfds) 510 | , nextIfdOffset(other.nextIfdOffset) 511 | { 512 | } 513 | ~TiffIfdPrivate() {} 514 | 515 | bool hasIfdEntry(quint16 tag); 516 | TiffIfdEntry ifdEntry(quint16 tag); 517 | 518 | QVector ifdEntries; 519 | QVector subIfds; 520 | qint64 nextIfdOffset{ 0 }; 521 | }; 522 | 523 | bool TiffIfdPrivate::hasIfdEntry(quint16 tag) 524 | { 525 | return ifdEntry(tag).isValid(); 526 | } 527 | 528 | TiffIfdEntry TiffIfdPrivate::ifdEntry(quint16 tag) 529 | { 530 | auto it = std::find_if(ifdEntries.cbegin(), ifdEntries.cend(), 531 | [tag](const TiffIfdEntry &de) { return tag == de.tag(); }); 532 | if (it == ifdEntries.cend()) 533 | return TiffIfdEntry(); 534 | return *it; 535 | } 536 | 537 | /*! 538 | * \class TiffIfd 539 | */ 540 | 541 | TiffIfd::TiffIfd() 542 | : d(new TiffIfdPrivate) 543 | { 544 | } 545 | 546 | TiffIfd::TiffIfd(const TiffIfd &other) 547 | : d(other.d) 548 | { 549 | } 550 | 551 | TiffIfd::~TiffIfd() 552 | { 553 | } 554 | 555 | QVector TiffIfd::ifdEntries() const 556 | { 557 | return d->ifdEntries; 558 | } 559 | 560 | QVector TiffIfd::subIfds() const 561 | { 562 | return d->subIfds; 563 | } 564 | 565 | qint64 TiffIfd::nextIfdOffset() const 566 | { 567 | return d->nextIfdOffset; 568 | } 569 | 570 | bool TiffIfd::isValid() const 571 | { 572 | return !d->ifdEntries.isEmpty(); 573 | } 574 | 575 | class TiffFilePrivate 576 | { 577 | public: 578 | TiffFilePrivate(); 579 | void setError(const QString &errorString); 580 | bool readHeader(); 581 | bool readIfd(qint64 offset, TiffIfd *parentIfd = nullptr); 582 | 583 | template 584 | T getValueFromFile() 585 | { 586 | T v{ 0 }; 587 | auto bytesRead = file.read(reinterpret_cast(&v), sizeof(T)); 588 | if (bytesRead != sizeof(T)) 589 | qCDebug(tiffLog) << "file read error."; 590 | return fixValueByteOrder(v, header.byteOrder); 591 | } 592 | 593 | struct Header 594 | { 595 | QByteArray rawBytes; 596 | TiffFile::ByteOrder byteOrder{ TiffFile::LittleEndian }; 597 | quint16 version{ 42 }; 598 | qint64 ifd0Offset{ 0 }; 599 | 600 | bool isBigTiff() const { return version == 43; } 601 | } header; 602 | 603 | QVector ifds; 604 | 605 | QFile file; 606 | QString errorString; 607 | bool hasError{ false }; 608 | 609 | TiffParserOptions parserOptions; 610 | }; 611 | 612 | TiffFilePrivate::TiffFilePrivate() 613 | { 614 | } 615 | 616 | void TiffFilePrivate::setError(const QString &errorString) 617 | { 618 | hasError = true; 619 | this->errorString = errorString; 620 | } 621 | 622 | bool TiffFilePrivate::readHeader() 623 | { 624 | auto headerBytes = file.peek(8); 625 | if (headerBytes.size() != 8) { 626 | setError(QStringLiteral("Invalid tiff file")); 627 | return false; 628 | } 629 | 630 | // magic bytes 631 | auto magicBytes = headerBytes.left(2); 632 | if (magicBytes == QByteArray("II")) { 633 | header.byteOrder = TiffFile::LittleEndian; 634 | } else if (magicBytes == QByteArray("MM")) { 635 | header.byteOrder = TiffFile::BigEndian; 636 | } else { 637 | setError(QStringLiteral("Invalid tiff file")); 638 | return false; 639 | } 640 | 641 | // version 642 | header.version = getValueFromBytes(headerBytes.data() + 2, header.byteOrder); 643 | if (!(header.version == 42 || header.version == 43)) { 644 | setError(QStringLiteral("Invalid tiff file: Unknown version")); 645 | return false; 646 | } 647 | header.rawBytes = file.read(header.isBigTiff() ? 16 : 8); 648 | 649 | // ifd0Offset 650 | if (!header.isBigTiff()) 651 | header.ifd0Offset = 652 | getValueFromBytes(header.rawBytes.data() + 4, header.byteOrder); 653 | else 654 | header.ifd0Offset = getValueFromBytes(header.rawBytes.data() + 8, header.byteOrder); 655 | 656 | return true; 657 | } 658 | 659 | bool TiffFilePrivate::readIfd(qint64 offset, TiffIfd *parentIfd) 660 | { 661 | if (!file.seek(offset)) { 662 | setError(file.errorString()); 663 | return false; 664 | } 665 | 666 | TiffIfd ifd; 667 | 668 | if (!header.isBigTiff()) { 669 | quint16 deCount = getValueFromFile(); 670 | for (int i = 0; i < deCount; ++i) { 671 | TiffIfdEntry ifdEntry; 672 | auto &dePrivate = ifdEntry.d; 673 | dePrivate->tag = getValueFromFile(); 674 | dePrivate->type = getValueFromFile(); 675 | dePrivate->count = getValueFromFile(); 676 | dePrivate->valueOrOffset = file.read(4); 677 | 678 | ifd.d->ifdEntries.append(ifdEntry); 679 | } 680 | ifd.d->nextIfdOffset = getValueFromFile(); 681 | } else { 682 | quint64 deCount = getValueFromFile(); 683 | for (quint64 i = 0; i < deCount; ++i) { 684 | TiffIfdEntry ifdEntry; 685 | auto &dePrivate = ifdEntry.d; 686 | dePrivate->tag = getValueFromFile(); 687 | dePrivate->type = getValueFromFile(); 688 | dePrivate->count = getValueFromFile(); 689 | dePrivate->valueOrOffset = file.read(8); 690 | 691 | ifd.d->ifdEntries.append(ifdEntry); 692 | } 693 | ifd.d->nextIfdOffset = getValueFromFile(); 694 | } 695 | 696 | // parser data of ifdEntry 697 | foreach (auto de, ifd.ifdEntries()) { 698 | auto &dePrivate = de.d; 699 | 700 | auto valueBytesCount = dePrivate->count * dePrivate->typeSize(); 701 | // skip unknown datatype 702 | if (valueBytesCount == 0) 703 | continue; 704 | QByteArray valueBytes; 705 | if (!header.isBigTiff() && valueBytesCount > 4) { 706 | auto valueOffset = getValueFromBytes(de.valueOrOffset(), header.byteOrder); 707 | if (!file.seek(valueOffset)) 708 | qCDebug(tiffLog) << "Fail to seek pos: " << valueOffset; 709 | valueBytes = file.read(valueBytesCount); 710 | } else if (header.isBigTiff() && valueBytesCount > 8) { 711 | auto valueOffset = getValueFromBytes(de.valueOrOffset(), header.byteOrder); 712 | if (!file.seek(valueOffset)) 713 | qCDebug(tiffLog) << "Fail to seek pos: " << valueOffset; 714 | valueBytes = file.read(valueBytesCount); 715 | } else { 716 | valueBytes = dePrivate->valueOrOffset; 717 | } 718 | dePrivate->parserValues(valueBytes, header.byteOrder); 719 | } 720 | 721 | if (!parentIfd) // IFD0 722 | ifds.append(ifd); 723 | else // subIfd 724 | parentIfd->d->subIfds.append(ifd); 725 | 726 | if (parserOptions.parserSubIfds) { 727 | // Note: 728 | // SUBIFDs in Tiff with pyramid generated by Adobe Photoshop CS6(Windows) can not be 729 | // parsered here. Nevertheless, Tiff generated by Adobe Photoshop CC 2018 is OK. 730 | TiffIfdEntry deSubIfd = ifd.d->ifdEntry(TiffIfdEntry::T_SubIfd); 731 | foreach (auto subIfdOffset, deSubIfd.values()) { 732 | // read sub ifds 733 | readIfd(subIfdOffset.toULongLong(), &ifd); 734 | } 735 | } 736 | 737 | if (ifd.nextIfdOffset() != 0) { 738 | // Read next ifd in the chain 739 | readIfd(ifd.nextIfdOffset(), parentIfd); 740 | } 741 | 742 | return true; 743 | } 744 | 745 | /*! 746 | * \class TiffFile 747 | */ 748 | 749 | /*! 750 | * Constructs the TiffFile object. 751 | */ 752 | TiffFile::TiffFile(const QString &filePath, const TiffParserOptions &options) 753 | : d(new TiffFilePrivate) 754 | { 755 | d->file.setFileName(filePath); 756 | d->parserOptions = options; 757 | if (!d->file.open(QFile::ReadOnly)) { 758 | d->hasError = true; 759 | d->errorString = d->file.errorString(); 760 | } 761 | 762 | if (!d->readHeader()) 763 | return; 764 | 765 | d->readIfd(d->header.ifd0Offset); 766 | } 767 | 768 | TiffFile::~TiffFile() 769 | { 770 | } 771 | 772 | QByteArray TiffFile::headerBytes() const 773 | { 774 | return d->header.rawBytes; 775 | } 776 | 777 | bool TiffFile::isBigTiff() const 778 | { 779 | return d->header.isBigTiff(); 780 | } 781 | 782 | TiffFile::ByteOrder TiffFile::byteOrder() const 783 | { 784 | return d->header.byteOrder; 785 | } 786 | 787 | int TiffFile::version() const 788 | { 789 | return d->header.version; 790 | } 791 | 792 | qint64 TiffFile::ifd0Offset() const 793 | { 794 | return d->header.ifd0Offset; 795 | } 796 | 797 | QVector TiffFile::ifds() const 798 | { 799 | return d->ifds; 800 | } 801 | 802 | QString TiffFile::errorString() const 803 | { 804 | return d->errorString; 805 | } 806 | 807 | bool TiffFile::hasError() const 808 | { 809 | return d->hasError; 810 | } 811 | -------------------------------------------------------------------------------- /src/tifffile.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** Copyright (c) 2018 Debao Zhang 3 | ** All right reserved. 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be 14 | ** included in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ** 24 | ****************************************************************************/ 25 | #pragma once 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | class QByteArray; 32 | class TiffIfdEntryPrivate; 33 | class TiffIfdPrivate; 34 | class TiffFilePrivate; 35 | 36 | struct TiffParserOptions 37 | { 38 | bool parserSubIfds{ true }; 39 | }; 40 | 41 | class TiffIfdEntry 42 | { 43 | public: 44 | enum Tag { 45 | T_SubFleType = 254, 46 | T_ImageWidth = 256, 47 | T_ImageLength = 257, 48 | T_Compression = 259, 49 | T_SubIfd = 330, 50 | T_Photoshop = 34377, 51 | }; 52 | 53 | enum DataType { 54 | DT_Byte = 1, 55 | DT_Ascii, 56 | DT_Short, 57 | DT_Long, 58 | DT_Rational, 59 | DT_SByte, 60 | DT_Undefined, 61 | DT_SShort, 62 | DT_SLong, 63 | DT_SRational, 64 | DT_Float, 65 | DT_Double, 66 | DT_Ifd, 67 | DT_Long8, 68 | DT_SLong8, 69 | DT_Ifd8 70 | }; 71 | 72 | TiffIfdEntry(); 73 | TiffIfdEntry(const TiffIfdEntry &other); 74 | ~TiffIfdEntry(); 75 | 76 | quint16 tag() const; 77 | QString tagName() const; 78 | quint16 type() const; 79 | QString typeName() const; 80 | quint64 count() const; 81 | QByteArray valueOrOffset() const; 82 | QVariantList values() const; 83 | QString valueDescription() const; 84 | bool isValid() const; 85 | 86 | private: 87 | friend class TiffFilePrivate; 88 | QExplicitlySharedDataPointer d; 89 | }; 90 | 91 | class TiffIfd 92 | { 93 | public: 94 | TiffIfd(); 95 | TiffIfd(const TiffIfd &other); 96 | ~TiffIfd(); 97 | 98 | QVector ifdEntries() const; 99 | QVector subIfds() const; 100 | qint64 nextIfdOffset() const; 101 | bool isValid() const; 102 | 103 | private: 104 | friend class TiffFilePrivate; 105 | QExplicitlySharedDataPointer d; 106 | }; 107 | 108 | class TiffFile 109 | { 110 | public: 111 | enum ByteOrder { LittleEndian, BigEndian }; 112 | 113 | TiffFile(const QString &filePath, const TiffParserOptions &options); 114 | ~TiffFile(); 115 | 116 | QString errorString() const; 117 | bool hasError() const; 118 | 119 | // header information 120 | QByteArray headerBytes() const; 121 | bool isBigTiff() const; 122 | ByteOrder byteOrder() const; 123 | int version() const; 124 | qint64 ifd0Offset() const; 125 | 126 | // ifds 127 | QVector ifds() const; 128 | 129 | private: 130 | QScopedPointer d; 131 | }; 132 | --------------------------------------------------------------------------------