mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
libkexiv2: import with moc adjustments
This commit is contained in:
parent
1b596d748c
commit
a896e5b45b
52 changed files with 29544 additions and 0 deletions
2
libkexiv2/.gitignore
vendored
Normal file
2
libkexiv2/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.kate-swp
|
||||
tests/databases/*/*.db
|
11
libkexiv2/AUTHORS
Normal file
11
libkexiv2/AUTHORS
Normal file
|
@ -0,0 +1,11 @@
|
|||
AUTHORS AND MAINTAINERS:
|
||||
|
||||
Caulier Gilles <caulier dot gilles at gmail dot com>
|
||||
Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
||||
|
||||
CONTRIBUTORS:
|
||||
|
||||
Angelo Naselli <anaselli at linux dot it>
|
||||
Gerhard Kulzer <gerhard at kulzer dot net>
|
||||
Achim Bohnet <ach at mpe dot mpg dot de>
|
||||
Guillaume Castagnino <guilc at fr dot st>
|
139
libkexiv2/CMakeLists.txt
Normal file
139
libkexiv2/CMakeLists.txt
Normal file
|
@ -0,0 +1,139 @@
|
|||
# ===========================================================
|
||||
#
|
||||
# This file is a part of digiKam project
|
||||
# <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
#
|
||||
# @date 2006-09-15
|
||||
# @brief Exiv2 library interface for KDE
|
||||
#
|
||||
# @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
# <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
#
|
||||
# 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, 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.
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
PROJECT(libkexiv2)
|
||||
|
||||
MESSAGE(STATUS "----------------------------------------------------------------------------------")
|
||||
MESSAGE(STATUS "Starting CMake configuration for: libkexiv2")
|
||||
|
||||
FIND_PACKAGE(KDE4 REQUIRED)
|
||||
|
||||
INCLUDE(KDE4Defaults)
|
||||
INCLUDE(MacroLibrary)
|
||||
INCLUDE(MacroOptionalAddSubdirectory)
|
||||
INCLUDE(MacroOptionalFindPackage)
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
ADD_DEFINITIONS (${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS} ${KDE4_DEFINITIONS})
|
||||
INCLUDE_DIRECTORIES (${QDBUS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})
|
||||
|
||||
SET(LIBKEXIV2_AREA_CODE_GENERAL 51003)
|
||||
ADD_DEFINITIONS(-DKDE_DEFAULT_DEBUG_AREA=${LIBKEXIV2_AREA_CODE_GENERAL})
|
||||
|
||||
SET(EXIV2_MIN_VERSION "0.21")
|
||||
FIND_PACKAGE(Exiv2)
|
||||
MACRO_LOG_FEATURE(EXIV2_FOUND "Exiv2" "Required to build libkexiv2." "http://www.exiv2.org"
|
||||
TRUE ${EXIV2_MIN_VERSION} "")
|
||||
|
||||
# =======================================================
|
||||
# Information to update before to release this library.
|
||||
|
||||
# Library version history:
|
||||
# API ABI
|
||||
# 0.1.0 => 0.1.0
|
||||
# 0.1.1 => 0.2.0
|
||||
# 0.1.2 => 1.0.1
|
||||
# 0.1.3 => 1.0.1
|
||||
# 0.1.4 => 2.0.2
|
||||
# 0.1.5 => 2.1.1
|
||||
# 0.1.6 => 3.0.0
|
||||
# 0.1.7 => 4.0.1
|
||||
# 0.1.8 => 5.0.0
|
||||
# 0.2.0 => 6.0.0 (released with KDE 4.1.0)
|
||||
# 0.3.0 => 7.0.0 (released with KDE 4.1.2)
|
||||
# 0.4.0 => 7.1.0
|
||||
# 0.5.0 => 7.2.0 (Released with KDE 4.2.0)
|
||||
# 0.6.0 => 7.3.0 (Released with KDE 4.3.0)
|
||||
# 1.0.0 => 8.0.0 (Released with KDE 4.4.0)
|
||||
# 1.1.0 => 8.1.0 (Released with KDE 4.5.0)
|
||||
# 1.2.0 => 9.0.0 (Released with KDE 4.6.0 - Including XMP sidecar support
|
||||
# 2.0.0 => 10.0.0 (Released with KDE 4.7.0)
|
||||
# 2.1.0 => 10.0.1 (Released with KDE 4.7.1) - Add AltLangStrEdit visible lines API
|
||||
# 2.1.1 => 10.0.2 (Released with KDE 4.7.4) - Add AltLangStrEdit::setCurrentLanguageCode()
|
||||
# 2.2.0 => 11.0.0 (Released with KDE 4.8.1) - Remove deprecated methods
|
||||
# 2.3.0 => 11.1.0 (Released with KDE 4.8.2) - Add new static methods about XMP sidecar file management.
|
||||
# 2.3.1 => 11.2.0 - Add new method to set specific XMP tag string
|
||||
|
||||
# Library API version
|
||||
SET(KEXIV2_LIB_MAJOR_VERSION "2")
|
||||
SET(KEXIV2_LIB_MINOR_VERSION "3")
|
||||
SET(KEXIV2_LIB_PATCH_VERSION "2")
|
||||
|
||||
# Suffix to add at end of version string. Usual values are:
|
||||
# "-git" : alpha code unstable from git. Do not use in production
|
||||
# "-beta1" : beta1 release.
|
||||
# "-beta2" : beta2 release.
|
||||
# "-beta3" : beta3 release.
|
||||
# "-rc" : release candidate.
|
||||
# "" : final relase. Can be used in production.
|
||||
SET(KEXIV2_LIB_SUFFIX_VERSION "")
|
||||
|
||||
# Library ABI version used by linker.
|
||||
# For details : http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
|
||||
SET(KEXIV2_LIB_SO_CUR_VERSION "11")
|
||||
SET(KEXIV2_LIB_SO_REV_VERSION "2")
|
||||
SET(KEXIV2_LIB_SO_AGE_VERSION "0")
|
||||
|
||||
# =======================================================
|
||||
# Set env. variables accordinly.
|
||||
|
||||
SET(KEXIV2_LIB_VERSION_STRING "${KEXIV2_LIB_MAJOR_VERSION}.${KEXIV2_LIB_MINOR_VERSION}.${KEXIV2_LIB_PATCH_VERSION}${KEXIV2_LIB_SUFFIX_VERSION}")
|
||||
SET(KEXIV2_LIB_VERSION_ID "0x0${KEXIV2_LIB_MAJOR_VERSION}0${KEXIV2_LIB_MINOR_VERSION}0${KEXIV2_LIB_PATCH_VERSION}")
|
||||
SET(KEXIV2_LIB_SO_VERSION_STRING "${KEXIV2_LIB_SO_CUR_VERSION}.${KEXIV2_LIB_SO_REV_VERSION}.${KEXIV2_LIB_SO_AGE_VERSION}")
|
||||
|
||||
# =======================================================
|
||||
|
||||
IF (EXIV2_FOUND)
|
||||
ADD_SUBDIRECTORY(libkexiv2)
|
||||
|
||||
IF(KDE4_BUILD_TESTS)
|
||||
MACRO_OPTIONAL_ADD_SUBDIRECTORY(tests)
|
||||
ENDIF(KDE4_BUILD_TESTS)
|
||||
|
||||
INCLUDE(CMakePackageConfigHelpers)
|
||||
CONFIGURE_PACKAGE_CONFIG_FILE(libkexiv2-config.cmake.in libkexiv2-config.cmake
|
||||
INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/libkexiv2-${KEXIV2_LIB_VERSION_STRING}"
|
||||
PATH_VARS INCLUDE_INSTALL_DIR
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
WRITE_BASIC_PACKAGE_VERSION_FILE(libkexiv2-config-version.cmake
|
||||
VERSION "${KEXIV2_LIB_MAJOR_VERSION}.${KEXIV2_LIB_MINOR_VERSION}.${KEXIV2_LIB_PATCH_VERSION}"
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
INSTALL(EXPORT Libkexiv2
|
||||
DESTINATION "${LIB_INSTALL_DIR}/cmake/libkexiv2-${KEXIV2_LIB_VERSION_STRING}"
|
||||
NAMESPACE Libkexiv2::
|
||||
FILE libkexiv2-export.cmake)
|
||||
INSTALL(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libkexiv2-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libkexiv2-config-version.cmake
|
||||
DESTINATION
|
||||
"${LIB_INSTALL_DIR}/cmake/libkexiv2-${KEXIV2_LIB_VERSION_STRING}"
|
||||
)
|
||||
IF(NOT WIN32)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libkexiv2.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkexiv2.pc)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libkexiv2.lsm.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkexiv2.lsm)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libkexiv2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
|
||||
ENDIF(NOT WIN32)
|
||||
ENDIF (EXIV2_FOUND)
|
||||
|
||||
MACRO_DISPLAY_FEATURE_LOG()
|
340
libkexiv2/COPYING
Normal file
340
libkexiv2/COPYING
Normal file
|
@ -0,0 +1,340 @@
|
|||
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 Library 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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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 Library General
|
||||
Public License instead of this License.
|
22
libkexiv2/COPYING-CMAKE-SCRIPTS
Normal file
22
libkexiv2/COPYING-CMAKE-SCRIPTS
Normal file
|
@ -0,0 +1,22 @@
|
|||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
481
libkexiv2/COPYING.LIB
Normal file
481
libkexiv2/COPYING.LIB
Normal file
|
@ -0,0 +1,481 @@
|
|||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, 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.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, 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 library, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, 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 companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. 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.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; 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.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
602
libkexiv2/ChangeLog
Normal file
602
libkexiv2/ChangeLog
Normal file
|
@ -0,0 +1,602 @@
|
|||
V 0.1.5 - 2007-05-08
|
||||
----------------------------------------------------------------------------
|
||||
Fixing: libtools version info again, we found not to be back compatible
|
||||
with libkexiv 0.1.1, unfortunately since 0.1.2 version.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
V 0.1.4 - 2007-05-06
|
||||
----------------------------------------------------------------------------
|
||||
Fixing: libtools version info
|
||||
|
||||
2007-05-06 21:23 anaselli
|
||||
|
||||
* [r661885] libkexiv2/ChangeLog, libkexiv2/Makefile.am,
|
||||
libkexiv2/libkexiv2.lsm, libkexiv2/libkexiv2.pc.in,
|
||||
libkexiv2/version.h:
|
||||
Release 0.1.4
|
||||
- fix libtools version-info
|
||||
|
||||
CCMAIL: kde-imaging@kde.org, digikam-devel@kde.org
|
||||
|
||||
2007-05-06 21:14 anaselli
|
||||
|
||||
* [r661883] libkexiv2/Makefile.am:
|
||||
Changed to add libtools version managing into prepare script
|
||||
(hope this will avoid to forget it)
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
v 0.1.3 - 2007-05-05
|
||||
----------------------------------------------------------------------------
|
||||
2007-05-05 14:01 gkulzer
|
||||
|
||||
* [r661413] libkexiv2/ChangeLog:
|
||||
0.1.3 release, coordinated with digiKam 0.9.2-beta1 release
|
||||
|
||||
2007-05-05 12:09 gkulzer
|
||||
|
||||
* [r661379] libkexiv2/ChangeLog, libkexiv2/NEWS,
|
||||
libkexiv2/libkexiv2.lsm:
|
||||
0.1.3 release, coordinated with digiKam 0.9.2-beta1 release
|
||||
|
||||
2007-05-02 07:11 cgilles
|
||||
|
||||
* [r660285] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h,
|
||||
libkexiv2/libkexiv2_export.h, libkexiv2/version.h:
|
||||
fix headers
|
||||
|
||||
2007-05-02 06:42 cgilles
|
||||
|
||||
* [r660276] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : test if file is readable before to load it
|
||||
in Exiv2.
|
||||
CCBUGS: 144895
|
||||
|
||||
* [r658899] BUG:144455
|
||||
thanks a lot to Stéphane Pontier, corrected a lot of spelling errors
|
||||
and missing mails
|
||||
M /trunk/extragear/libs/libkexiv2/libkexiv2_export.h
|
||||
M /trunk/extragear/libs/libkexiv2/kexiv2.cpp
|
||||
M /trunk/extragear/libs/libkexiv2/version.h
|
||||
M /trunk/extragear/libs/libkexiv2/kexiv2.h
|
||||
|
||||
* [r658847] GPS problem solved. BUG:144604
|
||||
M /trunk/extragear/libs/libkexiv2/kexiv2.cpp
|
||||
|
||||
Gilles patch
|
||||
* [r658711]M /trunk/extragear/libs/libkexiv2/libkexiv2.pc.in
|
||||
M /trunk/extragear/libs/libkexiv2/kexiv2.cpp
|
||||
M /trunk/extragear/libs/libkexiv2/libkexiv2.lsm
|
||||
M /trunk/extragear/libs/libkexiv2/version.h
|
||||
M /trunk/extragear/libs/libkexiv2/NEWS
|
||||
M /trunk/extragear/libs/libkexiv2/README
|
||||
M /trunk/extragear/libs/libkexiv2/kexiv2.h
|
||||
|
||||
V 0.1.2 - 2007-04-07
|
||||
----------------------------------------------------------------------------
|
||||
2007-03-26 07:54 cgilles
|
||||
|
||||
* [r646581] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : polish API : all "get...()" method must
|
||||
use const operator.
|
||||
|
||||
2007-03-15 11:10 anaselli
|
||||
|
||||
* [r642771] libkexiv2/Makefile.am, libkexiv2/NEWS:
|
||||
API changed, please check if it is correct.
|
||||
|
||||
CCMAIL: caulier.gilles@kdemail.net
|
||||
|
||||
2007-03-15 09:09 cgilles
|
||||
|
||||
* [r642722] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : Fix broken compilation with current Exiv2
|
||||
implementation (next 0.14.0 release), duing a change with C++
|
||||
Exception rule. I recommend to cleanup and recompile this library
|
||||
and clients program witch use it.
|
||||
|
||||
Note : libkexiv2 still compatible with older Exiv2 releases 0.12
|
||||
and 0.13
|
||||
|
||||
CCMAIL: digikam-devel@kde.org
|
||||
|
||||
2007-03-14 12:12 cgilles
|
||||
|
||||
* [r642432] libkexiv2/NEWS:
|
||||
--Cette ligupdatene, et les suivantes ci-dessous, seront
|
||||
ignorées--
|
||||
|
||||
M NEWS
|
||||
|
||||
2007-03-14 12:11 cgilles
|
||||
|
||||
* [r642431] libkexiv2/kexiv2.cpp:
|
||||
libkexiv2 from trunk : patch from Arnd Baecker about to avoid the
|
||||
GPS accuracy loss
|
||||
BUG: 140297
|
||||
|
||||
2007-03-14 12:05 cgilles
|
||||
|
||||
* [r642427] libkexiv2/kexiv2.cpp:
|
||||
sanity check again about empty byte array
|
||||
|
||||
2007-03-14 11:58 cgilles
|
||||
|
||||
* [r642421] libkexiv2/libkexiv2.lsm:
|
||||
fix version
|
||||
|
||||
2007-03-14 11:58 cgilles
|
||||
|
||||
* [r642420] libkexiv2/libkexiv2.pc.in:
|
||||
fix version
|
||||
|
||||
2007-03-14 11:57 cgilles
|
||||
|
||||
* [r642419] libkexiv2/version.h:
|
||||
fix version
|
||||
|
||||
2007-03-11 10:58 cgilles
|
||||
|
||||
* [r641445] libkexiv2/kexiv2.cpp:
|
||||
libkexiv2 from trunk : when Exiv2 0.14.0 will be available, a new
|
||||
tag Exif.Image.ProcessingSoftware will be use to store program
|
||||
informations
|
||||
|
||||
2007-03-11 10:31 cgilles
|
||||
|
||||
* [r641437] libkexiv2/NEWS:
|
||||
update
|
||||
|
||||
2007-03-11 10:30 cgilles
|
||||
|
||||
* [r641436] libkexiv2/kexiv2.cpp:
|
||||
fix comment
|
||||
|
||||
2007-03-11 10:29 cgilles
|
||||
|
||||
* [r641435] libkexiv2/kexiv2.cpp:
|
||||
libkexiv2 from trunk : set the Exif.Image.Software tag only if it
|
||||
doesn't exist.
|
||||
BUG: 142564
|
||||
|
||||
2007-02-28 22:19 ach
|
||||
|
||||
* [r638128] libkexiv2:
|
||||
libkexiv2: ignore Makefile.in
|
||||
|
||||
2007-02-28 22:14 ach
|
||||
|
||||
* [r638127] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h,
|
||||
libkexiv2/libkexiv2_export.h, libkexiv2/version.h:
|
||||
libkexiv2: remove unnecessary line break in license notice
|
||||
|
||||
2007-02-28 22:13 ach
|
||||
|
||||
* [r638126] libkexiv2/AUTHORS:
|
||||
libkexiv2: add final newline to AUTHORS
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
V 0.1.1 - 2007-02-24
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
2007-02-24 20:53 anaselli
|
||||
|
||||
* [r636958] libkexiv2/ChangeLog, libkexiv2/Makefile.am,
|
||||
libkexiv2/NEWS, libkexiv2/libkexiv2.lsm,
|
||||
libkexiv2/libkexiv2.pc.in, libkexiv2/version.h:
|
||||
libkexiv2 0.1.1
|
||||
bug fixing release
|
||||
|
||||
CCMAIL:kde-imaging@kde.org
|
||||
|
||||
2007-02-22 12:01 cgilles
|
||||
|
||||
* [r636211] kipi-plugins/kipiplugins.kdevelop,
|
||||
kipi-plugins/rawconverter/batchdialog.cpp,
|
||||
kipi-plugins/rawconverter/singledialog.cpp,
|
||||
libkdcraw/dcraw/dcraw.c, libkdcraw/dcraw/kdcraw.1,
|
||||
libkdcraw/dcrawbinary.cpp, libkdcraw/dcrawsettingswidget.cpp,
|
||||
libkdcraw/dcrawsettingswidget.h, libkdcraw/kdcraw.cpp,
|
||||
libkdcraw/libkdcraw.kdevelop, libkdcraw/rawdecodingsettings.h,
|
||||
libkexiv2/libkexiv2.kdevelop:
|
||||
libkdcraw from trunk : backport last dcraw implementation "8.60"
|
||||
: the old noise reduction algorithm used with 8.54 version have
|
||||
been remplaced by a wavelet algorithm witch is more intuitive,
|
||||
more easy to use (just one threshold setting) and very powerfull.
|
||||
|
||||
Kipi-plugins RAWConverter from svn trunk is fixed.
|
||||
The patch to use libkdcraw with current implementation of digiKam
|
||||
have been updated :
|
||||
|
||||
http://digikam3rdparty.free.fr/misc.tarballs/libkdcrawdigikamport.patch
|
||||
|
||||
CCMAIL: digikam-users@kde.org
|
||||
CCMAIL: digikam-devel@kde.org
|
||||
CCMAIL: kde-imaging@kde.org
|
||||
|
||||
2007-02-20 17:22 cgilles
|
||||
|
||||
* [r635684] libkexiv2/kexiv2.cpp:
|
||||
libkexiv2 from trunk : sanity check if QByteArray are null size
|
||||
everywhere
|
||||
Feedback welcome...
|
||||
|
||||
CCBUGS:141980
|
||||
|
||||
2007-02-16 22:01 cgilles
|
||||
|
||||
* [r634334] kipi-plugins/rawconverter/actionthread.cpp,
|
||||
kipi-plugins/rawconverter/rawdecodingiface.cpp,
|
||||
libkdcraw/kdcraw.cpp, libkexiv2/kexiv2.cpp:
|
||||
fix comments
|
||||
|
||||
2007-02-16 15:44 cgilles
|
||||
|
||||
* [r634186] libkexiv2/libkexiv2.kdevelop:
|
||||
kdevelop project
|
||||
|
||||
2007-02-16 15:44 cgilles
|
||||
|
||||
* [r634184] libkexiv2/AUTHORS, libkexiv2/kexiv2.cpp,
|
||||
libkexiv2/kexiv2.h, libkexiv2/libkexiv2.lsm:
|
||||
fix email
|
||||
|
||||
2007-02-15 13:15 cgilles
|
||||
|
||||
* [r633835] libkexiv2/kexiv2.cpp:
|
||||
indent
|
||||
|
||||
2007-02-13 22:41 ach
|
||||
|
||||
* [r633377] libkexiv2/README:
|
||||
fix library name in README
|
||||
|
||||
2007-02-12 15:26 cgilles
|
||||
|
||||
* [r632868] libkexiv2/configure.in.in:
|
||||
this condition is unused everywhere. removed
|
||||
|
||||
2007-02-12 09:24 cgilles
|
||||
|
||||
* [r632763] libkexiv2/configure.in.in:
|
||||
revert
|
||||
|
||||
2007-02-12 08:32 cgilles
|
||||
|
||||
* [r632755] libkexiv2/configure.in.in:
|
||||
polish
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
V 0.1.0 - 2007-02-11
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
2007-02-11 20:44 anaselli
|
||||
|
||||
* [r632646] libkexiv2/ChangeLog, libkexiv2/libkexiv2.lsm,
|
||||
libkexiv2/libkexiv2.pc.in:
|
||||
libkexiv2 0.1.0
|
||||
|
||||
CCMAIL:kde-imaging@kde.org
|
||||
|
||||
|
||||
2007-02-09 06:50 cgilles
|
||||
|
||||
* [r631791] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : new method to extract Exiv2 library
|
||||
release numbers
|
||||
|
||||
2007-02-06 22:12 cgilles
|
||||
|
||||
* [r630973] libkexiv2/version.h:
|
||||
compile
|
||||
|
||||
2007-02-06 22:04 cgilles
|
||||
|
||||
* [r630967] libkexiv2/Makefile.am, libkexiv2/version.h:
|
||||
libkexiv2 from trunk : add version header file for this library
|
||||
CCMAIL: Achim Bohnet <ach@mpe.mpg.de>
|
||||
|
||||
2007-02-06 21:39 cgilles
|
||||
|
||||
* [r630957] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
2 new methods to set IPTC or Exif tag using a bytes array
|
||||
|
||||
2007-02-05 20:16 cgilles
|
||||
|
||||
* [r630585] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
factorize source code
|
||||
|
||||
2007-02-05 14:38 cgilles
|
||||
|
||||
* [r630488] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
add boolean flag to add or not program name when a tag is
|
||||
changed/added
|
||||
|
||||
2007-02-05 14:00 cgilles
|
||||
|
||||
* [r630471] libkexiv2/kexiv2.h:
|
||||
set load() like virtual method. Re-implemented in digiKam core
|
||||
|
||||
2007-01-30 12:18 cgilles
|
||||
|
||||
* [r628573] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-30 12:16 cgilles
|
||||
|
||||
* [r628572] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-30 12:12 cgilles
|
||||
|
||||
* [r628569] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-30 11:57 cgilles
|
||||
|
||||
* [r628566] libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : polish implementation. Fix API doc
|
||||
|
||||
2007-01-30 11:18 cgilles
|
||||
|
||||
* [r628554] libkexiv2/kexiv2.h:
|
||||
libkexiv2 from trunk : Finamize API doc.
|
||||
|
||||
2007-01-30 10:23 cgilles
|
||||
|
||||
* [r628541] libkexiv2/kexiv2_export.h:
|
||||
wrong file name
|
||||
|
||||
2007-01-30 10:22 cgilles
|
||||
|
||||
* [r628540] kipi-plugins/galleryexport/gallerytalker.cpp,
|
||||
kipi-plugins/gpssync/gpslistviewitem.cpp,
|
||||
kipi-plugins/gpssync/plugin_gpssync.cpp,
|
||||
kipi-plugins/jpeglossless/jpegtransform.h,
|
||||
kipi-plugins/metadataedit/exifadjust.cpp,
|
||||
kipi-plugins/metadataedit/exifcaption.cpp,
|
||||
kipi-plugins/metadataedit/exifdatetime.cpp,
|
||||
kipi-plugins/metadataedit/exifdevice.cpp,
|
||||
kipi-plugins/metadataedit/exifeditdialog.cpp,
|
||||
kipi-plugins/metadataedit/exiflens.cpp,
|
||||
kipi-plugins/metadataedit/exiflight.cpp,
|
||||
kipi-plugins/metadataedit/iptccaption.cpp,
|
||||
kipi-plugins/metadataedit/iptccategories.cpp,
|
||||
kipi-plugins/metadataedit/iptccredits.cpp,
|
||||
kipi-plugins/metadataedit/iptcdatetime.cpp,
|
||||
kipi-plugins/metadataedit/iptceditdialog.cpp,
|
||||
kipi-plugins/metadataedit/iptckeywords.cpp,
|
||||
kipi-plugins/metadataedit/iptcorigin.cpp,
|
||||
kipi-plugins/metadataedit/iptcstatus.cpp,
|
||||
kipi-plugins/metadataedit/iptcsubjects.cpp,
|
||||
kipi-plugins/metadataedit/plugin_metadataedit.cpp,
|
||||
kipi-plugins/rawconverter/dcrawiface.cpp,
|
||||
kipi-plugins/sendimages/sendimages.cpp,
|
||||
kipi-plugins/timeadjust/timeadjustdialog.cpp,
|
||||
libkexiv2/Makefile.am, libkexiv2/kexiv2.cpp, libkexiv2/kexiv2.h,
|
||||
libkexiv2/kexiv2_export.h, libkexiv2/libkexiv2.cpp,
|
||||
libkexiv2/libkexiv2.h:
|
||||
kipi-plugins from trunk : fix libkexiv2 header file name
|
||||
|
||||
2007-01-30 09:45 cgilles
|
||||
|
||||
* [r628536] kipi-plugins/galleryexport/gallerytalker.cpp,
|
||||
kipi-plugins/gpssync/gpslistviewitem.cpp,
|
||||
kipi-plugins/gpssync/plugin_gpssync.cpp,
|
||||
kipi-plugins/jpeglossless/jpegtransform.cpp,
|
||||
kipi-plugins/jpeglossless/jpegtransform.h,
|
||||
kipi-plugins/metadataedit/exifadjust.cpp,
|
||||
kipi-plugins/metadataedit/exifcaption.cpp,
|
||||
kipi-plugins/metadataedit/exifdatetime.cpp,
|
||||
kipi-plugins/metadataedit/exifdevice.cpp,
|
||||
kipi-plugins/metadataedit/exifeditdialog.cpp,
|
||||
kipi-plugins/metadataedit/exiflens.cpp,
|
||||
kipi-plugins/metadataedit/exiflight.cpp,
|
||||
kipi-plugins/metadataedit/iptccaption.cpp,
|
||||
kipi-plugins/metadataedit/iptccategories.cpp,
|
||||
kipi-plugins/metadataedit/iptccredits.cpp,
|
||||
kipi-plugins/metadataedit/iptcdatetime.cpp,
|
||||
kipi-plugins/metadataedit/iptceditdialog.cpp,
|
||||
kipi-plugins/metadataedit/iptckeywords.cpp,
|
||||
kipi-plugins/metadataedit/iptcorigin.cpp,
|
||||
kipi-plugins/metadataedit/iptcstatus.cpp,
|
||||
kipi-plugins/metadataedit/iptcsubjects.cpp,
|
||||
kipi-plugins/metadataedit/plugin_metadataedit.cpp,
|
||||
kipi-plugins/rawconverter/dcrawiface.cpp,
|
||||
kipi-plugins/sendimages/sendimages.cpp,
|
||||
kipi-plugins/timeadjust/timeadjustdialog.cpp,
|
||||
libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2.h:
|
||||
kipi-plugins from trunk : fix libkexiv2 namespace and interface
|
||||
class name
|
||||
|
||||
2007-01-30 09:02 cgilles
|
||||
|
||||
* [r628532] libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2.h:
|
||||
libkexiv2 from trunk : backport new method to handle color
|
||||
work-space of image. update API doc.
|
||||
|
||||
2007-01-29 23:36 ach
|
||||
|
||||
* [r628396] libkexiv2/libkexiv2.h:
|
||||
libkexiv2: s/informations/information/
|
||||
|
||||
2007-01-29 21:19 lure
|
||||
|
||||
* [r628356] libkexiv2/libkexiv2.h:
|
||||
Fix broken comment - did not compile on Kubuntu Feisty
|
||||
|
||||
2007-01-29 19:57 cgilles
|
||||
|
||||
* [r628333] libkexiv2/libkexiv2.h:
|
||||
libkexiv2 from trunk : Doxygen API (uncomplete)
|
||||
|
||||
2007-01-29 16:49 cgilles
|
||||
|
||||
* [r628263] libkexiv2/libkexiv2.pc.in:
|
||||
update
|
||||
|
||||
2007-01-29 14:18 cgilles
|
||||
|
||||
* [r628206] libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2.h:
|
||||
libkexiv2 from trunk : backport all common methods from
|
||||
Digikam::DMetadata class. Backport all fix about comments
|
||||
encoding from Marcel. The implementation is clean now and ready
|
||||
to use with digiKam core.
|
||||
|
||||
CCMAIL : marcel.wiesweg@gmx.de, digikam-devel@kde.org,
|
||||
kde-imaging@kde.org
|
||||
|
||||
2007-01-29 09:50 cgilles
|
||||
|
||||
* [r628140] libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2.h:
|
||||
new protected methods to use with derivated class
|
||||
|
||||
2007-01-28 10:32 ach
|
||||
|
||||
* [r627809] libkexiv2/libkexiv2.lsm, libkexiv2/libkexiv2.pc.in,
|
||||
prepare_libkexiv2.rb:
|
||||
libkexiv2: use same version everywhere
|
||||
|
||||
2007-01-28 09:54 ach
|
||||
|
||||
* [r627801] libkexiv2/libkexiv2.pc.in:
|
||||
libkexiv2: fix libkexiv2.pc pkg-config file
|
||||
|
||||
2007-01-28 09:47 ach
|
||||
|
||||
* [r627799] libkexiv2/configure.in.in:
|
||||
libkexiv2: Fix configure. PKGCONFIGFOUND was not set.
|
||||
|
||||
CCMAIL: kde-imaging@kde.org
|
||||
|
||||
2007-01-27 12:40 cgilles
|
||||
|
||||
* [r627622] libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2_export.h:
|
||||
kipi-plugins from trunk : libkexiv2 : do not use kdDebug() here
|
||||
because it's not thread-safe. Use qDebug() instead...
|
||||
|
||||
CCBUGS: 133026
|
||||
|
||||
2007-01-27 10:57 cgilles
|
||||
|
||||
* [r627593] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-26 20:59 cgilles
|
||||
|
||||
* [r627509] libkexiv2/libkexiv2.lsm:
|
||||
update
|
||||
|
||||
2007-01-26 20:40 cgilles
|
||||
|
||||
* [r627501] kipi-plugins/NEWS, kipi-plugins/README,
|
||||
kipi-plugins/common/Makefile.am, kipi-plugins/common/exiv2iface,
|
||||
kipi-plugins/configure.in.bot, kipi-plugins/configure.in.in,
|
||||
kipi-plugins/galleryexport/Makefile.am,
|
||||
kipi-plugins/galleryexport/gallerytalker.cpp,
|
||||
kipi-plugins/gpssync/Makefile.am,
|
||||
kipi-plugins/gpssync/gpslistviewitem.cpp,
|
||||
kipi-plugins/gpssync/gpslistviewitem.h,
|
||||
kipi-plugins/gpssync/plugin_gpssync.cpp,
|
||||
kipi-plugins/gpssync/plugin_gpssync.h,
|
||||
kipi-plugins/jpeglossless/Makefile.am,
|
||||
kipi-plugins/jpeglossless/jpegtransform.cpp,
|
||||
kipi-plugins/jpeglossless/jpegtransform.h,
|
||||
kipi-plugins/metadataedit/Makefile.am,
|
||||
kipi-plugins/metadataedit/exifadjust.cpp,
|
||||
kipi-plugins/metadataedit/exifcaption.cpp,
|
||||
kipi-plugins/metadataedit/exifdatetime.cpp,
|
||||
kipi-plugins/metadataedit/exifdevice.cpp,
|
||||
kipi-plugins/metadataedit/exifeditdialog.cpp,
|
||||
kipi-plugins/metadataedit/exiflens.cpp,
|
||||
kipi-plugins/metadataedit/exiflight.cpp,
|
||||
kipi-plugins/metadataedit/iptccaption.cpp,
|
||||
kipi-plugins/metadataedit/iptccategories.cpp,
|
||||
kipi-plugins/metadataedit/iptccredits.cpp,
|
||||
kipi-plugins/metadataedit/iptcdatetime.cpp,
|
||||
kipi-plugins/metadataedit/iptceditdialog.cpp,
|
||||
kipi-plugins/metadataedit/iptckeywords.cpp,
|
||||
kipi-plugins/metadataedit/iptcorigin.cpp,
|
||||
kipi-plugins/metadataedit/iptcstatus.cpp,
|
||||
kipi-plugins/metadataedit/iptcsubjects.cpp,
|
||||
kipi-plugins/metadataedit/plugin_metadataedit.cpp,
|
||||
kipi-plugins/rawconverter/Makefile.am,
|
||||
kipi-plugins/rawconverter/dcrawiface.cpp,
|
||||
kipi-plugins/rawconverter/dcrawiface.h,
|
||||
kipi-plugins/sendimages/Makefile.am,
|
||||
kipi-plugins/sendimages/sendimages.cpp,
|
||||
kipi-plugins/sendimages/sendimages.h,
|
||||
kipi-plugins/sync/Makefile.am,
|
||||
kipi-plugins/sync/gallerytalker.cpp,
|
||||
kipi-plugins/timeadjust/Makefile.am,
|
||||
kipi-plugins/timeadjust/timeadjustdialog.cpp,
|
||||
kipi-plugins/timeadjust/timeadjustdialog.h,
|
||||
libkexiv2/libkexiv2.cpp, libkexiv2/libkexiv2.h:
|
||||
kipi-plugins from trunk : all plugins use the new shared library
|
||||
libkexiv2 instead KipiPlugins::EXiv2Iface class.
|
||||
|
||||
Marcel, next stage is to derivate the DigiKam::DMetadata class
|
||||
from LibKExiv2. Let's me hear when you whant to do it...
|
||||
|
||||
CCMAIL: digikam-devel@kde.org, kde-imaging@kde.org,
|
||||
marcel.wiesweg@gmx.de
|
||||
|
||||
2007-01-26 15:51 cgilles
|
||||
|
||||
* [r627439] libkexiv2/configure.in.bot, libkexiv2/configure.in.in:
|
||||
compile
|
||||
|
||||
2007-01-26 15:38 cgilles
|
||||
|
||||
* [r627433] libkexiv2/libkexiv2.h:
|
||||
using Export rules
|
||||
|
||||
2007-01-26 13:14 cgilles
|
||||
|
||||
* [r627401] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-26 13:10 cgilles
|
||||
|
||||
* [r627400] libkexiv2/README:
|
||||
update
|
||||
|
||||
2007-01-26 13:09 cgilles
|
||||
|
||||
* [r627399] libkexiv2/Makefile.am, libkexiv2/exiv2iface.cpp,
|
||||
libkexiv2/exiv2iface.h, libkexiv2/libkexiv2.cpp,
|
||||
libkexiv2/libkexiv2.h:
|
||||
exiv2iface ==> libkexiv2
|
||||
new namespace
|
||||
|
||||
2007-01-26 12:58 cgilles
|
||||
|
||||
* [r627393] libkexiv2/exiv2iface.cpp, libkexiv2/exiv2iface.h,
|
||||
libkexiv2/libkexiv2_export.h:
|
||||
fix header
|
||||
|
||||
2007-01-26 12:55 cgilles
|
||||
|
||||
* [r627390] libkexiv2, libkexiv2/AUTHORS, libkexiv2/COPYING,
|
||||
libkexiv2/ChangeLog, libkexiv2/INSTALL, libkexiv2/Makefile.am,
|
||||
libkexiv2/NEWS, libkexiv2/README, libkexiv2/configure.in.in,
|
||||
libkexiv2/exiv2iface.cpp, libkexiv2/exiv2iface.h,
|
||||
libkexiv2/libkexiv2.lsm, libkexiv2/libkexiv2.pc.in,
|
||||
libkexiv2/libkexiv2_export.h:
|
||||
New common library witch will be used by digiKam and kipi-plugins
|
||||
to handle Pictures metadata (Exif/Iptc).
|
||||
|
||||
This shared library is a wrapper around Exiv2 library. It give a
|
||||
Qt like interface to control
|
||||
Exiv2 actions on pictures. Using this library will remove all
|
||||
duplicate code on kipi-plugins::exiv2iface and
|
||||
digikam::dmetadata.
|
||||
|
||||
The current implementation is the same than
|
||||
kipi-plugins/exiv2iface. This library is not yet used in
|
||||
digiKam/kipi-plugins.
|
||||
We will added a new depency to this library in kipi-plugins in
|
||||
first and later to digiKam.
|
||||
|
||||
CCMAIL: digikam-devel@kde.org, kde-imaging@kde.org,
|
||||
marcel.wiesweg@gmx.de
|
||||
|
2
libkexiv2/Messages.sh
Executable file
2
libkexiv2/Messages.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#! /bin/sh
|
||||
$XGETTEXT libkexiv2/*.cpp -o $podir/libkexiv2.pot
|
101
libkexiv2/NEWS
Normal file
101
libkexiv2/NEWS
Normal file
|
@ -0,0 +1,101 @@
|
|||
0.6.0 - Released with KDE 4.4.0
|
||||
------------------------------------------------------------------------
|
||||
|
||||
- New widgets shared between digiKam and kipi-plugins to play with IPTC Subjects and Language Alternative.
|
||||
|
||||
0.5.0 - Released with KDE 4.3.0
|
||||
------------------------------------------------------------------------
|
||||
|
||||
- New option to enable/disable file timestamp updating when metadata are saved.
|
||||
|
||||
0.4.0 - Released with KDE 4.2.0
|
||||
------------------------------------------------------------------------
|
||||
|
||||
- Use kDebug(51003) instead qDebug()
|
||||
- New option to enable/disable Raw metadata writting.
|
||||
|
||||
0.3.0 - Released with KDE 4.1.2
|
||||
------------------------------------------------------------------------
|
||||
|
||||
- API changed: Added 2 new static methods to init and clear non re-entrant Adobe XMP
|
||||
SDK code from Exiv2 core. This code must be called before and after
|
||||
all multithreaded operations with KExiv2.
|
||||
* initializeExiv2().
|
||||
* cleanupExiv2().
|
||||
Added a new method to load image data from a byte array.
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 166424: Crash when editing Caption with Digikam4 SVN.
|
||||
|
||||
0.2.0 - Released with KDE 4.1.0
|
||||
------------------------------------------------------------------------
|
||||
Port to CMake/KDE4/QT4
|
||||
Support of XMP metadata (require Exiv2 0.16)
|
||||
Split methods to separate files to provide a more readable implementation.
|
||||
New method to fix orientation of a QImage accordingly with Exif orientation tag.
|
||||
Moved from extragear/libs to kdegraphics/libs
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 146864: Lesser XMP support in digiKam.
|
||||
|
||||
0.1.7
|
||||
------------------------------------------------------------------------
|
||||
|
||||
- API changed: Added a version method to get runtime library version.
|
||||
- Fix RemoveIptcTag() to handle all redondant Iptc tags at the same time.
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 157552: negative altitudes are not shown in metadata GPS sidebar
|
||||
|
||||
0.1.6
|
||||
------------------------------------------------------------------------
|
||||
- API Changed : Kexiv2 destructor is now virtual.
|
||||
Krazy Code Checker fix (http://www.englishbreakfastnetwork.org/krazy)
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 137750: color mode2 nikon d70s not recognized as adobe rgb
|
||||
002 ==> 149267: digiKam crashes after finding gif,avi,txt and so on.
|
||||
003 ==> 148182: Iptc.Application2.Keywords appends always the 0-byte.
|
||||
|
||||
0.1.5
|
||||
------------------------------------------------------------------------
|
||||
Fix release version information.
|
||||
|
||||
0.1.4
|
||||
------------------------------------------------------------------------
|
||||
Fix release version information.
|
||||
|
||||
0.1.3
|
||||
------------------------------------------------------------------------
|
||||
New features
|
||||
- API changed: added 4 new static methods to get Exif/Iptc tags description/title.
|
||||
moved depreceate protected methods used by digiKam core to private.
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 144604: Rotation causes Exif data corruption. GPS data fix
|
||||
|
||||
General : Make size of icons used in album icon view more configurable using a slider
|
||||
in status bar.
|
||||
General : Removing direct Exiv2 library depency. libkexiv2 interface is used everywhere
|
||||
instead.
|
||||
|
||||
0.1.2
|
||||
------------------------------------------------------------------------
|
||||
New features
|
||||
- added support for exiv2 from svn (next 0.14.0 release)
|
||||
- API changed: added printExiv2ExceptionError to manage Exiv2 C++ Exception error message
|
||||
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 142564: digiKam-signature in iptc and exif tags.
|
||||
002 ==> 140297: GPS kipi plugin truncates input coordinates, introducing inacuracy.
|
||||
|
||||
0.1.1
|
||||
------------------------------------------------------------------------
|
||||
Bugs fixed from B.K.O (http://bugs.kde.org):
|
||||
001 ==> 141980: digiKam crash when rescan certain files exiv2.
|
||||
|
||||
0.1.0
|
||||
------------------------------------------------------------------------
|
||||
First implementation
|
||||
|
||||
For details and info about previous versions, see ChangeLog.
|
75
libkexiv2/README
Normal file
75
libkexiv2/README
Normal file
|
@ -0,0 +1,75 @@
|
|||
EXIV2 Library interface for KDE
|
||||
|
||||
This library is a part of digiKam project (http://www.digikam.org)
|
||||
|
||||
-- AUTHORS ------------------------------------------------------------
|
||||
|
||||
See AUTHORS file for details.
|
||||
|
||||
-- ABOUT --------------------------------------------------------------
|
||||
|
||||
Libkexiv2 is a wrapper around Exiv2 library to manipulate pictures
|
||||
metadata as EXIF/IPTC and XMP. Metadata interface follow this paper:
|
||||
|
||||
http://www.metadataworkinggroup.com/pdf/mwg_guidance.pdf
|
||||
|
||||
This library is used by kipi-plugins, digiKam and others kipi host programs.
|
||||
|
||||
The library documentation is available on header files.
|
||||
|
||||
-- DEPENDENCIES -------------------------------------------------------
|
||||
|
||||
CMake >= 2.8.x http://www.cmake.org
|
||||
libqt >= 4.6.x http://www.qtsoftware.com
|
||||
libkde >= 4.4.x http://www.kde.org
|
||||
libexiv2 >= 0.21.0 http://www.exiv2.org
|
||||
|
||||
-- INSTALL ------------------------------------------------------------
|
||||
|
||||
In order to compile, especially when QT3/Qt4 are installed at the same time,
|
||||
just use something like that:
|
||||
|
||||
# export VERBOSE=1
|
||||
# export QTDIR=/usr/lib/qt4/
|
||||
# export PATH=$QTDIR/bin:$PATH
|
||||
# cmake .
|
||||
# make
|
||||
|
||||
Usual CMake options:
|
||||
|
||||
-DCMAKE_INSTALL_PREFIX : decide where the program will be install on your computer.
|
||||
-DCMAKE_BUILD_TYPE : decide which type of build you want. You can chose between "debugfull", "debug", "profile", "relwithdebinfo" and "release". The default is "relwithdebinfo" (-O2 -g).
|
||||
|
||||
Compared to old KDE3 autoconf options:
|
||||
|
||||
"cmake . -DCMAKE_BUILD_TYPE=debugfull" is equivalent to "./configure --enable-debug=full"
|
||||
"cmake . -DCMAKE_INSTALL_PREFIX=/usr" is equivalent to "./configure --prefix=/usr"
|
||||
|
||||
More details can be found ata this url: http://techbase.kde.org/Development/Tutorials/CMake#Environment_Variables
|
||||
|
||||
Note: To know KDE install path on your computer, use 'kde-config --prefix' command line like this (with full debug object enabled):
|
||||
|
||||
"cmake . -DCMAKE_BUILD_TYPE=debugfull -DCMAKE_INSTALL_PREFIX=`kde4-config --prefix`"
|
||||
|
||||
-- CONTACT ------------------------------------------------------------
|
||||
|
||||
If you have questions, comments, suggestions to make do email at:
|
||||
|
||||
kde-imaging@kde.org
|
||||
|
||||
IRC channel from freenode.net server:
|
||||
|
||||
#kde-imaging
|
||||
|
||||
-- BUGS ---------------------------------------------------------------
|
||||
|
||||
IMPORTANT : the bugreports and wishlist are hosted by the KDE bugs report
|
||||
system who can be contacted by the standard Kde help menu of plugins dialog.
|
||||
A mail will be automatically sent to the Kipi mailing list.
|
||||
There is no need to contact directly the Kipi mailing list for a bug report
|
||||
or a devel wish.
|
||||
|
||||
The current Kipi bugs and devel wish reported to the Kde bugs report can be see
|
||||
at this url:
|
||||
|
||||
http://bugs.kde.org/buglist.cgi?product=digikam&component=libkexiv2&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED
|
3
libkexiv2/TODO
Normal file
3
libkexiv2/TODO
Normal file
|
@ -0,0 +1,3 @@
|
|||
- Extract GPS info from XMP tags.
|
||||
- Exif/Iptc => Xmp synchronization.
|
||||
- Xmp side-car file support.
|
8
libkexiv2/libkexiv2-config.cmake.in
Normal file
8
libkexiv2/libkexiv2-config.cmake.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
@PACKAGE_INIT@
|
||||
set_and_check(libkexiv2_INCLUDE_DIR @PACKAGE_INCLUDE_INSTALL_DIR@)
|
||||
if(NOT TARGET Libkexiv2::kexiv2)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/libkexiv2-export.cmake)
|
||||
endif()
|
||||
|
||||
set(libkexiv2_LIBRARY Libkexiv2::kexiv2)
|
||||
set(libkexiv2_LIBRARIES ${libkexiv2_LIBRARY})
|
15
libkexiv2/libkexiv2.lsm.cmake
Normal file
15
libkexiv2/libkexiv2.lsm.cmake
Normal file
|
@ -0,0 +1,15 @@
|
|||
Begin4
|
||||
Title: libkexiv2
|
||||
Version: ${KEXIV2_LIB_VERSION_STRING}
|
||||
Entered-date: 2007-05-08
|
||||
Description: A C++ library to manipulate EXIF/IPTC/XMP metadata using Exiv2 library. This library is used by digiKam and kipi-plugins.
|
||||
Keywords: kde image jpeg raw tiff png exif iptc xmp digikam kipi-plugins
|
||||
Author: caulier dot gilles at gmail dot com (Gilles Caulier)
|
||||
marcel dot wiesweg at gmx dot de (Marcel Wiesweg)
|
||||
Maintained-by:
|
||||
Primary-site: http://www.digikam.org
|
||||
Alternate-site: http://www.digikam.org
|
||||
Original-site:
|
||||
Platforms: Linux and other Unices, MacOs-X, Win32. Needs KDE 4.x
|
||||
Copying-policy: GPL
|
||||
End
|
12
libkexiv2/libkexiv2.pc.cmake
Normal file
12
libkexiv2/libkexiv2.pc.cmake
Normal file
|
@ -0,0 +1,12 @@
|
|||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${BIN_INSTALL_DIR}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
|
||||
Name: libkexiv2
|
||||
Description: A C++ library to manipulate EXIF/IPTC/XMP metadata using Exiv2 library. This library is used by digiKam and kipi-plugins.
|
||||
URL: http://www.digikam.org
|
||||
Requires:
|
||||
Version: ${KEXIV2_LIB_VERSION_STRING}
|
||||
Libs: -L${LIB_INSTALL_DIR} -lkexiv2
|
||||
Cflags: -I${INCLUDE_INSTALL_DIR}
|
87
libkexiv2/libkexiv2/CMakeLists.txt
Normal file
87
libkexiv2/libkexiv2/CMakeLists.txt
Normal file
|
@ -0,0 +1,87 @@
|
|||
# ===========================================================
|
||||
#
|
||||
# This file is a part of digiKam project
|
||||
# <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
#
|
||||
# @date 2006-09-15
|
||||
# @brief Exiv2 library interface for KDE
|
||||
#
|
||||
# @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
# <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
#
|
||||
# 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, 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.
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
INCLUDE_DIRECTORIES(${EXIV2_INCLUDE_DIR})
|
||||
|
||||
ADD_DEFINITIONS(${EXIV2_DEFINITIONS})
|
||||
ADD_DEFINITIONS(${KDE4_ENABLE_EXCEPTIONS})
|
||||
|
||||
IF( WIN32 )
|
||||
ADD_DEFINITIONS( -DEXV_HAVE_DLL )
|
||||
ENDIF( WIN32 )
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
|
||||
# This was used to enable catching of exceptions thrown by libexiv2.
|
||||
# We use now #pragma GCC visibility push(default) around the headers
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default")
|
||||
|
||||
SET(kexiv2_LIB_SRCS kexiv2.cpp
|
||||
kexiv2_p.cpp
|
||||
kexiv2image.cpp
|
||||
kexiv2comments.cpp
|
||||
kexiv2exif.cpp
|
||||
kexiv2iptc.cpp
|
||||
kexiv2gps.cpp
|
||||
kexiv2xmp.cpp
|
||||
kexiv2previews.cpp
|
||||
altlangstredit.cpp
|
||||
msgtextedit.cpp
|
||||
countryselector.cpp
|
||||
subjectwidget.cpp
|
||||
rotationmatrix.cpp
|
||||
)
|
||||
|
||||
KDE4_ADD_LIBRARY(kexiv2 SHARED ${kexiv2_LIB_SRCS})
|
||||
|
||||
TARGET_LINK_LIBRARIES(kexiv2 ${KDE4_KDEUI_LIBS} ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY}
|
||||
${QT_QTXML_LIBRARY} ${EXIV2_LIBRARIES})
|
||||
|
||||
IF( WIN32 )
|
||||
TARGET_LINK_LIBRARIES( kexiv2 ${EXPAT_LIBRARIES} )
|
||||
ENDIF( WIN32 )
|
||||
|
||||
SET_TARGET_PROPERTIES(kexiv2 PROPERTIES VERSION ${KEXIV2_LIB_SO_VERSION_STRING}
|
||||
SOVERSION ${KEXIV2_LIB_SO_CUR_VERSION})
|
||||
|
||||
INSTALL(TARGETS kexiv2 EXPORT Libkexiv2 ${INSTALL_TARGETS_DEFAULT_ARGS} )
|
||||
|
||||
INSTALL(FILES topicset.iptc-subjectcode.xml DESTINATION ${DATA_INSTALL_DIR}/libkexiv2/data)
|
||||
|
||||
# Check if compilation is done through digiKam SC or not. If yes, do not install header files.
|
||||
IF(NOT DIGIKAMSC_USE_PRIVATE_KDEGRAPHICS)
|
||||
INSTALL(FILES kexiv2.h
|
||||
kexiv2data.h
|
||||
kexiv2previews.h
|
||||
libkexiv2_export.h
|
||||
msgtextedit.h
|
||||
subjectwidget.h
|
||||
altlangstredit.h
|
||||
countryselector.h
|
||||
rotationmatrix.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/libkexiv2
|
||||
COMPONENT Devel
|
||||
)
|
||||
ENDIF()
|
1067
libkexiv2/libkexiv2/Doxyfile
Normal file
1067
libkexiv2/libkexiv2/Doxyfile
Normal file
File diff suppressed because it is too large
Load diff
1067
libkexiv2/libkexiv2/Doxyfile-developer
Normal file
1067
libkexiv2/libkexiv2/Doxyfile-developer
Normal file
File diff suppressed because it is too large
Load diff
14
libkexiv2/libkexiv2/Mainpage.dox
Normal file
14
libkexiv2/libkexiv2/Mainpage.dox
Normal file
|
@ -0,0 +1,14 @@
|
|||
/** @mainpage libKExiv2
|
||||
|
||||
libKExiv2 is a wrapper around <a href="http://exiv2.org/">exiv2</a>, providing read/write access to EXIF, IPTC and XMP metadata. The main differences to <a href="http://exiv2.org/">exiv2</a> are:
|
||||
<ul>
|
||||
<li> Functions take standard Qt containers as parameters
|
||||
<li> Helper functions for GPS data
|
||||
<li> Transparent XMP sidecar support
|
||||
</ul>
|
||||
|
||||
To get started, have a look at the KExiv2Iface::KExiv2 class.
|
||||
|
||||
@see KExiv2Iface::KExiv2
|
||||
|
||||
*/
|
537
libkexiv2/libkexiv2/altlangstredit.cpp
Normal file
537
libkexiv2/libkexiv2/altlangstredit.cpp
Normal file
|
@ -0,0 +1,537 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-06-15
|
||||
* @brief multi-languages string editor
|
||||
*
|
||||
* @author Copyright (C) 2009-2013 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "moc_altlangstredit.cpp"
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QEvent>
|
||||
#include <QStyle>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QToolButton>
|
||||
#include <QGridLayout>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <kdialog.h>
|
||||
#include <kglobal.h>
|
||||
#include <kiconloader.h>
|
||||
#include <klocale.h>
|
||||
#include <ktextedit.h>
|
||||
#include <kcombobox.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "msgtextedit.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class AltLangStrEdit::Private
|
||||
{
|
||||
public:
|
||||
|
||||
Private()
|
||||
{
|
||||
valueEdit = 0;
|
||||
titleLabel = 0;
|
||||
delValueButton = 0;
|
||||
languageCB = 0;
|
||||
linesVisible = 0;
|
||||
currentLanguage = "x-default";
|
||||
|
||||
// We cannot use KLocale::allLanguagesList() here because KDE only
|
||||
// support 2 characters country codes. XMP require 2+2 characters language+country
|
||||
// following ISO 3066 (http://babelwiki.babelzilla.org/index.php?title=Language_codes)
|
||||
|
||||
// The first one from the list is the Default Language code specified by XMP paper
|
||||
languageCodeMap.insert( "x-default", i18n("Default Language") );
|
||||
|
||||
// Standard ISO 3066 country codes.
|
||||
|
||||
languageCodeMap.insert( "af-ZA", i18n("Afrikaans (South Africa)") );
|
||||
languageCodeMap.insert( "am-ET", i18n("Amharic (Ethiopia)") );
|
||||
languageCodeMap.insert( "ar-AE", i18n("Arabic (UAE)") );
|
||||
languageCodeMap.insert( "ar-BH", i18n("Arabic (Bahrain)") );
|
||||
languageCodeMap.insert( "ar-DZ", i18n("Arabic (Algeria)") );
|
||||
languageCodeMap.insert( "ar-EG", i18n("Arabic (Egypt)") );
|
||||
languageCodeMap.insert( "ar-IQ", i18n("Arabic (Iraq)") );
|
||||
languageCodeMap.insert( "ar-JO", i18n("Arabic (Jordan)") );
|
||||
languageCodeMap.insert( "ar-KW", i18n("Arabic (Kuwait)") );
|
||||
languageCodeMap.insert( "ar-LB", i18n("Arabic (Lebanon)") );
|
||||
languageCodeMap.insert( "ar-LY", i18n("Arabic (Libya)") );
|
||||
languageCodeMap.insert( "ar-MA", i18n("Arabic (Morocco)") );
|
||||
languageCodeMap.insert( "ar-OM", i18n("Arabic (Oman)") );
|
||||
languageCodeMap.insert( "ar-QA", i18n("Arabic (Qatar)") );
|
||||
languageCodeMap.insert( "ar-SA", i18n("Arabic (Saudi Arabia)") );
|
||||
languageCodeMap.insert( "ar-SY", i18n("Arabic (Syria)") );
|
||||
languageCodeMap.insert( "ar-TN", i18n("Arabic (Tunisia)") );
|
||||
languageCodeMap.insert( "ar-YE", i18n("Arabic (Yemen)") );
|
||||
languageCodeMap.insert( "as-IN", i18n("Assamese (India)") );
|
||||
languageCodeMap.insert( "ba-RU", i18n("Bashkir (Russia)") );
|
||||
languageCodeMap.insert( "be-BY", i18n("Belarusian (Belarus)") );
|
||||
languageCodeMap.insert( "bg-BG", i18n("Bulgarian (Bulgaria)") );
|
||||
languageCodeMap.insert( "bn-IN", i18n("Bengali (India)") );
|
||||
languageCodeMap.insert( "bo-BT", i18n("Tibetan (Bhutan)") );
|
||||
languageCodeMap.insert( "bo-CN", i18n("Tibetan (PRC)") );
|
||||
languageCodeMap.insert( "br-FR", i18n("Breton (France)") );
|
||||
languageCodeMap.insert( "ca-AD", i18n("Catalan (Andorra)") );
|
||||
languageCodeMap.insert( "ca-ES", i18n("Catalan (Spain)") );
|
||||
languageCodeMap.insert( "ca-FR", i18n("Catalan (France)") );
|
||||
languageCodeMap.insert( "co-FR", i18n("Corsican (France)") );
|
||||
languageCodeMap.insert( "cs-CZ", i18n("Czech (Czech Republic)") );
|
||||
languageCodeMap.insert( "cy-GB", i18n("Welsh (United Kingdom)") );
|
||||
languageCodeMap.insert( "da-DK", i18n("Danish (Denmark)") );
|
||||
languageCodeMap.insert( "de-AT", i18n("German (Austria)") );
|
||||
languageCodeMap.insert( "de-CH", i18n("German (Switzerland)") );
|
||||
languageCodeMap.insert( "de-DE", i18n("German (Germany)") );
|
||||
languageCodeMap.insert( "de-LI", i18n("German (Liechtenstein)") );
|
||||
languageCodeMap.insert( "de-LU", i18n("German (Luxembourg)") );
|
||||
languageCodeMap.insert( "el-GR", i18n("Greek (Greece)") );
|
||||
languageCodeMap.insert( "en-AU", i18n("English (Australia)") );
|
||||
languageCodeMap.insert( "en-BZ", i18n("English (Belize)") );
|
||||
languageCodeMap.insert( "en-CA", i18n("English (Canada)") );
|
||||
languageCodeMap.insert( "en-CB", i18n("English (Caribbean)") );
|
||||
languageCodeMap.insert( "en-GB", i18n("English (United Kingdom)") );
|
||||
languageCodeMap.insert( "en-IE", i18n("English (Ireland)") );
|
||||
languageCodeMap.insert( "en-IN", i18n("English (India)") );
|
||||
languageCodeMap.insert( "en-JA", i18n("English (Jamaica)") );
|
||||
languageCodeMap.insert( "en-MY", i18n("English (Malaysia)") );
|
||||
languageCodeMap.insert( "en-NZ", i18n("English (New Zealand)") );
|
||||
languageCodeMap.insert( "en-PH", i18n("English (Philippines)") );
|
||||
languageCodeMap.insert( "en-SG", i18n("English (Singapore)") );
|
||||
languageCodeMap.insert( "en-TT", i18n("English (Trinidad)") );
|
||||
languageCodeMap.insert( "en-US", i18n("English (United States)") );
|
||||
languageCodeMap.insert( "en-ZA", i18n("English (South Africa)") );
|
||||
languageCodeMap.insert( "en-ZW", i18n("English (Zimbabwe)") );
|
||||
languageCodeMap.insert( "es-AR", i18n("Spanish (Argentina)") );
|
||||
languageCodeMap.insert( "es-BO", i18n("Spanish (Bolivia)") );
|
||||
languageCodeMap.insert( "es-CL", i18n("Spanish (Chile)") );
|
||||
languageCodeMap.insert( "es-CO", i18n("Spanish (Colombia)") );
|
||||
languageCodeMap.insert( "es-CR", i18n("Spanish (Costa Rica)") );
|
||||
languageCodeMap.insert( "es-DO", i18n("Spanish (Dominican Republic)") );
|
||||
languageCodeMap.insert( "es-EC", i18n("Spanish (Ecuador)") );
|
||||
languageCodeMap.insert( "es-ES", i18n("Spanish (Spain)") );
|
||||
languageCodeMap.insert( "es-GT", i18n("Spanish (Guatemala)") );
|
||||
languageCodeMap.insert( "es-HN", i18n("Spanish (Honduras)") );
|
||||
languageCodeMap.insert( "es-MX", i18n("Spanish (Mexico)") );
|
||||
languageCodeMap.insert( "es-NI", i18n("Spanish (Nicaragua)") );
|
||||
languageCodeMap.insert( "es-PA", i18n("Spanish (Panama)") );
|
||||
languageCodeMap.insert( "es-PE", i18n("Spanish (Peru)") );
|
||||
languageCodeMap.insert( "es-PR", i18n("Spanish (Puerto Rico)") );
|
||||
languageCodeMap.insert( "es-PY", i18n("Spanish (Paraguay)") );
|
||||
languageCodeMap.insert( "es-SV", i18n("Spanish (El Salvador)") );
|
||||
languageCodeMap.insert( "es-UR", i18n("Spanish (Uruguay)") );
|
||||
languageCodeMap.insert( "es-US", i18n("Spanish (United States)") );
|
||||
languageCodeMap.insert( "es-VE", i18n("Spanish (Venezuela)") );
|
||||
languageCodeMap.insert( "et-EE", i18n("Estonian (Estonia)") );
|
||||
languageCodeMap.insert( "eu-ES", i18n("Basque (Basque Country)") );
|
||||
languageCodeMap.insert( "fa-IR", i18n("Persian (Iran)") );
|
||||
languageCodeMap.insert( "fi-FI", i18n("Finnish (Finland)") );
|
||||
languageCodeMap.insert( "fo-FO", i18n("Faeroese (Faero Islands)") );
|
||||
languageCodeMap.insert( "fr-BE", i18n("French (Belgium)") );
|
||||
languageCodeMap.insert( "fr-CA", i18n("French (Canada)") );
|
||||
languageCodeMap.insert( "fr-CH", i18n("French (Switzerland)") );
|
||||
languageCodeMap.insert( "fr-FR", i18n("French (France)") );
|
||||
languageCodeMap.insert( "fr-LU", i18n("French (Luxembourg)") );
|
||||
languageCodeMap.insert( "fr-MC", i18n("French (Monaco)") );
|
||||
languageCodeMap.insert( "fy-NL", i18n("Frisian (Netherlands)") );
|
||||
languageCodeMap.insert( "ga-IE", i18n("Irish (Ireland)") );
|
||||
languageCodeMap.insert( "gl-ES", i18n("Galician (Galicia)") );
|
||||
languageCodeMap.insert( "gu-IN", i18n("Gujarati (India)") );
|
||||
languageCodeMap.insert( "he-IL", i18n("Hebrew (Israel)") );
|
||||
languageCodeMap.insert( "hi-IN", i18n("Hindi (India)") );
|
||||
languageCodeMap.insert( "hr-BA", i18n("Croatian (Bosnia and Herzegovina, Latin)") );
|
||||
languageCodeMap.insert( "hr-HR", i18n("Croatian (Croatia)") );
|
||||
languageCodeMap.insert( "hu-HU", i18n("Hungarian (Hungary)") );
|
||||
languageCodeMap.insert( "hy-AM", i18n("Armenian (Armenia)") );
|
||||
languageCodeMap.insert( "id-ID", i18n("(Indonesian)") );
|
||||
languageCodeMap.insert( "ii-CN", i18n("Yi (PRC)") );
|
||||
languageCodeMap.insert( "is-IS", i18n("Icelandic (Iceland)") );
|
||||
languageCodeMap.insert( "it-CH", i18n("Italian (Switzerland)") );
|
||||
languageCodeMap.insert( "it-IT", i18n("Italian (Italy)") );
|
||||
languageCodeMap.insert( "ja-JP", i18n("Japanese (Japan)") );
|
||||
languageCodeMap.insert( "ka-GE", i18n("Georgian (Georgia)") );
|
||||
languageCodeMap.insert( "kk-KZ", i18n("Kazakh (Kazakhstan)") );
|
||||
languageCodeMap.insert( "kl-GL", i18n("Greenlandic (Greenland)") );
|
||||
languageCodeMap.insert( "km-KH", i18n("Khmer (Cambodia)") );
|
||||
languageCodeMap.insert( "kn-IN", i18n("Kannada (India)") );
|
||||
languageCodeMap.insert( "ko-KR", i18n("Korean (South Korea)") );
|
||||
languageCodeMap.insert( "ky-KG", i18n("Kyrgyz (Kyrgyzstan)") );
|
||||
languageCodeMap.insert( "lb-LU", i18n("Luxembourgish (Luxembourg)") );
|
||||
languageCodeMap.insert( "lo-LA", i18n("Lao (Lao PDR)") );
|
||||
languageCodeMap.insert( "lt-LT", i18n("Lithuanian (Lithuania)") );
|
||||
languageCodeMap.insert( "lv-LV", i18n("Latvian (Latvia)") );
|
||||
languageCodeMap.insert( "mi-NZ", i18n("Maori (New Zealand)") );
|
||||
languageCodeMap.insert( "mk-MK", i18n("Macedonian (Macedonia)") );
|
||||
languageCodeMap.insert( "ml-IN", i18n("Malayalam (India)") );
|
||||
languageCodeMap.insert( "mn-CN", i18n("Mongolian (PRC)") );
|
||||
languageCodeMap.insert( "mn-MN", i18n("Mongolian (Mongolia)") );
|
||||
languageCodeMap.insert( "mr-IN", i18n("Marathi (India)") );
|
||||
languageCodeMap.insert( "ms-BN", i18n("Malay (Brunei Darussalam)") );
|
||||
languageCodeMap.insert( "ms-MY", i18n("Malay (Malaysia)") );
|
||||
languageCodeMap.insert( "mt-MT", i18n("Maltese (Malta)") );
|
||||
languageCodeMap.insert( "nb-NO", i18n("Norwegian Bokmål (Norway)") );
|
||||
languageCodeMap.insert( "ne-NP", i18n("Nepali (Nepal)") );
|
||||
languageCodeMap.insert( "nl-BE", i18n("Dutch (Belgium)") );
|
||||
languageCodeMap.insert( "nl-NL", i18n("Dutch (Netherlands)") );
|
||||
languageCodeMap.insert( "nn-NO", i18n("Norwegian Nynorsk (Norway)") );
|
||||
languageCodeMap.insert( "ns-ZA", i18n("Sesotho sa Leboa (South Africa)") );
|
||||
languageCodeMap.insert( "oc-FR", i18n("Occitan (France)") );
|
||||
languageCodeMap.insert( "or-IN", i18n("Oriya (India)") );
|
||||
languageCodeMap.insert( "pa-IN", i18n("Punjabi (India)") );
|
||||
languageCodeMap.insert( "pl-PL", i18n("Polish (Poland)") );
|
||||
languageCodeMap.insert( "ps-AF", i18n("Pashto (Afghanistan)") );
|
||||
languageCodeMap.insert( "pt-BR", i18n("Portuguese (Brazil)") );
|
||||
languageCodeMap.insert( "pt-PT", i18n("Portuguese (Portugal)") );
|
||||
languageCodeMap.insert( "rm-CH", i18n("Romansh (Switzerland)") );
|
||||
languageCodeMap.insert( "ro-RO", i18n("Romanian (Romania)") );
|
||||
languageCodeMap.insert( "ru-RU", i18n("Russian (Russia)") );
|
||||
languageCodeMap.insert( "rw-RW", i18n("Kinyarwanda (Rwanda)") );
|
||||
languageCodeMap.insert( "sa-IN", i18n("Sanskrit (India)") );
|
||||
languageCodeMap.insert( "se-FI", i18n("Sami (Northern, Finland)") );
|
||||
languageCodeMap.insert( "se-NO", i18n("Sami (Northern, Norway)") );
|
||||
languageCodeMap.insert( "se-SE", i18n("Sami (Northern, Sweden)") );
|
||||
languageCodeMap.insert( "si-LK", i18n("Sinhala (Sri Lanka)") );
|
||||
languageCodeMap.insert( "sk-SK", i18n("Slovak (Slovakia)") );
|
||||
languageCodeMap.insert( "sl-SI", i18n("Slovenian (Slovenia)") );
|
||||
languageCodeMap.insert( "sq-AL", i18n("Albanian (Albania)") );
|
||||
languageCodeMap.insert( "sv-FI", i18n("Swedish (Finland)") );
|
||||
languageCodeMap.insert( "sv-SE", i18n("Swedish (Sweden)") );
|
||||
languageCodeMap.insert( "sw-KE", i18n("Swahili (Kenya)") );
|
||||
languageCodeMap.insert( "ta-IN", i18n("Tamil (India)") );
|
||||
languageCodeMap.insert( "te-IN", i18n("Telugu (India)") );
|
||||
languageCodeMap.insert( "th-TH", i18n("Thai (Thailand)") );
|
||||
languageCodeMap.insert( "tk-TM", i18n("Turkmen (Turkmenistan)") );
|
||||
languageCodeMap.insert( "tn-ZA", i18n("Setswana Tswana (South Africa)") );
|
||||
languageCodeMap.insert( "tr-IN", i18n("Urdu (India)") );
|
||||
languageCodeMap.insert( "tr-TR", i18n("Turkish (Turkey)") );
|
||||
languageCodeMap.insert( "tt-RU", i18n("Tatar (Russia)") );
|
||||
languageCodeMap.insert( "ug-CN", i18n("Uighur (PRC)") );
|
||||
languageCodeMap.insert( "uk-UA", i18n("Ukrainian (Ukraine)") );
|
||||
languageCodeMap.insert( "ur-PK", i18n("Urdu (Pakistan)") );
|
||||
languageCodeMap.insert( "vi-VN", i18n("Vietnamese (Vietnam)") );
|
||||
languageCodeMap.insert( "wo-SN", i18n("Wolof (Senegal)") );
|
||||
languageCodeMap.insert( "xh-ZA", i18n("isiXhosa Xhosa (South Africa)") );
|
||||
languageCodeMap.insert( "yo-NG", i18n("Yoruba (Nigeria)") );
|
||||
languageCodeMap.insert( "zh-CN", i18n("Chinese (PRC)") );
|
||||
languageCodeMap.insert( "zh-HK", i18n("Chinese (Hong Kong SAR, PRC)") );
|
||||
languageCodeMap.insert( "zh-MO", i18n("Chinese (Macao SAR)") );
|
||||
languageCodeMap.insert( "zh-SG", i18n("Chinese (Singapore)") );
|
||||
languageCodeMap.insert( "zh-TW", i18n("Chinese (Taiwan)") );
|
||||
languageCodeMap.insert( "zu-ZA", i18n("isiZulu Zulu (South Africa)") );
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
languageCodeMap.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
typedef QMap<QString, QString> LanguageCodeMap;
|
||||
|
||||
LanguageCodeMap languageCodeMap;
|
||||
|
||||
QString currentLanguage;
|
||||
|
||||
uint linesVisible;
|
||||
|
||||
QLabel* titleLabel;
|
||||
|
||||
QToolButton* delValueButton;
|
||||
|
||||
MsgTextEdit* valueEdit;
|
||||
|
||||
KComboBox* languageCB;
|
||||
|
||||
KExiv2::AltLangMap values;
|
||||
};
|
||||
|
||||
AltLangStrEdit::AltLangStrEdit(QWidget* parent)
|
||||
: QWidget(parent), d(new Private)
|
||||
{
|
||||
QGridLayout* const grid = new QGridLayout(this);
|
||||
d->titleLabel = new QLabel(this);
|
||||
d->delValueButton = new QToolButton(this);
|
||||
d->delValueButton->setIcon(SmallIcon("edit-clear"));
|
||||
d->delValueButton->setToolTip(i18n("Remove entry for this language"));
|
||||
d->delValueButton->setEnabled(false);
|
||||
|
||||
d->languageCB = new KComboBox(this);
|
||||
d->languageCB->setSizeAdjustPolicy(KComboBox::AdjustToContents);
|
||||
d->languageCB->setWhatsThis(i18n("Select item language here."));
|
||||
|
||||
d->valueEdit = new MsgTextEdit(this);
|
||||
d->valueEdit->setCheckSpellingEnabled(true);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
grid->setAlignment( Qt::AlignTop );
|
||||
grid->addWidget(d->titleLabel, 0, 0, 1, 1);
|
||||
grid->addWidget(d->languageCB, 0, 2, 1, 1);
|
||||
grid->addWidget(d->delValueButton, 0, 3, 1, 1);
|
||||
grid->addWidget(d->valueEdit, 1, 0, 1,-1);
|
||||
grid->setColumnStretch(1, 10);
|
||||
grid->setMargin(0);
|
||||
grid->setSpacing(KDialog::spacingHint());
|
||||
|
||||
loadLangAltListEntries();
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
connect(d->languageCB, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(slotSelectionChanged()));
|
||||
|
||||
connect(d->delValueButton, SIGNAL(clicked()),
|
||||
this, SLOT(slotDeleteValue()));
|
||||
|
||||
connect(d->valueEdit, SIGNAL(textChanged()),
|
||||
this, SLOT(slotTextChanged()));
|
||||
}
|
||||
|
||||
AltLangStrEdit::~AltLangStrEdit()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QString AltLangStrEdit::currentLanguageCode() const
|
||||
{
|
||||
return d->currentLanguage;
|
||||
}
|
||||
|
||||
void AltLangStrEdit::setCurrentLanguageCode(const QString& lang)
|
||||
{
|
||||
if(d->currentLanguage.isEmpty())
|
||||
{
|
||||
d->currentLanguage = "x-default";
|
||||
}
|
||||
else
|
||||
{
|
||||
d->currentLanguage = lang;
|
||||
}
|
||||
}
|
||||
|
||||
QString AltLangStrEdit::languageCode(int index) const
|
||||
{
|
||||
return d->languageCB->itemText(index);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::setTitle(const QString& title)
|
||||
{
|
||||
d->titleLabel->setText(title);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::setClickMessage(const QString& msg)
|
||||
{
|
||||
d->valueEdit->setClickMessage(msg);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::reset()
|
||||
{
|
||||
setValues(KExiv2::AltLangMap());
|
||||
}
|
||||
|
||||
void AltLangStrEdit::slotDeleteValue()
|
||||
{
|
||||
d->values.remove(d->currentLanguage);
|
||||
setValues(d->values);
|
||||
emit signalValueDeleted(d->currentLanguage);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::slotSelectionChanged()
|
||||
{
|
||||
d->currentLanguage = d->languageCB->currentText();
|
||||
|
||||
// There are bogus signals caused by spell checking, see bug #141663.
|
||||
// so we must block signals here.
|
||||
|
||||
d->valueEdit->blockSignals(true);
|
||||
|
||||
QString langISO3066 = d->currentLanguage;
|
||||
langISO3066.replace('-', '_');
|
||||
d->valueEdit->setSpellCheckingLanguage(langISO3066);
|
||||
|
||||
QString text = d->values.value(d->currentLanguage);
|
||||
d->valueEdit->setText(text);
|
||||
d->delValueButton->setEnabled(!text.isNull());
|
||||
|
||||
d->valueEdit->blockSignals(false);
|
||||
|
||||
d->languageCB->setToolTip(d->languageCodeMap.value(d->currentLanguage));
|
||||
|
||||
emit signalSelectionChanged(d->currentLanguage);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::setValues(const KExiv2::AltLangMap& values)
|
||||
{
|
||||
d->values = values;
|
||||
loadLangAltListEntries();
|
||||
|
||||
d->valueEdit->blockSignals(true);
|
||||
|
||||
QString text = d->values.value(d->currentLanguage);
|
||||
d->valueEdit->setText(text);
|
||||
d->delValueButton->setEnabled(!text.isNull());
|
||||
|
||||
d->valueEdit->blockSignals(false);
|
||||
}
|
||||
|
||||
KExiv2::AltLangMap& AltLangStrEdit::values()
|
||||
{
|
||||
return d->values;
|
||||
}
|
||||
|
||||
void AltLangStrEdit::loadLangAltListEntries()
|
||||
{
|
||||
d->languageCB->blockSignals(true);
|
||||
|
||||
d->languageCB->clear();
|
||||
|
||||
// In first we fill already assigned languages.
|
||||
|
||||
QStringList list = d->values.keys();
|
||||
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
foreach(const QString& item, list)
|
||||
{
|
||||
d->languageCB->addItem(item);
|
||||
d->languageCB->setItemIcon(d->languageCB->count()-1, SmallIcon("dialog-ok"));
|
||||
}
|
||||
|
||||
d->languageCB->insertSeparator(d->languageCB->count());
|
||||
}
|
||||
|
||||
// ...and now, all the rest...
|
||||
|
||||
for (Private::LanguageCodeMap::Iterator it = d->languageCodeMap.begin();
|
||||
it != d->languageCodeMap.end(); ++it)
|
||||
{
|
||||
if (!list.contains(it.key()))
|
||||
{
|
||||
d->languageCB->addItem(it.key());
|
||||
}
|
||||
}
|
||||
|
||||
d->languageCB->setCurrentItem(d->currentLanguage);
|
||||
|
||||
d->languageCB->blockSignals(false);
|
||||
}
|
||||
|
||||
QString AltLangStrEdit::defaultAltLang() const
|
||||
{
|
||||
return d->values.value(QString("x-default"));
|
||||
}
|
||||
|
||||
bool AltLangStrEdit::asDefaultAltLang() const
|
||||
{
|
||||
return !defaultAltLang().isNull();
|
||||
}
|
||||
|
||||
void AltLangStrEdit::slotTextChanged()
|
||||
{
|
||||
QString editedText = d->valueEdit->toPlainText();
|
||||
QString previousText = d->values.value(d->currentLanguage);
|
||||
|
||||
// Special case : if edited and previous strings are empty, do nothing.
|
||||
// See bug #152948.
|
||||
if (editedText.isEmpty() && previousText.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (editedText.isEmpty())
|
||||
{
|
||||
slotDeleteValue();
|
||||
}
|
||||
else if (previousText.isNull())
|
||||
{
|
||||
addCurrent();
|
||||
}
|
||||
else if (editedText != previousText)
|
||||
{
|
||||
// we cannot trust that the text actually changed
|
||||
// (there are bogus signals caused by spell checking, see bug #141663)
|
||||
// so we have to check before marking the metadata as modified.
|
||||
d->values.insert(d->currentLanguage, editedText);
|
||||
emit signalModified(d->currentLanguage, editedText);
|
||||
}
|
||||
}
|
||||
|
||||
void AltLangStrEdit::addCurrent()
|
||||
{
|
||||
QString text = d->valueEdit->toPlainText();
|
||||
|
||||
d->values.insert(d->currentLanguage, text);
|
||||
loadLangAltListEntries();
|
||||
d->delValueButton->setEnabled(true);
|
||||
emit signalValueAdded(d->currentLanguage, text);
|
||||
}
|
||||
|
||||
void AltLangStrEdit::setLinesVisible(uint lines)
|
||||
{
|
||||
d->linesVisible = lines;
|
||||
|
||||
if (d->linesVisible == 0)
|
||||
{
|
||||
d->valueEdit->setFixedHeight(QWIDGETSIZE_MAX); // reset
|
||||
}
|
||||
else
|
||||
{
|
||||
d->valueEdit->setFixedHeight(d->valueEdit->fontMetrics().lineSpacing() * d->linesVisible +
|
||||
d->valueEdit->contentsMargins().top() +
|
||||
d->valueEdit->contentsMargins().bottom() +
|
||||
1 +
|
||||
2*(d->valueEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) +
|
||||
d->valueEdit->style()->pixelMetric(QStyle::PM_FocusFrameVMargin))
|
||||
);
|
||||
}
|
||||
|
||||
// It's not possible to display scrollbar properlly if size is too small
|
||||
if (d->linesVisible < 3)
|
||||
{
|
||||
d->valueEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
||||
}
|
||||
|
||||
uint AltLangStrEdit::linesVisible() const
|
||||
{
|
||||
return d->linesVisible;
|
||||
}
|
||||
|
||||
void AltLangStrEdit::changeEvent(QEvent* e)
|
||||
{
|
||||
if (e->type() == QEvent::FontChange)
|
||||
{
|
||||
setLinesVisible(linesVisible());
|
||||
}
|
||||
|
||||
QWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
115
libkexiv2/libkexiv2/altlangstredit.h
Normal file
115
libkexiv2/libkexiv2/altlangstredit.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-06-15
|
||||
* @brief multi-languages string editor
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef ALTLANGSTREDIT_H
|
||||
#define ALTLANGSTREDIT_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtCore/QString>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
#include "kexiv2.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT AltLangStrEdit : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AltLangStrEdit(QWidget* parent);
|
||||
~AltLangStrEdit();
|
||||
|
||||
void setTitle(const QString& title);
|
||||
void setClickMessage(const QString& msg);
|
||||
|
||||
void setCurrentLanguageCode(const QString& lang);
|
||||
QString currentLanguageCode() const;
|
||||
|
||||
QString languageCode(int index) const;
|
||||
|
||||
void setValues(const KExiv2::AltLangMap& values);
|
||||
KExiv2::AltLangMap& values();
|
||||
|
||||
/** Fix lines visibile in text editor to lines. If zero, do not fix layout to number of lines visible.
|
||||
*/
|
||||
void setLinesVisible(uint lines);
|
||||
uint linesVisible() const;
|
||||
|
||||
QString defaultAltLang() const;
|
||||
bool asDefaultAltLang() const;
|
||||
|
||||
/**
|
||||
* Reset widget, clear all entries
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Ensure that the current language is added to the list of entries,
|
||||
* even if the text is empty.
|
||||
* signalValueAdded() will be emitted.
|
||||
*/
|
||||
void addCurrent();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted when the user changes the text for the current language.
|
||||
*/
|
||||
void signalModified(const QString& lang, const QString& text);
|
||||
/// Emitted when the current language changed
|
||||
void signalSelectionChanged(const QString& lang);
|
||||
/// Emitted when an entry for a new language is added
|
||||
void signalValueAdded(const QString& lang, const QString& text);
|
||||
/// Emitted when the entry for a language is removed.
|
||||
void signalValueDeleted(const QString& lang);
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
void slotTextChanged();
|
||||
void slotSelectionChanged();
|
||||
void slotDeleteValue();
|
||||
|
||||
protected:
|
||||
|
||||
void loadLangAltListEntries();
|
||||
|
||||
virtual void changeEvent(QEvent* e);
|
||||
|
||||
private:
|
||||
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif // ALTLANGSTREDIT_H
|
370
libkexiv2/libkexiv2/countryselector.cpp
Normal file
370
libkexiv2/libkexiv2/countryselector.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-07-07
|
||||
* @brief country selector combo-box.
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "countryselector.h"
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <klocale.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class CountrySelector::Private
|
||||
{
|
||||
public:
|
||||
|
||||
Private()
|
||||
{
|
||||
// We cannot use KLocale::allCountriesList() here because KDE only
|
||||
// support 2 characters country codes. XMP require 3 characters country
|
||||
// following ISO 3166 (http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html)
|
||||
|
||||
// Standard ISO 3166 country codes.
|
||||
|
||||
countryCodeMap.insert( "AFG", i18n("Afghanistan") );
|
||||
countryCodeMap.insert( "ALB", i18n("Albania") );
|
||||
countryCodeMap.insert( "DZA", i18n("Algeria") );
|
||||
countryCodeMap.insert( "ASM", i18n("American Samoa") );
|
||||
countryCodeMap.insert( "AND", i18n("Andorra") );
|
||||
countryCodeMap.insert( "AGO", i18n("Angola") );
|
||||
countryCodeMap.insert( "AIA", i18n("Anguilla") );
|
||||
countryCodeMap.insert( "AGO", i18n("Angola") );
|
||||
countryCodeMap.insert( "ATA", i18n("Antarctica") );
|
||||
countryCodeMap.insert( "ATG", i18n("Antigua and Barbuda") );
|
||||
countryCodeMap.insert( "ARG", i18n("Argentina") );
|
||||
countryCodeMap.insert( "ARM", i18n("Armenia") );
|
||||
countryCodeMap.insert( "ABW", i18n("Aruba") );
|
||||
countryCodeMap.insert( "AUS", i18n("Australia") );
|
||||
countryCodeMap.insert( "AUT", i18n("Austria") );
|
||||
countryCodeMap.insert( "AZE", i18n("Azerbaijan") );
|
||||
countryCodeMap.insert( "BHS", i18n("Bahamas") );
|
||||
countryCodeMap.insert( "BHR", i18n("Bahrain") );
|
||||
countryCodeMap.insert( "BGD", i18n("Bangladesh") );
|
||||
countryCodeMap.insert( "BRB", i18n("Barbados") );
|
||||
countryCodeMap.insert( "BLR", i18n("Belarus") );
|
||||
countryCodeMap.insert( "BEL", i18n("Belgium") );
|
||||
countryCodeMap.insert( "BLZ", i18n("Belize") );
|
||||
countryCodeMap.insert( "BEN", i18n("Benin") );
|
||||
countryCodeMap.insert( "BMU", i18n("Bermuda") );
|
||||
countryCodeMap.insert( "BTN", i18n("Bhutan") );
|
||||
countryCodeMap.insert( "BOL", i18n("Bolivia") );
|
||||
countryCodeMap.insert( "BIH", i18n("Bosnia and Herzegovina") );
|
||||
countryCodeMap.insert( "BWA", i18n("Botswana") );
|
||||
countryCodeMap.insert( "BVT", i18n("Bouvet Island") );
|
||||
countryCodeMap.insert( "BRA", i18n("Brazil") );
|
||||
countryCodeMap.insert( "IOT", i18n("British Indian Ocean Territory") );
|
||||
countryCodeMap.insert( "VGB", i18n("British Virgin Islands") );
|
||||
countryCodeMap.insert( "BRN", i18n("Brunei Darussalam") );
|
||||
countryCodeMap.insert( "BGR", i18n("Bulgaria") );
|
||||
countryCodeMap.insert( "BFA", i18n("Burkina Faso") );
|
||||
countryCodeMap.insert( "BDI", i18n("Burundi") );
|
||||
countryCodeMap.insert( "KHM", i18n("Cambodia") );
|
||||
countryCodeMap.insert( "CMR", i18n("Cameroon") );
|
||||
countryCodeMap.insert( "CAN", i18n("Canada") );
|
||||
countryCodeMap.insert( "CPV", i18n("Cape Verde") );
|
||||
countryCodeMap.insert( "CYM", i18n("Cayman Islands") );
|
||||
countryCodeMap.insert( "CAF", i18n("Central African Republic") );
|
||||
countryCodeMap.insert( "TCD", i18n("Chad") );
|
||||
countryCodeMap.insert( "CHL", i18n("Chile") );
|
||||
countryCodeMap.insert( "CHN", i18n("China") );
|
||||
countryCodeMap.insert( "CXR", i18n("Christmas Island ") );
|
||||
countryCodeMap.insert( "CCK", i18n("Cocos Islands") );
|
||||
countryCodeMap.insert( "COL", i18n("Colombia") );
|
||||
countryCodeMap.insert( "COM", i18n("Comoros") );
|
||||
countryCodeMap.insert( "COD", i18n("Zaire") );
|
||||
countryCodeMap.insert( "COG", i18n("Congo") );
|
||||
countryCodeMap.insert( "COK", i18n("Cook Islands") );
|
||||
countryCodeMap.insert( "CRI", i18n("Costa Rica") );
|
||||
countryCodeMap.insert( "CIV", i18n("Ivory Coast") );
|
||||
countryCodeMap.insert( "CUB", i18n("Cuba") );
|
||||
countryCodeMap.insert( "CYP", i18n("Cyprus") );
|
||||
countryCodeMap.insert( "CZE", i18n("Czech Republic") );
|
||||
countryCodeMap.insert( "DNK", i18n("Denmark") );
|
||||
countryCodeMap.insert( "DJI", i18n("Djibouti") );
|
||||
countryCodeMap.insert( "DMA", i18n("Dominica") );
|
||||
countryCodeMap.insert( "DOM", i18n("Dominican Republic") );
|
||||
countryCodeMap.insert( "ECU", i18n("Ecuador") );
|
||||
countryCodeMap.insert( "EGY", i18n("Egypt") );
|
||||
countryCodeMap.insert( "SLV", i18n("El Salvador") );
|
||||
countryCodeMap.insert( "GNQ", i18n("Equatorial Guinea") );
|
||||
countryCodeMap.insert( "ERI", i18n("Eritrea") );
|
||||
countryCodeMap.insert( "EST", i18n("Estonia") );
|
||||
countryCodeMap.insert( "ETH", i18n("Ethiopia") );
|
||||
countryCodeMap.insert( "FRO", i18n("Faeroe Islands") );
|
||||
countryCodeMap.insert( "FLK", i18n("Falkland Islands") );
|
||||
countryCodeMap.insert( "FJI", i18n("Fiji Islands") );
|
||||
countryCodeMap.insert( "FIN", i18n("Finland") );
|
||||
countryCodeMap.insert( "FRA", i18n("France") );
|
||||
countryCodeMap.insert( "GUF", i18n("French Guiana") );
|
||||
countryCodeMap.insert( "PYF", i18n("French Polynesia") );
|
||||
countryCodeMap.insert( "ATF", i18n("French Southern Territories") );
|
||||
countryCodeMap.insert( "GAB", i18n("Gabon") );
|
||||
countryCodeMap.insert( "GMB", i18n("Gambia") );
|
||||
countryCodeMap.insert( "GEO", i18n("Georgia") );
|
||||
countryCodeMap.insert( "DEU", i18n("Germany") );
|
||||
countryCodeMap.insert( "GHA", i18n("Ghana") );
|
||||
countryCodeMap.insert( "GIB", i18n("Gibraltar") );
|
||||
countryCodeMap.insert( "GRC", i18n("Greece") );
|
||||
countryCodeMap.insert( "GRL", i18n("Greenland") );
|
||||
countryCodeMap.insert( "GRD", i18n("Grenada") );
|
||||
countryCodeMap.insert( "GLP", i18n("Guadaloupe") );
|
||||
countryCodeMap.insert( "GUM", i18n("Guam") );
|
||||
countryCodeMap.insert( "GTM", i18n("Guatemala") );
|
||||
countryCodeMap.insert( "GIN", i18n("Guinea") );
|
||||
countryCodeMap.insert( "GNB", i18n("Guinea-Bissau") );
|
||||
countryCodeMap.insert( "GUY", i18n("Guyana") );
|
||||
countryCodeMap.insert( "HTI", i18n("Haiti") );
|
||||
countryCodeMap.insert( "HMD", i18n("Heard and McDonald Islands") );
|
||||
countryCodeMap.insert( "VAT", i18n("Vatican") );
|
||||
countryCodeMap.insert( "HND", i18n("Honduras") );
|
||||
countryCodeMap.insert( "HKG", i18n("Hong Kong") );
|
||||
countryCodeMap.insert( "HRV", i18n("Croatia") );
|
||||
countryCodeMap.insert( "HUN", i18n("Hungary") );
|
||||
countryCodeMap.insert( "ISL", i18n("Iceland") );
|
||||
countryCodeMap.insert( "IND", i18n("India") );
|
||||
countryCodeMap.insert( "IDN", i18n("Indonesia") );
|
||||
countryCodeMap.insert( "IRN", i18n("Iran") );
|
||||
countryCodeMap.insert( "IRQ", i18n("Iraq") );
|
||||
countryCodeMap.insert( "IRL", i18n("Ireland") );
|
||||
countryCodeMap.insert( "ISR", i18n("Israel") );
|
||||
countryCodeMap.insert( "ITA", i18n("Italy") );
|
||||
countryCodeMap.insert( "JAM", i18n("Jamaica") );
|
||||
countryCodeMap.insert( "JPN", i18n("Japan") );
|
||||
countryCodeMap.insert( "JOR", i18n("Jordan") );
|
||||
countryCodeMap.insert( "KAZ", i18n("Kazakhstan") );
|
||||
countryCodeMap.insert( "KEN", i18n("Kenya") );
|
||||
countryCodeMap.insert( "KIR", i18n("Kiribati") );
|
||||
countryCodeMap.insert( "PRK", i18n("North-Korea") );
|
||||
countryCodeMap.insert( "KOR", i18n("South-Korea") );
|
||||
countryCodeMap.insert( "KWT", i18n("Kuwait") );
|
||||
countryCodeMap.insert( "KGZ", i18n("Kyrgyz Republic") );
|
||||
countryCodeMap.insert( "LAO", i18n("Lao") );
|
||||
countryCodeMap.insert( "LVA", i18n("Latvia") );
|
||||
countryCodeMap.insert( "LBN", i18n("Lebanon") );
|
||||
countryCodeMap.insert( "LSO", i18n("Lesotho") );
|
||||
countryCodeMap.insert( "LBR", i18n("Liberia") );
|
||||
countryCodeMap.insert( "LBY", i18n("Libyan Arab Jamahiriya") );
|
||||
countryCodeMap.insert( "LIE", i18n("Liechtenstein") );
|
||||
countryCodeMap.insert( "LTU", i18n("Lithuania") );
|
||||
countryCodeMap.insert( "LUX", i18n("Luxembourg") );
|
||||
countryCodeMap.insert( "MAC", i18n("Macao") );
|
||||
countryCodeMap.insert( "MKD", i18n("Macedonia") );
|
||||
countryCodeMap.insert( "MDG", i18n("Madagascar") );
|
||||
countryCodeMap.insert( "MWI", i18n("Malawi") );
|
||||
countryCodeMap.insert( "MYS", i18n("Malaysia") );
|
||||
countryCodeMap.insert( "MDV", i18n("Maldives") );
|
||||
countryCodeMap.insert( "MLI", i18n("Mali") );
|
||||
countryCodeMap.insert( "MLT", i18n("Malta") );
|
||||
countryCodeMap.insert( "MHL", i18n("Marshall Islands") );
|
||||
countryCodeMap.insert( "MTQ", i18n("Martinique") );
|
||||
countryCodeMap.insert( "MRT", i18n("Mauritania") );
|
||||
countryCodeMap.insert( "MUS", i18n("Mauritius") );
|
||||
countryCodeMap.insert( "MYT", i18n("Mayotte") );
|
||||
countryCodeMap.insert( "MEX", i18n("Mexico") );
|
||||
countryCodeMap.insert( "FSM", i18n("Micronesia") );
|
||||
countryCodeMap.insert( "MDA", i18n("Moldova") );
|
||||
countryCodeMap.insert( "MCO", i18n("Monaco") );
|
||||
countryCodeMap.insert( "MNG", i18n("Mongolia") );
|
||||
countryCodeMap.insert( "MSR", i18n("Montserrat") );
|
||||
countryCodeMap.insert( "MAR", i18n("Morocco") );
|
||||
countryCodeMap.insert( "MOZ", i18n("Mozambique") );
|
||||
countryCodeMap.insert( "MMR", i18n("Myanmar") );
|
||||
countryCodeMap.insert( "NAM", i18n("Namibia") );
|
||||
countryCodeMap.insert( "NRU", i18n("Nauru") );
|
||||
countryCodeMap.insert( "NPL", i18n("Nepal") );
|
||||
countryCodeMap.insert( "ANT", i18n("Netherlands Antilles") );
|
||||
countryCodeMap.insert( "NLD", i18n("Netherlands") );
|
||||
countryCodeMap.insert( "NCL", i18n("New Caledonia") );
|
||||
countryCodeMap.insert( "NZL", i18n("New Zealand") );
|
||||
countryCodeMap.insert( "NIC", i18n("Nicaragua") );
|
||||
countryCodeMap.insert( "NER", i18n("Niger") );
|
||||
countryCodeMap.insert( "NGA", i18n("Nigeria") );
|
||||
countryCodeMap.insert( "NIU", i18n("Niue") );
|
||||
countryCodeMap.insert( "NFK", i18n("Norfolk Island") );
|
||||
countryCodeMap.insert( "MNP", i18n("Northern Mariana Islands") );
|
||||
countryCodeMap.insert( "NOR", i18n("Norway") );
|
||||
countryCodeMap.insert( "OMN", i18n("Oman") );
|
||||
countryCodeMap.insert( "PAK", i18n("Pakistan") );
|
||||
countryCodeMap.insert( "PLW", i18n("Palau") );
|
||||
countryCodeMap.insert( "PSE", i18n("Palestinian Territory") );
|
||||
countryCodeMap.insert( "PAN", i18n("Panama") );
|
||||
countryCodeMap.insert( "PNG", i18n("Papua New Guinea") );
|
||||
countryCodeMap.insert( "PRY", i18n("Paraguay") );
|
||||
countryCodeMap.insert( "PER", i18n("Peru") );
|
||||
countryCodeMap.insert( "PHL", i18n("Philippines") );
|
||||
countryCodeMap.insert( "PCN", i18n("Pitcairn Island") );
|
||||
countryCodeMap.insert( "POL", i18n("Poland") );
|
||||
countryCodeMap.insert( "PRT", i18n("Portugal") );
|
||||
countryCodeMap.insert( "PRI", i18n("Puerto Rico") );
|
||||
countryCodeMap.insert( "QAT", i18n("Qatar") );
|
||||
countryCodeMap.insert( "REU", i18n("Reunion") );
|
||||
countryCodeMap.insert( "ROU", i18n("Romania") );
|
||||
countryCodeMap.insert( "RUS", i18n("Russian Federation") );
|
||||
countryCodeMap.insert( "RWA", i18n("Rwanda") );
|
||||
countryCodeMap.insert( "SHN", i18n("St. Helena") );
|
||||
countryCodeMap.insert( "KNA", i18n("St. Kitts and Nevis") );
|
||||
countryCodeMap.insert( "LCA", i18n("St. Lucia") );
|
||||
countryCodeMap.insert( "SPM", i18n("St. Pierre and Miquelon") );
|
||||
countryCodeMap.insert( "VCT", i18n("St. Vincent and the Grenadines") );
|
||||
countryCodeMap.insert( "WSM", i18n("Samoa") );
|
||||
countryCodeMap.insert( "SMR", i18n("San Marino") );
|
||||
countryCodeMap.insert( "STP", i18n("Sao Tome and Principe") );
|
||||
countryCodeMap.insert( "SAU", i18n("Saudi Arabia") );
|
||||
countryCodeMap.insert( "SEN", i18n("Senegal") );
|
||||
countryCodeMap.insert( "SCG", i18n("Serbia and Montenegro") );
|
||||
countryCodeMap.insert( "SYC", i18n("Seychelles") );
|
||||
countryCodeMap.insert( "SLE", i18n("Sierra Leone") );
|
||||
countryCodeMap.insert( "SGP", i18n("Singapore") );
|
||||
countryCodeMap.insert( "SVK", i18n("Slovakia") );
|
||||
countryCodeMap.insert( "SVN", i18n("Slovenia") );
|
||||
countryCodeMap.insert( "SLB", i18n("Solomon Islands") );
|
||||
countryCodeMap.insert( "SOM", i18n("Somalia") );
|
||||
countryCodeMap.insert( "ZAF", i18n("South Africa") );
|
||||
countryCodeMap.insert( "SGS", i18n("South Georgia and the South Sandwich Islands") );
|
||||
countryCodeMap.insert( "ESP", i18n("Spain") );
|
||||
countryCodeMap.insert( "LKA", i18n("Sri Lanka") );
|
||||
countryCodeMap.insert( "SDN", i18n("Sudan") );
|
||||
countryCodeMap.insert( "SUR", i18n("Suriname") );
|
||||
countryCodeMap.insert( "SJM", i18n("Svalbard & Jan Mayen Islands") );
|
||||
countryCodeMap.insert( "SWZ", i18n("Swaziland") );
|
||||
countryCodeMap.insert( "SWE", i18n("Sweden") );
|
||||
countryCodeMap.insert( "CHE", i18n("Switzerland") );
|
||||
countryCodeMap.insert( "SYR", i18n("Syrian Arab Republic") );
|
||||
countryCodeMap.insert( "TWN", i18n("Taiwan") );
|
||||
countryCodeMap.insert( "TJK", i18n("Tajikistan") );
|
||||
countryCodeMap.insert( "TZA", i18n("Tanzania") );
|
||||
countryCodeMap.insert( "THA", i18n("Thailand") );
|
||||
countryCodeMap.insert( "TLS", i18n("Timor-Leste") );
|
||||
countryCodeMap.insert( "TGO", i18n("Togo") );
|
||||
countryCodeMap.insert( "TKL", i18n("Tokelau Islands") );
|
||||
countryCodeMap.insert( "TON", i18n("Tonga") );
|
||||
countryCodeMap.insert( "TTO", i18n("Trinidad and Tobago") );
|
||||
countryCodeMap.insert( "TUN", i18n("Tunisia") );
|
||||
countryCodeMap.insert( "TUR", i18n("Turkey") );
|
||||
countryCodeMap.insert( "TKM", i18n("Turkmenistan") );
|
||||
countryCodeMap.insert( "TCA", i18n("Turks and Caicos Islands") );
|
||||
countryCodeMap.insert( "TUV", i18n("Tuvalu") );
|
||||
countryCodeMap.insert( "VIR", i18n("US Virgin Islands") );
|
||||
countryCodeMap.insert( "UGA", i18n("Uganda") );
|
||||
countryCodeMap.insert( "UKR", i18n("Ukraine") );
|
||||
countryCodeMap.insert( "ARE", i18n("United Arab Emirates") );
|
||||
countryCodeMap.insert( "GBR", i18n("United Kingdom") );
|
||||
countryCodeMap.insert( "UMI", i18n("United States Minor Outlying Islands") );
|
||||
countryCodeMap.insert( "USA", i18n("United States of America") );
|
||||
countryCodeMap.insert( "URY", i18n("Uruguay, Eastern Republic of") );
|
||||
countryCodeMap.insert( "UZB", i18n("Uzbekistan") );
|
||||
countryCodeMap.insert( "VUT", i18n("Vanuatu") );
|
||||
countryCodeMap.insert( "VEN", i18n("Venezuela") );
|
||||
countryCodeMap.insert( "VNM", i18n("Viet Nam") );
|
||||
countryCodeMap.insert( "WLF", i18n("Wallis and Futuna Islands ") );
|
||||
countryCodeMap.insert( "ESH", i18n("Western Sahara") );
|
||||
countryCodeMap.insert( "YEM", i18n("Yemen") );
|
||||
countryCodeMap.insert( "ZMB", i18n("Zambia") );
|
||||
countryCodeMap.insert( "ZWE", i18n("Zimbabwe") );
|
||||
|
||||
// Supplemental IPTC/IIM country codes.
|
||||
|
||||
countryCodeMap.insert( "XUN", i18n("United Nations") );
|
||||
countryCodeMap.insert( "XEU", i18n("European Union") );
|
||||
countryCodeMap.insert( "XSP", i18n("Space") );
|
||||
countryCodeMap.insert( "XSE", i18n("At Sea") );
|
||||
countryCodeMap.insert( "XIF", i18n("In Flight") );
|
||||
countryCodeMap.insert( "XEN", i18n("England") );
|
||||
countryCodeMap.insert( "XSC", i18n("Scotland") );
|
||||
countryCodeMap.insert( "XNI", i18n("Northern Ireland") );
|
||||
countryCodeMap.insert( "XWA", i18n("Wales") );
|
||||
countryCodeMap.insert( "PSE", i18n("Palestine") );
|
||||
countryCodeMap.insert( "GZA", i18n("Gaza") );
|
||||
countryCodeMap.insert( "JRO", i18n("Jericho") );
|
||||
}
|
||||
|
||||
typedef QMap<QString, QString> CountryCodeMap;
|
||||
|
||||
CountryCodeMap countryCodeMap;
|
||||
};
|
||||
|
||||
CountrySelector::CountrySelector(QWidget* parent)
|
||||
: KComboBox(parent), d(new Private)
|
||||
{
|
||||
for (Private::CountryCodeMap::Iterator it = d->countryCodeMap.begin();
|
||||
it != d->countryCodeMap.end(); ++it)
|
||||
{
|
||||
addItem(QString("%1 - %2").arg(it.key()).arg(it.value()));
|
||||
}
|
||||
|
||||
model()->sort(0);
|
||||
|
||||
insertSeparator(count());
|
||||
addItem(i18nc("Unknown country", "Unknown"));
|
||||
}
|
||||
|
||||
CountrySelector::~CountrySelector()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void CountrySelector::setCountry(const QString& countryCode)
|
||||
{
|
||||
// NOTE: if countryCode is empty or do not matches code map, unknow is selected from the list.
|
||||
|
||||
int id = count()-1;
|
||||
for (int i = 0 ; i < d->countryCodeMap.count() ; i++)
|
||||
{
|
||||
if (itemText(i).left(3) == countryCode)
|
||||
{
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setCurrentIndex(id);
|
||||
|
||||
kDebug() << count() << " :: " << id;
|
||||
}
|
||||
|
||||
bool CountrySelector::country(QString& countryCode, QString& countryName)
|
||||
{
|
||||
// Unknow is selected ?
|
||||
if (currentIndex() == count()-1)
|
||||
return false;
|
||||
|
||||
countryName = currentText().mid(6);
|
||||
countryCode = currentText().left(3);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString CountrySelector::countryForCode(const QString& countryCode)
|
||||
{
|
||||
Private priv;
|
||||
return (priv.countryCodeMap[countryCode]);
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
66
libkexiv2/libkexiv2/countryselector.h
Normal file
66
libkexiv2/libkexiv2/countryselector.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-07-07
|
||||
* @brief country selector combo-box.
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef COUNTRY_SELECTOR_H
|
||||
#define COUNTRY_SELECTOR_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QWidget>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <kcombobox.h>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT CountrySelector : public KComboBox
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
CountrySelector(QWidget* parent);
|
||||
~CountrySelector();
|
||||
|
||||
void setCountry(const QString& countryCode);
|
||||
bool country(QString& countryCode, QString& countryName);
|
||||
|
||||
static QString countryForCode(const QString& countryCode);
|
||||
|
||||
private:
|
||||
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif // COUNTRY_SELECTOR_H
|
544
libkexiv2/libkexiv2/kexiv2.cpp
Normal file
544
libkexiv2/libkexiv2/kexiv2.cpp
Normal file
|
@ -0,0 +1,544 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-09-15
|
||||
* @brief Exiv2 library interface for KDE
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2013 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2.h"
|
||||
#include "kexiv2_p.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "version.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
KExiv2Data::KExiv2Data()
|
||||
: d(0)
|
||||
{
|
||||
}
|
||||
|
||||
KExiv2Data::KExiv2Data(const KExiv2Data& other)
|
||||
{
|
||||
d = other.d;
|
||||
}
|
||||
|
||||
KExiv2Data::~KExiv2Data()
|
||||
{
|
||||
}
|
||||
|
||||
KExiv2Data& KExiv2Data::operator=(const KExiv2Data& other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
KExiv2::KExiv2()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KExiv2::KExiv2(const KExiv2& metadata)
|
||||
: d(new Private)
|
||||
{
|
||||
d->copyPrivateData(metadata.d);
|
||||
}
|
||||
|
||||
KExiv2::KExiv2(const KExiv2Data& data)
|
||||
: d(new Private)
|
||||
{
|
||||
setData(data);
|
||||
}
|
||||
|
||||
KExiv2::KExiv2(const QString& filePath)
|
||||
: d(new Private)
|
||||
{
|
||||
load(filePath);
|
||||
}
|
||||
|
||||
KExiv2::~KExiv2()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
KExiv2& KExiv2::operator=(const KExiv2& metadata)
|
||||
{
|
||||
d->copyPrivateData(metadata.d);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-- Statics methods ----------------------------------------------
|
||||
|
||||
bool KExiv2::initializeExiv2()
|
||||
{
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
if (!Exiv2::XmpParser::initialize())
|
||||
return false;
|
||||
|
||||
registerXmpNameSpace(QString("http://ns.adobe.com/lightroom/1.0/"), QString("lr"));
|
||||
registerXmpNameSpace(QString("http://www.digikam.org/ns/kipi/1.0/"), QString("kipi"));
|
||||
registerXmpNameSpace(QString("http://ns.microsoft.com/photo/1.2/"), QString("MP"));
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KExiv2::cleanupExiv2()
|
||||
{
|
||||
// Fix memory leak if Exiv2 support XMP.
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
unregisterXmpNameSpace(QString("http://ns.adobe.com/lightroom/1.0/"));
|
||||
unregisterXmpNameSpace(QString("http://www.digikam.org/ns/kipi/1.0/"));
|
||||
unregisterXmpNameSpace(QString("http://ns.microsoft.com/photo/1.2/"));
|
||||
|
||||
Exiv2::XmpParser::terminate();
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KExiv2::supportXmp()
|
||||
{
|
||||
#ifdef _XMP_SUPPORT_
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // _XMP_SUPPORT_
|
||||
}
|
||||
|
||||
bool KExiv2::supportMetadataWritting(const QString& typeMime)
|
||||
{
|
||||
if (typeMime == QString("image/jpeg"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (typeMime == QString("image/tiff"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (typeMime == QString("image/png"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (typeMime == QString("image/jp2"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (typeMime == QString("image/x-raw"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (typeMime == QString("image/pgf"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString KExiv2::Exiv2Version()
|
||||
{
|
||||
// Since 0.14.0 release, we can extract run-time version of Exiv2.
|
||||
// else we return make version.
|
||||
|
||||
return QString(Exiv2::version());
|
||||
}
|
||||
|
||||
QString KExiv2::version()
|
||||
{
|
||||
return QString(kexiv2_version);
|
||||
}
|
||||
|
||||
QString KExiv2::sidecarFilePathForFile(const QString& path)
|
||||
{
|
||||
QString ret;
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
ret = path + QString(".xmp");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
KUrl KExiv2::sidecarUrl(const KUrl& url)
|
||||
{
|
||||
QString sidecarPath = sidecarFilePathForFile(url.path());
|
||||
KUrl sidecarUrl(url);
|
||||
sidecarUrl.setPath(sidecarPath);
|
||||
return sidecarUrl;
|
||||
}
|
||||
|
||||
KUrl KExiv2::sidecarUrl(const QString& path)
|
||||
{
|
||||
return KUrl::fromPath(sidecarFilePathForFile(path));
|
||||
}
|
||||
|
||||
QString KExiv2::sidecarPath(const QString& path)
|
||||
{
|
||||
return sidecarFilePathForFile(path);
|
||||
}
|
||||
|
||||
bool KExiv2::hasSidecar(const QString& path)
|
||||
{
|
||||
return QFileInfo(sidecarFilePathForFile(path)).exists();
|
||||
}
|
||||
|
||||
//-- General methods ----------------------------------------------
|
||||
|
||||
KExiv2Data KExiv2::data() const
|
||||
{
|
||||
KExiv2Data data;
|
||||
data.d = d->data;
|
||||
return data;
|
||||
}
|
||||
|
||||
void KExiv2::setData(const KExiv2Data& data)
|
||||
{
|
||||
if (data.d)
|
||||
{
|
||||
d->data = data.d;
|
||||
}
|
||||
else
|
||||
{
|
||||
// KExiv2Data can have a null pointer,
|
||||
// but we never want a null pointer in Private.
|
||||
d->data->clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool KExiv2::loadFromData(const QByteArray& imgData) const
|
||||
{
|
||||
if (imgData.isEmpty())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size());
|
||||
|
||||
d->filePath.clear();
|
||||
image->readMetadata();
|
||||
|
||||
// Size and mimetype ---------------------------------
|
||||
|
||||
d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight());
|
||||
d->mimeType = image->mimeType().c_str();
|
||||
|
||||
// Image comments ---------------------------------
|
||||
|
||||
d->imageComments() = image->comment();
|
||||
|
||||
// Exif metadata ----------------------------------
|
||||
|
||||
d->exifMetadata() = image->exifData();
|
||||
|
||||
// Iptc metadata ----------------------------------
|
||||
|
||||
d->iptcMetadata() = image->iptcData();
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
// Xmp metadata -----------------------------------
|
||||
|
||||
d->xmpMetadata() = image->xmpData();
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot load metadata using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::load(const QString& filePath) const
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
d->filePath = filePath;
|
||||
bool hasLoaded = false;
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image;
|
||||
|
||||
image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(filePath)));
|
||||
|
||||
image->readMetadata();
|
||||
|
||||
// Size and mimetype ---------------------------------
|
||||
|
||||
d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight());
|
||||
d->mimeType = image->mimeType().c_str();
|
||||
|
||||
// Image comments ---------------------------------
|
||||
|
||||
d->imageComments() = image->comment();
|
||||
|
||||
// Exif metadata ----------------------------------
|
||||
|
||||
d->exifMetadata() = image->exifData();
|
||||
|
||||
// Iptc metadata ----------------------------------
|
||||
|
||||
d->iptcMetadata() = image->iptcData();
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
// Xmp metadata -----------------------------------
|
||||
d->xmpMetadata() = image->xmpData();
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
hasLoaded = true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot load metadata from file ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
try
|
||||
{
|
||||
if (d->useXMPSidecar4Reading)
|
||||
{
|
||||
QString xmpSidecarPath = sidecarFilePathForFile(filePath);
|
||||
QFileInfo xmpSidecarFileInfo(xmpSidecarPath);
|
||||
|
||||
Exiv2::Image::AutoPtr xmpsidecar;
|
||||
if (xmpSidecarFileInfo.exists() && xmpSidecarFileInfo.isReadable())
|
||||
{
|
||||
// Read sidecar data
|
||||
xmpsidecar = Exiv2::ImageFactory::open((const char*)QFile::encodeName(xmpSidecarPath));
|
||||
xmpsidecar->readMetadata();
|
||||
|
||||
// Merge
|
||||
d->loadSidecarData(xmpsidecar);
|
||||
hasLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot load XMP sidecar", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return hasLoaded;
|
||||
}
|
||||
|
||||
bool KExiv2::save(const QString& imageFilePath) const
|
||||
{
|
||||
// If our image is really a symlink, we should follow the symlink so that
|
||||
// when we delete the file and rewrite it, we are honoring the symlink
|
||||
// (rather than just deleting it and putting a file there).
|
||||
|
||||
// However, this may be surprising to the user when they are writing sidecar
|
||||
// files. They might expect them to show up where the symlink is. So, we
|
||||
// shouldn't follow the link when figuring out what the filename for the
|
||||
// sidecar should be.
|
||||
|
||||
// Note, we are not yet handling the case where the sidecar itself is a
|
||||
// symlink.
|
||||
QString regularFilePath = imageFilePath; // imageFilePath might be a
|
||||
// symlink. Below we will change
|
||||
// regularFile to the pointed to
|
||||
// file if so.
|
||||
QFileInfo givenFileInfo(imageFilePath);
|
||||
if (givenFileInfo.isSymLink())
|
||||
{
|
||||
kDebug() << "filePath" << imageFilePath << "is a symlink."
|
||||
<< "Using target" << givenFileInfo.canonicalPath();
|
||||
|
||||
regularFilePath = givenFileInfo.canonicalPath();// Walk all the symlinks
|
||||
}
|
||||
|
||||
// NOTE: see B.K.O #137770 & #138540 : never touch the file if is read only.
|
||||
QFileInfo finfo(regularFilePath);
|
||||
QFileInfo dinfo(finfo.path());
|
||||
if (!dinfo.isWritable())
|
||||
{
|
||||
kDebug() << "Dir '" << dinfo.filePath() << "' is read-only. Metadata not saved.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool writeToFile = false;
|
||||
bool writeToSidecar = false;
|
||||
bool writeToSidecarIfFileNotPossible = false;
|
||||
bool writtenToFile = false;
|
||||
bool writtenToSidecar = false;
|
||||
|
||||
kDebug() << "KExiv2::metadataWritingMode" << d->metadataWritingMode;
|
||||
|
||||
switch(d->metadataWritingMode)
|
||||
{
|
||||
case WRITETOSIDECARONLY:
|
||||
writeToSidecar = true;
|
||||
break;
|
||||
case WRITETOIMAGEONLY:
|
||||
writeToFile = true;
|
||||
break;
|
||||
case WRITETOSIDECARANDIMAGE:
|
||||
writeToFile = true;
|
||||
writeToSidecar = true;
|
||||
break;
|
||||
case WRITETOSIDECARONLY4READONLYFILES:
|
||||
writeToFile = true;
|
||||
writeToSidecarIfFileNotPossible = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (writeToFile)
|
||||
{
|
||||
kDebug() << "Will write Metadata to file" << finfo.fileName();
|
||||
writtenToFile = d->saveToFile(finfo);
|
||||
if (writeToFile)
|
||||
{
|
||||
kDebug() << "Metadata for file" << finfo.fileName() << "written to file.";
|
||||
}
|
||||
}
|
||||
|
||||
if (writeToSidecar || (writeToSidecarIfFileNotPossible && !writtenToFile))
|
||||
{
|
||||
kDebug() << "Will write XMP sidecar for file" << givenFileInfo.fileName();
|
||||
writtenToSidecar = d->saveToXMPSidecar(imageFilePath);
|
||||
if (writtenToSidecar)
|
||||
{
|
||||
kDebug() << "Metadata for file '" << givenFileInfo.fileName() << "' written to XMP sidecar.";
|
||||
}
|
||||
}
|
||||
|
||||
return writtenToFile || writtenToSidecar;
|
||||
}
|
||||
|
||||
bool KExiv2::applyChanges() const
|
||||
{
|
||||
if (d->filePath.isEmpty())
|
||||
{
|
||||
kDebug() << "Failed to apply changes: file path is empty!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return save(d->filePath);
|
||||
}
|
||||
|
||||
bool KExiv2::isEmpty() const
|
||||
{
|
||||
if (!hasComments() && !hasExif() && !hasIptc() && !hasXmp())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KExiv2::setFilePath(const QString& path)
|
||||
{
|
||||
d->filePath = path;
|
||||
}
|
||||
|
||||
QString KExiv2::getFilePath() const
|
||||
{
|
||||
return d->filePath;
|
||||
}
|
||||
|
||||
QSize KExiv2::getPixelSize() const
|
||||
{
|
||||
return d->pixelSize;
|
||||
}
|
||||
|
||||
QString KExiv2::getMimeType() const
|
||||
{
|
||||
return d->mimeType;
|
||||
}
|
||||
|
||||
void KExiv2::setWriteRawFiles(const bool on)
|
||||
{
|
||||
d->writeRawFiles = on;
|
||||
}
|
||||
|
||||
bool KExiv2::writeRawFiles() const
|
||||
{
|
||||
return d->writeRawFiles;
|
||||
}
|
||||
|
||||
void KExiv2::setUseXMPSidecar4Reading(const bool on)
|
||||
{
|
||||
d->useXMPSidecar4Reading = on;
|
||||
}
|
||||
|
||||
bool KExiv2::useXMPSidecar4Reading() const
|
||||
{
|
||||
return d->useXMPSidecar4Reading;
|
||||
}
|
||||
|
||||
void KExiv2::setMetadataWritingMode(const int mode)
|
||||
{
|
||||
d->metadataWritingMode = mode;
|
||||
}
|
||||
|
||||
int KExiv2::metadataWritingMode() const
|
||||
{
|
||||
return d->metadataWritingMode;
|
||||
}
|
||||
|
||||
void KExiv2::setUpdateFileTimeStamp(bool on)
|
||||
{
|
||||
d->updateFileTimeStamp = on;
|
||||
}
|
||||
|
||||
bool KExiv2::updateFileTimeStamp() const
|
||||
{
|
||||
return d->updateFileTimeStamp;
|
||||
}
|
||||
|
||||
bool KExiv2::setProgramId(bool /*on*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // NameSpace KExiv2Iface
|
1109
libkexiv2/libkexiv2/kexiv2.h
Normal file
1109
libkexiv2/libkexiv2/kexiv2.h
Normal file
File diff suppressed because it is too large
Load diff
538
libkexiv2/libkexiv2/kexiv2_p.cpp
Normal file
538
libkexiv2/libkexiv2/kexiv2_p.cpp
Normal file
|
@ -0,0 +1,538 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2007-09-03
|
||||
* @brief Exiv2 library interface for KDE
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2_p.h"
|
||||
|
||||
// C ANSI includes
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <sys/stat.h>
|
||||
#include <utime.h>
|
||||
}
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
KExiv2::Private::Private()
|
||||
: data(new KExiv2Data::Private)
|
||||
{
|
||||
writeRawFiles = false;
|
||||
updateFileTimeStamp = false;
|
||||
useXMPSidecar4Reading = false;
|
||||
metadataWritingMode = WRITETOIMAGEONLY;
|
||||
loadedFromSidecar = false;
|
||||
Exiv2::LogMsg::setHandler(KExiv2::Private::printExiv2MessageHandler);
|
||||
}
|
||||
|
||||
KExiv2::Private::~Private()
|
||||
{
|
||||
}
|
||||
|
||||
void KExiv2::Private::copyPrivateData(const Private* const other)
|
||||
{
|
||||
data = other->data;
|
||||
filePath = other->filePath;
|
||||
writeRawFiles = other->writeRawFiles;
|
||||
updateFileTimeStamp = other->updateFileTimeStamp;
|
||||
useXMPSidecar4Reading = other->useXMPSidecar4Reading;
|
||||
metadataWritingMode = other->metadataWritingMode;
|
||||
}
|
||||
|
||||
bool KExiv2::Private::saveToXMPSidecar(const QFileInfo& finfo) const
|
||||
{
|
||||
QString filePath = KExiv2::sidecarFilePathForFile(finfo.filePath());
|
||||
|
||||
if (filePath.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image;
|
||||
image = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, (const char*)(QFile::encodeName(filePath)));
|
||||
return saveOperations(finfo, image);
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
printExiv2ExceptionError("Cannot save metadata to XMP sidecar using Exiv2 ", e);
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KExiv2::Private::saveToFile(const QFileInfo& finfo) const
|
||||
{
|
||||
if (!finfo.isWritable())
|
||||
{
|
||||
kDebug() << "File '" << finfo.fileName().toAscii().constData() << "' is read only. Metadata not written.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList rawTiffBasedSupported, rawTiffBasedNotSupported;
|
||||
|
||||
// Raw files supported by Exiv2 0.21
|
||||
rawTiffBasedSupported << "dng" << "nef" << "pef" << "orf" << "srw";
|
||||
|
||||
if (Exiv2::testVersion(0,23,0))
|
||||
{
|
||||
rawTiffBasedSupported << "cr2";
|
||||
}
|
||||
|
||||
// Raw files not supported by Exiv2 0.21
|
||||
rawTiffBasedNotSupported
|
||||
<< "3fr" << "arw" << "dcr" << "erf" << "k25" << "kdc"
|
||||
<< "mos" << "raw" << "sr2" << "srf" << "rw2";
|
||||
|
||||
if (!Exiv2::testVersion(0,23,0))
|
||||
{
|
||||
rawTiffBasedNotSupported << "cr2";
|
||||
}
|
||||
|
||||
QString ext = finfo.suffix().toLower();
|
||||
|
||||
if (!writeRawFiles && (rawTiffBasedSupported.contains(ext) || rawTiffBasedNotSupported.contains(ext)) )
|
||||
{
|
||||
kDebug() << finfo.fileName()
|
||||
<< "is a TIFF based RAW file, writing to such a file is disabled by current settings.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (rawTiffBasedNotSupported.contains(ext))
|
||||
{
|
||||
kDebug() << finfo.fileName()
|
||||
<< "is TIFF based RAW file not yet supported. Metadata not saved.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rawTiffBasedSupported.contains(ext) && !writeRawFiles)
|
||||
{
|
||||
kDebug() << finfo.fileName()
|
||||
<< "is TIFF based RAW file supported but writing mode is disabled. "
|
||||
<< "Metadata not saved.";
|
||||
return false;
|
||||
}
|
||||
|
||||
kDebug() << "File Extension: " << ext << " is supported for writing mode";
|
||||
|
||||
bool ret = false;
|
||||
*/
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image;
|
||||
image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(finfo.filePath())));
|
||||
return saveOperations(finfo, image);
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
printExiv2ExceptionError("Cannot save metadata to image using Exiv2 ", e);
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KExiv2::Private::saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::AccessMode mode;
|
||||
bool wroteComment = false, wroteEXIF = false, wroteIPTC = false, wroteXMP = false;
|
||||
|
||||
// We need to load target file metadata to merge with new one. It's mandatory with TIFF format:
|
||||
// like all tiff file structure is based on Exif.
|
||||
image->readMetadata();
|
||||
|
||||
// Image Comments ---------------------------------
|
||||
|
||||
mode = image->checkMode(Exiv2::mdComment);
|
||||
|
||||
if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
|
||||
{
|
||||
image->setComment(imageComments());
|
||||
wroteComment = true;
|
||||
}
|
||||
|
||||
// Exif metadata ----------------------------------
|
||||
|
||||
mode = image->checkMode(Exiv2::mdExif);
|
||||
|
||||
if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
|
||||
{
|
||||
if (image->mimeType() == "image/tiff")
|
||||
{
|
||||
Exiv2::ExifData orgExif = image->exifData();
|
||||
Exiv2::ExifData newExif;
|
||||
QStringList untouchedTags;
|
||||
|
||||
// With tiff image we cannot overwrite whole Exif data as well, because
|
||||
// image data are stored in Exif container. We need to take a care about
|
||||
// to not lost image data.
|
||||
untouchedTags << "Exif.Image.ImageWidth";
|
||||
untouchedTags << "Exif.Image.ImageLength";
|
||||
untouchedTags << "Exif.Image.BitsPerSample";
|
||||
untouchedTags << "Exif.Image.Compression";
|
||||
untouchedTags << "Exif.Image.PhotometricInterpretation";
|
||||
untouchedTags << "Exif.Image.FillOrder";
|
||||
untouchedTags << "Exif.Image.SamplesPerPixel";
|
||||
untouchedTags << "Exif.Image.StripOffsets";
|
||||
untouchedTags << "Exif.Image.RowsPerStrip";
|
||||
untouchedTags << "Exif.Image.StripByteCounts";
|
||||
untouchedTags << "Exif.Image.XResolution";
|
||||
untouchedTags << "Exif.Image.YResolution";
|
||||
untouchedTags << "Exif.Image.PlanarConfiguration";
|
||||
untouchedTags << "Exif.Image.ResolutionUnit";
|
||||
|
||||
for (Exiv2::ExifData::iterator it = orgExif.begin(); it != orgExif.end(); ++it)
|
||||
{
|
||||
if (untouchedTags.contains(it->key().c_str()))
|
||||
{
|
||||
newExif[it->key().c_str()] = orgExif[it->key().c_str()];
|
||||
}
|
||||
}
|
||||
|
||||
Exiv2::ExifData readedExif = exifMetadata();
|
||||
|
||||
for (Exiv2::ExifData::iterator it = readedExif.begin(); it != readedExif.end(); ++it)
|
||||
{
|
||||
if (!untouchedTags.contains(it->key().c_str()))
|
||||
{
|
||||
newExif[it->key().c_str()] = readedExif[it->key().c_str()];
|
||||
}
|
||||
}
|
||||
|
||||
image->setExifData(newExif);
|
||||
}
|
||||
else
|
||||
{
|
||||
image->setExifData(exifMetadata());
|
||||
}
|
||||
|
||||
wroteEXIF = true;
|
||||
}
|
||||
|
||||
// Iptc metadata ----------------------------------
|
||||
|
||||
mode = image->checkMode(Exiv2::mdIptc);
|
||||
|
||||
if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
|
||||
{
|
||||
image->setIptcData(iptcMetadata());
|
||||
wroteIPTC = true;
|
||||
}
|
||||
|
||||
// Xmp metadata -----------------------------------
|
||||
|
||||
mode = image->checkMode(Exiv2::mdXmp);
|
||||
|
||||
if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
|
||||
{
|
||||
#ifdef _XMP_SUPPORT_
|
||||
image->setXmpData(xmpMetadata());
|
||||
wroteXMP = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!wroteComment && !wroteEXIF && !wroteIPTC && !wroteXMP)
|
||||
{
|
||||
kDebug() << "Writing metadata is not supported for file" << finfo.fileName();
|
||||
return false;
|
||||
}
|
||||
else if (!wroteEXIF || !wroteIPTC || !wroteXMP)
|
||||
{
|
||||
kDebug() << "Support for writing metadata is limited for file" << finfo.fileName()
|
||||
<< "EXIF" << wroteEXIF << "IPTC" << wroteIPTC << "XMP" << wroteXMP;
|
||||
}
|
||||
|
||||
if (!updateFileTimeStamp)
|
||||
{
|
||||
// Don't touch access and modification timestamp of file.
|
||||
struct stat st;
|
||||
struct utimbuf ut;
|
||||
int ret = ::stat(QFile::encodeName(filePath), &st);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
ut.modtime = st.st_mtime;
|
||||
ut.actime = st.st_atime;
|
||||
}
|
||||
|
||||
image->writeMetadata();
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
::utime(QFile::encodeName(filePath), &ut);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image->writeMetadata();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
printExiv2ExceptionError("Cannot save metadata using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KExiv2Data::Private::clear()
|
||||
{
|
||||
imageComments.clear();
|
||||
exifMetadata.clear();
|
||||
iptcMetadata.clear();
|
||||
#ifdef _XMP_SUPPORT_
|
||||
xmpMetadata.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
void KExiv2::Private::printExiv2ExceptionError(const QString& msg, Exiv2::Error& e)
|
||||
{
|
||||
std::string s(e.what());
|
||||
kError() << msg.toAscii().constData() << " (Error #"
|
||||
<< e.code() << ": " << s.c_str();
|
||||
}
|
||||
|
||||
void KExiv2::Private::printExiv2MessageHandler(int lvl, const char* msg)
|
||||
{
|
||||
kDebug() << "Exiv2 (" << lvl << ") : " << msg;
|
||||
}
|
||||
|
||||
QString KExiv2::Private::convertCommentValue(const Exiv2::Exifdatum& exifDatum) const
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string comment;
|
||||
std::string charset;
|
||||
|
||||
comment = exifDatum.toString();
|
||||
|
||||
// libexiv2 will prepend "charset=\"SomeCharset\" " if charset is specified
|
||||
// Before conversion to QString, we must know the charset, so we stay with std::string for a while
|
||||
if (comment.length() > 8 && comment.substr(0, 8) == "charset=")
|
||||
{
|
||||
// the prepended charset specification is followed by a blank
|
||||
std::string::size_type pos = comment.find_first_of(' ');
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
// extract string between the = and the blank
|
||||
charset = comment.substr(8, pos-8);
|
||||
// get the rest of the string after the charset specification
|
||||
comment = comment.substr(pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
if (charset == "\"Unicode\"")
|
||||
{
|
||||
return QString::fromUtf8(comment.data());
|
||||
}
|
||||
else if (charset == "\"Jis\"")
|
||||
{
|
||||
QTextCodec* codec = QTextCodec::codecForName("JIS7");
|
||||
return codec->toUnicode(comment.c_str());
|
||||
}
|
||||
else if (charset == "\"Ascii\"")
|
||||
{
|
||||
return QString::fromLatin1(comment.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
return detectEncodingAndDecode(comment);
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
printExiv2ExceptionError("Cannot convert Comment using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString KExiv2::Private::detectEncodingAndDecode(const std::string& value) const
|
||||
{
|
||||
// For charset autodetection, we could use sophisticated code
|
||||
// (Mozilla chardet, KHTML's autodetection, QTextCodec::codecForContent),
|
||||
// but that is probably too much.
|
||||
// We check for UTF8, Local encoding and ASCII.
|
||||
// TODO: Gilles ==> Marcel : Look like KEncodingDetector class can provide a full implementation for encoding detection.
|
||||
|
||||
if (value.empty())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (KStringHandler::isUtf8(value.c_str()))
|
||||
{
|
||||
return QString::fromUtf8(value.c_str());
|
||||
}
|
||||
|
||||
// Utf8 has a pretty unique byte pattern.
|
||||
// Thats not true for ASCII, it is not possible
|
||||
// to reliably autodetect different ISO-8859 charsets.
|
||||
// So we can use either local encoding, or latin1.
|
||||
|
||||
//TODO: KDE4PORT: check for regression of #134999 (very probably no regression!)
|
||||
return QString::fromLocal8Bit(value.c_str());
|
||||
//return QString::fromLatin1(value.c_str());
|
||||
}
|
||||
|
||||
int KExiv2::Private::getXMPTagsListFromPrefix(const QString& pf, KExiv2::TagsMap& tagsMap) const
|
||||
{
|
||||
QList<const Exiv2::XmpPropertyInfo*> tags;
|
||||
tags << Exiv2::XmpProperties::propertyList(pf.toAscii().data());
|
||||
int i = 0;
|
||||
|
||||
for (QList<const Exiv2::XmpPropertyInfo*>::iterator it = tags.begin(); it != tags.end(); ++it)
|
||||
{
|
||||
while ( (*it) && !QString((*it)->name_).isNull() )
|
||||
{
|
||||
QString key = QLatin1String( Exiv2::XmpKey( pf.toAscii().data(), (*it)->name_ ).key().c_str() );
|
||||
QStringList values;
|
||||
values << (*it)->name_ << (*it)->title_ << (*it)->desc_;
|
||||
tagsMap.insert(key, values);
|
||||
++(*it);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
void KExiv2::Private::loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar)
|
||||
{
|
||||
// Having a sidecar is a special situation.
|
||||
// The sidecar data often "dominates", see in particular bug 309058 for important aspects:
|
||||
// If a field is removed from the sidecar, we must ignore (older) data for this field in the file.
|
||||
|
||||
// First: Ignore file XMP, only use sidecar XMP
|
||||
xmpMetadata() = xmpsidecar->xmpData();
|
||||
loadedFromSidecar = true;
|
||||
|
||||
// EXIF
|
||||
// Four groups of properties are mapped between EXIF and XMP:
|
||||
// Date/Time, Description, Copyright, Creator
|
||||
// A few more tags are defined "writeback" tags in the XMP specification, the sidecar value therefore overrides the Exif value.
|
||||
// The rest is kept side-by-side.
|
||||
// (to understand, remember that the xmpsidecar's Exif data is actually XMP data mapped back to Exif)
|
||||
|
||||
// Description, Copyright and Creator is dominated by the sidecar: Remove file Exif fields, if field not in XMP.
|
||||
ExifMergeHelper exifDominatedHelper;
|
||||
exifDominatedHelper << QLatin1String("Exif.Image.ImageDescription")
|
||||
<< QLatin1String("Exif.Photo.UserComment")
|
||||
<< QLatin1String("Exif.Image.Copyright")
|
||||
<< QLatin1String("Exif.Image.Artist");
|
||||
exifDominatedHelper.exclusiveMerge(xmpsidecar->exifData(), exifMetadata());
|
||||
// Date/Time and "the few more" from the XMP spec are handled as writeback
|
||||
// Note that Date/Time mapping is slightly contradictory in latest specs.
|
||||
ExifMergeHelper exifWritebackHelper;
|
||||
exifWritebackHelper << QLatin1String("Exif.Image.DateTime")
|
||||
<< QLatin1String("Exif.Image.DateTime")
|
||||
<< QLatin1String("Exif.Photo.DateTimeOriginal")
|
||||
<< QLatin1String("Exif.Photo.DateTimeDigitized")
|
||||
<< QLatin1String("Exif.Image.Orientation")
|
||||
<< QLatin1String("Exif.Image.XResolution")
|
||||
<< QLatin1String("Exif.Image.YResolution")
|
||||
<< QLatin1String("Exif.Image.ResolutionUnit")
|
||||
<< QLatin1String("Exif.Image.Software")
|
||||
<< QLatin1String("Exif.Photo.RelatedSoundFile");
|
||||
exifWritebackHelper.mergeFields(xmpsidecar->exifData(), exifMetadata());
|
||||
|
||||
// IPTC
|
||||
// These fields cover almost all relevant IPTC data and are defined in the XMP specification for reconciliation.
|
||||
IptcMergeHelper iptcDominatedHelper;
|
||||
iptcDominatedHelper << QLatin1String("Iptc.Application2.ObjectName")
|
||||
<< QLatin1String("Iptc.Application2.Urgency")
|
||||
<< QLatin1String("Iptc.Application2.Category")
|
||||
<< QLatin1String("Iptc.Application2.SuppCategory")
|
||||
<< QLatin1String("Iptc.Application2.Keywords")
|
||||
<< QLatin1String("Iptc.Application2.SubLocation")
|
||||
<< QLatin1String("Iptc.Application2.SpecialInstructions")
|
||||
<< QLatin1String("Iptc.Application2.Byline")
|
||||
<< QLatin1String("Iptc.Application2.BylineTitle")
|
||||
<< QLatin1String("Iptc.Application2.City")
|
||||
<< QLatin1String("Iptc.Application2.ProvinceState")
|
||||
<< QLatin1String("Iptc.Application2.CountryCode")
|
||||
<< QLatin1String("Iptc.Application2.CountryName")
|
||||
<< QLatin1String("Iptc.Application2.TransmissionReference")
|
||||
<< QLatin1String("Iptc.Application2.Headline")
|
||||
<< QLatin1String("Iptc.Application2.Credit")
|
||||
<< QLatin1String("Iptc.Application2.Source")
|
||||
<< QLatin1String("Iptc.Application2.Copyright")
|
||||
<< QLatin1String("Iptc.Application2.Caption")
|
||||
<< QLatin1String("Iptc.Application2.Writer");
|
||||
iptcDominatedHelper.exclusiveMerge(xmpsidecar->iptcData(), iptcMetadata());
|
||||
|
||||
IptcMergeHelper iptcWritebackHelper;
|
||||
iptcWritebackHelper << QLatin1String("Iptc.Application2.DateCreated")
|
||||
<< QLatin1String("Iptc.Application2.TimeCreated")
|
||||
<< QLatin1String("Iptc.Application2.DigitizationDate")
|
||||
<< QLatin1String("Iptc.Application2.DigitizationTime");
|
||||
iptcWritebackHelper.mergeFields(xmpsidecar->iptcData(), iptcMetadata());
|
||||
|
||||
/*
|
||||
* TODO: Exiv2 (referring to 0.23) does not correctly synchronize all times values as given below.
|
||||
* Time values and their synchronization:
|
||||
* Original Date/Time – Creation date of the intellectual content (e.g. the photograph),
|
||||
rather than the creatio*n date of the content being shown
|
||||
Exif DateTimeOriginal (36867, 0x9003) and SubSecTimeOriginal (37521, 0x9291)
|
||||
IPTC DateCreated (IIM 2:55, 0x0237) and TimeCreated (IIM 2:60, 0x023C)
|
||||
XMP (photoshop:DateCreated)
|
||||
* Digitized Date/Time – Creation date of the digital representation
|
||||
Exif DateTimeDigitized (36868, 0x9004) and SubSecTimeDigitized (37522, 0x9292)
|
||||
IPTC DigitalCreationDate (IIM 2:62, 0x023E) and DigitalCreationTime (IIM 2:63, 0x023F)
|
||||
XMP (xmp:CreateDate)
|
||||
* Modification Date/Time – Modification date of the digital image file
|
||||
Exif DateTime (306, 0x132) and SubSecTime (37520, 0x9290)
|
||||
XMP (xmp:ModifyDate)
|
||||
*/
|
||||
}
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
} // NameSpace KExiv2Iface
|
328
libkexiv2/libkexiv2/kexiv2_p.h
Normal file
328
libkexiv2/libkexiv2/kexiv2_p.h
Normal file
|
@ -0,0 +1,328 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2007-09-03
|
||||
* @brief Exiv2 library interface for KDE
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef KEXIV2PRIVATE_H
|
||||
#define KEXIV2PRIVATE_H
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
// C++ includes
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QSize>
|
||||
#include <QLatin1String>
|
||||
#include <QTextCodec>
|
||||
#include <QMatrix>
|
||||
#include <QFileInfo>
|
||||
#include <QDataStream>
|
||||
#include <QSharedData>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <ktemporaryfile.h>
|
||||
#include <kencodingdetector.h>
|
||||
#include <kstringhandler.h>
|
||||
#include <kdeversion.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
// Exiv2 includes -------------------------------------------------------
|
||||
|
||||
// NOTE: All Exiv2 header must be stay there to not expose external source code to Exiv2 API
|
||||
// and reduce Exiv2 dependency to client code.
|
||||
|
||||
// The pragmas are required to be able to catch exceptions thrown by libexiv2:
|
||||
// See http://gcc.gnu.org/wiki/Visibility, the section about c++ exceptions.
|
||||
// They are needed for all libexiv2 versions that do not care about visibility.
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC visibility push(default)
|
||||
#endif
|
||||
|
||||
#include <exiv2/exv_conf.h>
|
||||
#include <exiv2/error.hpp>
|
||||
#include <exiv2/image.hpp>
|
||||
#include <exiv2/jpgimage.hpp>
|
||||
#include <exiv2/datasets.hpp>
|
||||
#include <exiv2/tags.hpp>
|
||||
#include <exiv2/preview.hpp>
|
||||
#include <exiv2/properties.hpp>
|
||||
#include <exiv2/types.hpp>
|
||||
#include <exiv2/exif.hpp>
|
||||
#include <exiv2/xmpsidecar.hpp>
|
||||
|
||||
// Check if Exiv2 support XMP
|
||||
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
# define _XMP_SUPPORT_ 1
|
||||
#endif
|
||||
|
||||
// Make sure an EXIV2_TEST_VERSION macro exists:
|
||||
|
||||
#ifdef EXIV2_VERSION
|
||||
# ifndef EXIV2_TEST_VERSION
|
||||
# define EXIV2_TEST_VERSION(major,minor,patch) \
|
||||
( EXIV2_VERSION >= EXIV2_MAKE_VERSION(major,minor,patch) )
|
||||
# endif
|
||||
#else
|
||||
# define EXIV2_TEST_VERSION(major,minor,patch) (false)
|
||||
#endif
|
||||
|
||||
// With exiv2 > 0.20.0, all makernote header files have been removed to increase binary compatibility.
|
||||
// See Exiv2 bugzilla entry http://dev.exiv2.org/issues/719
|
||||
// and wiki topic http://dev.exiv2.org/boards/3/topics/583
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC visibility pop
|
||||
#endif
|
||||
|
||||
// End of Exiv2 headers ------------------------------------------------------
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KExiv2Data::Private : public QSharedData
|
||||
{
|
||||
public:
|
||||
|
||||
void clear();
|
||||
|
||||
public:
|
||||
|
||||
std::string imageComments;
|
||||
|
||||
Exiv2::ExifData exifMetadata;
|
||||
|
||||
Exiv2::IptcData iptcMetadata;
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
Exiv2::XmpData xmpMetadata;
|
||||
#endif
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
class KExiv2::Private
|
||||
{
|
||||
public:
|
||||
|
||||
Private();
|
||||
~Private();
|
||||
|
||||
void copyPrivateData(const Private* const other);
|
||||
|
||||
bool saveToXMPSidecar(const QFileInfo& finfo) const;
|
||||
bool saveToFile(const QFileInfo& finfo) const;
|
||||
bool saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const;
|
||||
|
||||
/** Wrapper method to convert a Comments content to a QString.
|
||||
*/
|
||||
QString convertCommentValue(const Exiv2::Exifdatum& exifDatum) const;
|
||||
|
||||
/** Charset autodetection to convert a string to a QString.
|
||||
*/
|
||||
QString detectEncodingAndDecode(const std::string& value) const;
|
||||
|
||||
int getXMPTagsListFromPrefix(const QString& pf, KExiv2::TagsMap& tagsMap) const;
|
||||
|
||||
const Exiv2::ExifData& exifMetadata() const { return data.constData()->exifMetadata; }
|
||||
const Exiv2::IptcData& iptcMetadata() const { return data.constData()->iptcMetadata; }
|
||||
const std::string& imageComments() const { return data.constData()->imageComments; }
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
const Exiv2::XmpData& xmpMetadata() const { return data.constData()->xmpMetadata; }
|
||||
#endif
|
||||
|
||||
Exiv2::ExifData& exifMetadata() { return data.data()->exifMetadata; }
|
||||
Exiv2::IptcData& iptcMetadata() { return data.data()->iptcMetadata; }
|
||||
std::string& imageComments() { return data.data()->imageComments; }
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
Exiv2::XmpData& xmpMetadata() { return data.data()->xmpMetadata; }
|
||||
|
||||
void loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/** Generic method to print the Exiv2 C++ Exception error message from 'e'.
|
||||
* 'msg' string is printed using kDebug rules..
|
||||
*/
|
||||
static void printExiv2ExceptionError(const QString& msg, Exiv2::Error& e);
|
||||
|
||||
/** Generic method to print debug message from Exiv2.
|
||||
* 'msg' string is printed using kDebug rules. 'lvl' is the debug level of Exiv2 message.
|
||||
*/
|
||||
static void printExiv2MessageHandler(int lvl, const char* msg);
|
||||
|
||||
public:
|
||||
|
||||
bool writeRawFiles;
|
||||
bool updateFileTimeStamp;
|
||||
|
||||
bool useXMPSidecar4Reading;
|
||||
|
||||
/// A mode from #MetadataWritingMode enum.
|
||||
int metadataWritingMode;
|
||||
|
||||
/// XMP, and parts of EXIF/IPTC, were loaded from an XMP sidecar file
|
||||
bool loadedFromSidecar;
|
||||
|
||||
QString filePath;
|
||||
QSize pixelSize;
|
||||
QString mimeType;
|
||||
|
||||
QSharedDataPointer<KExiv2Data::Private> data;
|
||||
};
|
||||
|
||||
template <class Data, class Key, class KeyString, class KeyStringList = QList<KeyString> >
|
||||
class MergeHelper
|
||||
{
|
||||
public:
|
||||
|
||||
KeyStringList keys;
|
||||
|
||||
MergeHelper& operator<<(const KeyString& key)
|
||||
{
|
||||
keys << key;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two (Exif,IPTC,Xmp)Data packages, where the result is stored in dest
|
||||
* and fields from src take precedence over existing data from dest.
|
||||
*/
|
||||
void mergeAll(const Data& src, Data& dest)
|
||||
{
|
||||
for (typename Data::const_iterator it = src.begin(); it != src.end(); ++it)
|
||||
{
|
||||
typename Data::iterator destIt = dest.findKey(Key(it->key()));
|
||||
|
||||
if (destIt == dest.end())
|
||||
{
|
||||
dest.add(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
*destIt = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two (Exif,IPTC,Xmp)Data packages, the result is stored in dest.
|
||||
* Only keys in keys are considered for merging.
|
||||
* Fields from src take precedence over existing data from dest.
|
||||
*/
|
||||
void mergeFields(const Data& src, Data& dest)
|
||||
{
|
||||
foreach (const KeyString& keyString, keys)
|
||||
{
|
||||
Key key(keyString.latin1());
|
||||
typename Data::const_iterator it = src.findKey(key);
|
||||
if (it == src.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
typename Data::iterator destIt = dest.findKey(key);
|
||||
if (destIt == dest.end())
|
||||
{
|
||||
dest.add(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
*destIt = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two (Exif,IPTC,Xmp)Data packages, the result is stored in dest.
|
||||
* The following steps apply only to keys in "keys":
|
||||
* The result is determined by src.
|
||||
* Keys must exist in src to kept in dest.
|
||||
* Fields from src take precedence over existing data from dest.
|
||||
*/
|
||||
void exclusiveMerge(const Data& src, Data& dest)
|
||||
{
|
||||
foreach (const KeyString& keyString, keys)
|
||||
{
|
||||
Key key(keyString.latin1());
|
||||
typename Data::const_iterator it = src.findKey(key);
|
||||
typename Data::iterator destIt = dest.findKey(key);
|
||||
|
||||
if (destIt == dest.end())
|
||||
{
|
||||
if (it != src.end())
|
||||
{
|
||||
dest.add(*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it == src.end())
|
||||
{
|
||||
dest.erase(destIt);
|
||||
}
|
||||
else
|
||||
{
|
||||
*destIt = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ExifMergeHelper : public MergeHelper<Exiv2::ExifData, Exiv2::ExifKey, QLatin1String>
|
||||
{
|
||||
};
|
||||
|
||||
class IptcMergeHelper : public MergeHelper<Exiv2::IptcData, Exiv2::IptcKey, QLatin1String>
|
||||
{
|
||||
};
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
class XmpMergeHelper : public MergeHelper<Exiv2::XmpData, Exiv2::XmpKey, QLatin1String>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
} // NameSpace KExiv2Iface
|
||||
|
||||
#endif // KEXIV2PRIVATE_H
|
104
libkexiv2/libkexiv2/kexiv2comments.cpp
Normal file
104
libkexiv2/libkexiv2/kexiv2comments.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-09-15
|
||||
* @brief Comments manipulation methods
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2_p.h"
|
||||
#include "kexiv2.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
bool KExiv2::canWriteComment(const QString& filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)
|
||||
(QFile::encodeName(filePath)));
|
||||
|
||||
Exiv2::AccessMode mode = image->checkMode(Exiv2::mdComment);
|
||||
return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
std::string s(e.what());
|
||||
kError() << "Cannot check Comment access mode using Exiv2 (Error #"
|
||||
<< e.code() << ": " << s.c_str() << ")";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::hasComments() const
|
||||
{
|
||||
return !d->imageComments().empty();
|
||||
}
|
||||
|
||||
bool KExiv2::clearComments() const
|
||||
{
|
||||
return setComments(QByteArray());
|
||||
}
|
||||
|
||||
QByteArray KExiv2::getComments() const
|
||||
{
|
||||
return QByteArray(d->imageComments().data(), d->imageComments().size());
|
||||
}
|
||||
|
||||
QString KExiv2::getCommentsDecoded() const
|
||||
{
|
||||
return d->detectEncodingAndDecode(d->imageComments());
|
||||
}
|
||||
|
||||
bool KExiv2::setComments(const QByteArray& data) const
|
||||
{
|
||||
d->imageComments() = std::string(data.data(), data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
QString KExiv2::detectLanguageAlt(const QString& value, QString& lang)
|
||||
{
|
||||
// Ex. from an Xmp tag Xmp.tiff.copyright: "lang="x-default" (c) Gilles Caulier 2007"
|
||||
|
||||
if (value.size() > 6 && value.startsWith(QString("lang=\"")))
|
||||
{
|
||||
int pos = value.indexOf(QString("\""), 6);
|
||||
if (pos != -1)
|
||||
{
|
||||
lang = value.mid(6, pos-6);
|
||||
return (value.mid(pos+2));
|
||||
}
|
||||
}
|
||||
|
||||
lang.clear();
|
||||
return value;
|
||||
}
|
||||
|
||||
} // NameSpace KExiv2Iface
|
66
libkexiv2/libkexiv2/kexiv2data.h
Normal file
66
libkexiv2/libkexiv2/kexiv2data.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-11-15
|
||||
* @brief Exiv2 library interface for KDE
|
||||
*
|
||||
* @author Copyright (C) 2009-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2009-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef KEXIV2DATA_H
|
||||
#define KEXIV2DATA_H
|
||||
|
||||
// QT includes
|
||||
|
||||
#include <QtCore/QSharedDataPointer>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT KExiv2Data
|
||||
{
|
||||
public:
|
||||
|
||||
KExiv2Data();
|
||||
KExiv2Data(const KExiv2Data&);
|
||||
~KExiv2Data();
|
||||
|
||||
KExiv2Data& operator=(const KExiv2Data&);
|
||||
|
||||
public:
|
||||
|
||||
// Declared as public due to use in KExiv2Priv class
|
||||
class Private;
|
||||
|
||||
private:
|
||||
|
||||
QSharedDataPointer<Private> d;
|
||||
|
||||
friend class KExiv2;
|
||||
};
|
||||
|
||||
} // NameSpace KExiv2Iface
|
||||
|
||||
#endif /* KEXIV2_H */
|
1160
libkexiv2/libkexiv2/kexiv2exif.cpp
Normal file
1160
libkexiv2/libkexiv2/kexiv2exif.cpp
Normal file
File diff suppressed because it is too large
Load diff
989
libkexiv2/libkexiv2/kexiv2gps.cpp
Normal file
989
libkexiv2/libkexiv2/kexiv2gps.cpp
Normal file
|
@ -0,0 +1,989 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-09-15
|
||||
* @brief GPS manipulation methods
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
* @author Copyright (C) 2010-2012 by Michael G. Hansen
|
||||
* <a href="mailto:mike at mghansen dot de">mike at mghansen dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2.h"
|
||||
#include "kexiv2_p.h"
|
||||
|
||||
// C++ includes
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
bool KExiv2::getGPSInfo(double& altitude, double& latitude, double& longitude) const
|
||||
{
|
||||
// Some GPS device do not set Altitude. So a valid GPS position can be with a zero value.
|
||||
// No need to check return value.
|
||||
getGPSAltitude(&altitude);
|
||||
|
||||
if (!getGPSLatitudeNumber(&latitude))
|
||||
return false;
|
||||
|
||||
if (!getGPSLongitudeNumber(&longitude))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KExiv2::getGPSLatitudeNumber(double* const latitude) const
|
||||
{
|
||||
try
|
||||
{
|
||||
*latitude=0.0;
|
||||
|
||||
// Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
|
||||
if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLatitude"), latitude) )
|
||||
return true;
|
||||
|
||||
// Now try to get the reference from Exif.
|
||||
const QByteArray latRef = getExifTagData("Exif.GPSInfo.GPSLatitudeRef");
|
||||
|
||||
if (!latRef.isEmpty())
|
||||
{
|
||||
Exiv2::ExifKey exifKey("Exif.GPSInfo.GPSLatitude");
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
|
||||
|
||||
if (it != exifData.end() && (*it).count() == 3)
|
||||
{
|
||||
// Latitude decoding from Exif.
|
||||
double num, den, min, sec;
|
||||
|
||||
num = (double)((*it).toRational(0).first);
|
||||
den = (double)((*it).toRational(0).second);
|
||||
|
||||
if (den == 0)
|
||||
return false;
|
||||
|
||||
*latitude = num/den;
|
||||
|
||||
num = (double)((*it).toRational(1).first);
|
||||
den = (double)((*it).toRational(1).second);
|
||||
|
||||
if (den == 0)
|
||||
return false;
|
||||
|
||||
min = num/den;
|
||||
|
||||
if (min != -1.0)
|
||||
*latitude = *latitude + min/60.0;
|
||||
|
||||
num = (double)((*it).toRational(2).first);
|
||||
den = (double)((*it).toRational(2).second);
|
||||
|
||||
if (den == 0)
|
||||
{
|
||||
// be relaxed and accept 0/0 seconds. See #246077.
|
||||
if (num == 0)
|
||||
den = 1;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
sec = num/den;
|
||||
|
||||
if (sec != -1.0)
|
||||
*latitude = *latitude + sec/3600.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (latRef[0] == 'S')
|
||||
*latitude *= -1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get GPS tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::getGPSLongitudeNumber(double* const longitude) const
|
||||
{
|
||||
try
|
||||
{
|
||||
*longitude=0.0;
|
||||
|
||||
// Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
|
||||
if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLongitude"), longitude) )
|
||||
return true;
|
||||
|
||||
// Now try to get the reference from Exif.
|
||||
const QByteArray lngRef = getExifTagData("Exif.GPSInfo.GPSLongitudeRef");
|
||||
|
||||
if (!lngRef.isEmpty())
|
||||
{
|
||||
// Longitude decoding from Exif.
|
||||
|
||||
Exiv2::ExifKey exifKey2("Exif.GPSInfo.GPSLongitude");
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(exifKey2);
|
||||
|
||||
if (it != exifData.end() && (*it).count() == 3)
|
||||
{
|
||||
/// @todo Decoding of latitude and longitude works in the same way,
|
||||
/// code here can be put in a separate function
|
||||
double num, den;
|
||||
|
||||
num = (double)((*it).toRational(0).first);
|
||||
den = (double)((*it).toRational(0).second);
|
||||
|
||||
if (den == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*longitude = num/den;
|
||||
|
||||
num = (double)((*it).toRational(1).first);
|
||||
den = (double)((*it).toRational(1).second);
|
||||
|
||||
if (den == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const double min = num/den;
|
||||
|
||||
if (min != -1.0)
|
||||
{
|
||||
*longitude = *longitude + min/60.0;
|
||||
}
|
||||
|
||||
num = (double)((*it).toRational(2).first);
|
||||
den = (double)((*it).toRational(2).second);
|
||||
|
||||
if (den == 0)
|
||||
{
|
||||
// be relaxed and accept 0/0 seconds. See #246077.
|
||||
if (num == 0)
|
||||
den = 1;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const double sec = num/den;
|
||||
|
||||
if (sec != -1.0)
|
||||
{
|
||||
*longitude = *longitude + sec/3600.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lngRef[0] == 'W')
|
||||
{
|
||||
*longitude *= -1.0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get GPS tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::getGPSAltitude(double* const altitude) const
|
||||
{
|
||||
try
|
||||
{
|
||||
double num, den;
|
||||
*altitude=0.0;
|
||||
|
||||
// Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
|
||||
const QString altRefXmp = getXmpTagString("Xmp.exif.GPSAltitudeRef");
|
||||
|
||||
if (!altRefXmp.isEmpty())
|
||||
{
|
||||
const QString altXmp = getXmpTagString("Xmp.exif.GPSAltitude");
|
||||
|
||||
if (!altXmp.isEmpty())
|
||||
{
|
||||
num = altXmp.section('/', 0, 0).toDouble();
|
||||
den = altXmp.section('/', 1, 1).toDouble();
|
||||
|
||||
if (den == 0)
|
||||
return false;
|
||||
|
||||
*altitude = num/den;
|
||||
|
||||
if (altRefXmp == QString("1"))
|
||||
*altitude *= -1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the reference from Exif (above/below sea level)
|
||||
const QByteArray altRef = getExifTagData("Exif.GPSInfo.GPSAltitudeRef");
|
||||
|
||||
if (!altRef.isEmpty())
|
||||
{
|
||||
// Altitude decoding from Exif.
|
||||
|
||||
Exiv2::ExifKey exifKey3("Exif.GPSInfo.GPSAltitude");
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(exifKey3);
|
||||
if (it != exifData.end() && (*it).count())
|
||||
{
|
||||
num = (double)((*it).toRational(0).first);
|
||||
den = (double)((*it).toRational(0).second);
|
||||
|
||||
if (den == 0)
|
||||
return false;
|
||||
|
||||
*altitude = num/den;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (altRef[0] == '1')
|
||||
*altitude *= -1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get GPS tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString KExiv2::getGPSLatitudeString() const
|
||||
{
|
||||
double latitude;
|
||||
|
||||
if (!getGPSLatitudeNumber(&latitude))
|
||||
return QString();
|
||||
|
||||
return convertToGPSCoordinateString(true, latitude);
|
||||
}
|
||||
|
||||
QString KExiv2::getGPSLongitudeString() const
|
||||
{
|
||||
double longitude;
|
||||
|
||||
if (!getGPSLongitudeNumber(&longitude))
|
||||
return QString();
|
||||
|
||||
return convertToGPSCoordinateString(false, longitude);
|
||||
}
|
||||
|
||||
bool KExiv2::initializeGPSInfo(const bool setProgramName)
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: what happens if these already exist?
|
||||
|
||||
// Do all the easy constant ones first.
|
||||
// GPSVersionID tag: standard says is should be four bytes: 02 00 00 00
|
||||
// (and, must be present).
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
|
||||
value->read("2 0 0 0");
|
||||
d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), value.get());
|
||||
|
||||
// Datum: the datum of the measured data. If not given, we insert WGS-84.
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSMapDatum"] = "WGS-84";
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
setXmpTagString("Xmp.exif.GPSVersionID", QString("2.0.0.0"), false);
|
||||
setXmpTagString("Xmp.exif.GPSMapDatum", QString("WGS-84"), false);
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot initialize GPS data using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::setGPSInfo(const double altitude, const double latitude, const double longitude, const bool setProgramName)
|
||||
{
|
||||
return setGPSInfo(&altitude, latitude, longitude, setProgramName);
|
||||
}
|
||||
|
||||
bool KExiv2::setGPSInfo(const double* const altitude, const double latitude, const double longitude, const bool setProgramName)
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// In first, we need to clean up all existing GPS info.
|
||||
removeGPSInfo();
|
||||
|
||||
// now re-initialize the GPS info:
|
||||
if (!initializeGPSInfo(setProgramName))
|
||||
return false;
|
||||
|
||||
char scratchBuf[100];
|
||||
long int nom, denom;
|
||||
long int deg, min;
|
||||
|
||||
// Now start adding data.
|
||||
|
||||
// ALTITUDE.
|
||||
if (altitude)
|
||||
{
|
||||
// Altitude reference: byte "00" meaning "above sea level", "01" mening "behing sea level".
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
|
||||
|
||||
if ((*altitude) >= 0) value->read("0");
|
||||
else value->read("1");
|
||||
|
||||
d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), value.get());
|
||||
|
||||
// And the actual altitude, as absolute value..
|
||||
convertToRational(fabs(*altitude), &nom, &denom, 4);
|
||||
snprintf(scratchBuf, 100, "%ld/%ld", nom, denom);
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSAltitude"] = scratchBuf;
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
setXmpTagString("Xmp.exif.GPSAltitudeRef", ((*altitude) >= 0) ? QString("0") : QString("1"), false);
|
||||
setXmpTagString("Xmp.exif.GPSAltitude", QString(scratchBuf), false);
|
||||
#endif // _XMP_SUPPORT_
|
||||
}
|
||||
|
||||
// LATITUDE
|
||||
// Latitude reference:
|
||||
// latitude < 0 : "S"
|
||||
// latitude > 0 : "N"
|
||||
//
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSLatitudeRef"] = (latitude < 0 ) ? "S" : "N";
|
||||
|
||||
// Now the actual latitude itself.
|
||||
// This is done as three rationals.
|
||||
// I choose to do it as:
|
||||
// dd/1 - degrees.
|
||||
// mmmm/100 - minutes
|
||||
// 0/1 - seconds
|
||||
// Exif standard says you can do it with minutes
|
||||
// as mm/1 and then seconds as ss/1, but its
|
||||
// (slightly) more accurate to do it as
|
||||
// mmmm/100 than to split it.
|
||||
// We also absolute the value (with fabs())
|
||||
// as the sign is encoded in LatRef.
|
||||
// Further note: original code did not translate between
|
||||
// dd.dddddd to dd mm.mm - that's why we now multiply
|
||||
// by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
|
||||
deg = (int)floor(fabs(latitude)); // Slice off after decimal.
|
||||
min = (int)floor((fabs(latitude) - floor(fabs(latitude))) * 60000000);
|
||||
snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSLatitude"] = scratchBuf;
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
/** @todo The XMP spec does not mention Xmp.exif.GPSLatitudeRef,
|
||||
* because the reference is included in Xmp.exif.GPSLatitude.
|
||||
* Is there a historic reason for writing it anyway?
|
||||
*/
|
||||
setXmpTagString("Xmp.exif.GPSLatitudeRef", (latitude < 0) ? QString("S") : QString("N"), false);
|
||||
setXmpTagString("Xmp.exif.GPSLatitude", convertToGPSCoordinateString(true, latitude), false);
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// LONGITUDE
|
||||
// Longitude reference:
|
||||
// longitude < 0 : "W"
|
||||
// longitude > 0 : "E"
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSLongitudeRef"] = (longitude < 0 ) ? "W" : "E";
|
||||
|
||||
// Now the actual longitude itself.
|
||||
// This is done as three rationals.
|
||||
// I choose to do it as:
|
||||
// dd/1 - degrees.
|
||||
// mmmm/100 - minutes
|
||||
// 0/1 - seconds
|
||||
// Exif standard says you can do it with minutes
|
||||
// as mm/1 and then seconds as ss/1, but its
|
||||
// (slightly) more accurate to do it as
|
||||
// mmmm/100 than to split it.
|
||||
// We also absolute the value (with fabs())
|
||||
// as the sign is encoded in LongRef.
|
||||
// Further note: original code did not translate between
|
||||
// dd.dddddd to dd mm.mm - that's why we now multiply
|
||||
// by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
|
||||
deg = (int)floor(fabs(longitude)); // Slice off after decimal.
|
||||
min = (int)floor((fabs(longitude) - floor(fabs(longitude))) * 60000000);
|
||||
snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
|
||||
d->exifMetadata()["Exif.GPSInfo.GPSLongitude"] = scratchBuf;
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
/** @todo The XMP spec does not mention Xmp.exif.GPSLongitudeRef,
|
||||
* because the reference is included in Xmp.exif.GPSLongitude.
|
||||
* Is there a historic reason for writing it anyway?
|
||||
*/
|
||||
setXmpTagString("Xmp.exif.GPSLongitudeRef", (longitude < 0) ? QString("W") : QString("E"), false);
|
||||
setXmpTagString("Xmp.exif.GPSLongitude", convertToGPSCoordinateString(false, longitude), false);
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Exif GPS tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::setGPSInfo(const double altitude, const QString& latitude, const QString& longitude, const bool setProgramName)
|
||||
{
|
||||
double longitudeValue, latitudeValue;
|
||||
|
||||
if (!convertFromGPSCoordinateString(latitude, &latitudeValue))
|
||||
return false;
|
||||
|
||||
if (!convertFromGPSCoordinateString(longitude, &longitudeValue))
|
||||
return false;
|
||||
|
||||
return setGPSInfo(&altitude, latitudeValue, longitudeValue, setProgramName);
|
||||
}
|
||||
|
||||
bool KExiv2::removeGPSInfo(const bool setProgramName)
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
QStringList gpsTagsKeys;
|
||||
|
||||
for (Exiv2::ExifData::iterator it = d->exifMetadata().begin();
|
||||
it != d->exifMetadata().end(); ++it)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key.section('.', 1, 1) == QString("GPSInfo"))
|
||||
gpsTagsKeys.append(key);
|
||||
}
|
||||
|
||||
for(QStringList::const_iterator it2 = gpsTagsKeys.constBegin(); it2 != gpsTagsKeys.constEnd(); ++it2)
|
||||
{
|
||||
Exiv2::ExifKey gpsKey((*it2).toAscii().constData());
|
||||
Exiv2::ExifData::iterator it3 = d->exifMetadata().findKey(gpsKey);
|
||||
|
||||
if (it3 != d->exifMetadata().end())
|
||||
d->exifMetadata().erase(it3);
|
||||
}
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
/** @todo The XMP spec does not mention Xmp.exif.GPSLongitudeRef,
|
||||
* and Xmp.exif.GPSLatitudeRef. But because we write them in setGPSInfo(),
|
||||
* we should also remove them here.
|
||||
*/
|
||||
removeXmpTag("Xmp.exif.GPSLatitudeRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSLongitudeRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSVersionID", false);
|
||||
removeXmpTag("Xmp.exif.GPSLatitude", false);
|
||||
removeXmpTag("Xmp.exif.GPSLongitude", false);
|
||||
removeXmpTag("Xmp.exif.GPSAltitudeRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSAltitude", false);
|
||||
removeXmpTag("Xmp.exif.GPSTimeStamp", false);
|
||||
removeXmpTag("Xmp.exif.GPSSatellites", false);
|
||||
removeXmpTag("Xmp.exif.GPSStatus", false);
|
||||
removeXmpTag("Xmp.exif.GPSMeasureMode", false);
|
||||
removeXmpTag("Xmp.exif.GPSDOP", false);
|
||||
removeXmpTag("Xmp.exif.GPSSpeedRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSSpeed", false);
|
||||
removeXmpTag("Xmp.exif.GPSTrackRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSTrack", false);
|
||||
removeXmpTag("Xmp.exif.GPSImgDirectionRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSImgDirection", false);
|
||||
removeXmpTag("Xmp.exif.GPSMapDatum", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestLatitude", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestLongitude", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestBearingRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestBearing", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestDistanceRef", false);
|
||||
removeXmpTag("Xmp.exif.GPSDestDistance", false);
|
||||
removeXmpTag("Xmp.exif.GPSProcessingMethod", false);
|
||||
removeXmpTag("Xmp.exif.GPSAreaInformation", false);
|
||||
removeXmpTag("Xmp.exif.GPSDifferential", false);
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot remove Exif GPS tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KExiv2::convertToRational(const double number, long int* const numerator,
|
||||
long int* const denominator, const int rounding)
|
||||
{
|
||||
// This function converts the given decimal number
|
||||
// to a rational (fractional) number.
|
||||
//
|
||||
// Examples in comments use Number as 25.12345, Rounding as 4.
|
||||
|
||||
// Split up the number.
|
||||
double whole = trunc(number);
|
||||
double fractional = number - whole;
|
||||
|
||||
// Calculate the "number" used for rounding.
|
||||
// This is 10^Digits - ie, 4 places gives us 10000.
|
||||
double rounder = pow(10.0, rounding);
|
||||
|
||||
// Round the fractional part, and leave the number
|
||||
// as greater than 1.
|
||||
// To do this we: (for example)
|
||||
// 0.12345 * 10000 = 1234.5
|
||||
// floor(1234.5) = 1234 - now bigger than 1 - ready...
|
||||
fractional = round(fractional * rounder);
|
||||
|
||||
// Convert the whole thing to a fraction.
|
||||
// Fraction is:
|
||||
// (25 * 10000) + 1234 251234
|
||||
// ------------------- = ------ = 25.1234
|
||||
// 10000 10000
|
||||
double numTemp = (whole * rounder) + fractional;
|
||||
double denTemp = rounder;
|
||||
|
||||
// Now we should reduce until we can reduce no more.
|
||||
|
||||
// Try simple reduction...
|
||||
// if Num
|
||||
// ----- = integer out then....
|
||||
// Den
|
||||
if (trunc(numTemp / denTemp) == (numTemp / denTemp))
|
||||
{
|
||||
// Divide both by Denominator.
|
||||
numTemp /= denTemp;
|
||||
denTemp /= denTemp;
|
||||
}
|
||||
|
||||
// And, if that fails, brute force it.
|
||||
while (1)
|
||||
{
|
||||
// Jump out if we can't integer divide one.
|
||||
if ((numTemp / 2) != trunc(numTemp / 2)) break;
|
||||
if ((denTemp / 2) != trunc(denTemp / 2)) break;
|
||||
// Otherwise, divide away.
|
||||
numTemp /= 2;
|
||||
denTemp /= 2;
|
||||
}
|
||||
|
||||
// Copy out the numbers.
|
||||
*numerator = (int)numTemp;
|
||||
*denominator = (int)denTemp;
|
||||
}
|
||||
|
||||
void KExiv2::convertToRationalSmallDenominator(const double number, long int* const numerator, long int* const denominator)
|
||||
{
|
||||
// This function converts the given decimal number
|
||||
// to a rational (fractional) number.
|
||||
//
|
||||
// This method, in contrast to the method above, will retrieve the smallest possible
|
||||
// denominator. It is tested to retrieve the correct value for 1/x, with 0 < x <= 1000000.
|
||||
// Note: This requires double precision, storing in float breaks some numbers (49, 59, 86,...)
|
||||
|
||||
// Split up the number.
|
||||
double whole = trunc(number);
|
||||
double fractional = number - whole;
|
||||
|
||||
/*
|
||||
* Find best rational approximation to a double
|
||||
* by C.B. Falconer, 2006-09-07. Released to public domain.
|
||||
*
|
||||
* Newsgroups: comp.lang.c, comp.programming
|
||||
* From: CBFalconer <cbfalconer@yahoo.com>
|
||||
* Date: Thu, 07 Sep 2006 17:35:30 -0400
|
||||
* Subject: Rational approximations
|
||||
*/
|
||||
int lastnum = 500; // this is _not_ the largest possible denominator
|
||||
long int num, approx, bestnum=0, bestdenom=1;
|
||||
double value, error, leasterr, criterion;
|
||||
|
||||
value = fractional;
|
||||
|
||||
if (value == 0.0)
|
||||
{
|
||||
*numerator = (long int)whole;
|
||||
*denominator = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
criterion = 2 * value * DBL_EPSILON;
|
||||
|
||||
for (leasterr = value, num = 1; num < lastnum; ++num)
|
||||
{
|
||||
approx = (int)(num / value + 0.5);
|
||||
error = fabs((double)num / approx - value);
|
||||
|
||||
if (error < leasterr)
|
||||
{
|
||||
bestnum = num;
|
||||
bestdenom = approx;
|
||||
leasterr = error;
|
||||
|
||||
if (leasterr <= criterion) break;
|
||||
}
|
||||
}
|
||||
|
||||
// add whole number part
|
||||
if (bestdenom * whole > (double)INT_MAX)
|
||||
{
|
||||
// In some cases, we would generate an integer overflow.
|
||||
// Fall back to Gilles's code which is better suited for such numbers.
|
||||
convertToRational(number, numerator, denominator, 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
bestnum += bestdenom * (long int)whole;
|
||||
*numerator = bestnum;
|
||||
*denominator = bestdenom;
|
||||
}
|
||||
}
|
||||
|
||||
QString KExiv2::convertToGPSCoordinateString(const long int numeratorDegrees, const long int denominatorDegrees,
|
||||
const long int numeratorMinutes, const long int denominatorMinutes,
|
||||
const long int numeratorSeconds, long int denominatorSeconds,
|
||||
const char directionReference)
|
||||
{
|
||||
/**
|
||||
* Precision:
|
||||
* A second at sea level measures 30m for our purposes, a minute 1800m.
|
||||
* (for more details, see http://en.wikipedia.org/wiki/Geographic_coordinate_system)
|
||||
* This means with a decimal precision of 8 for minutes we get +/-0,018mm.
|
||||
* (if I calculated correctly)
|
||||
*/
|
||||
|
||||
QString coordinate;
|
||||
|
||||
// be relaxed with seconds of 0/0
|
||||
if (denominatorSeconds == 0 && numeratorSeconds == 0)
|
||||
denominatorSeconds = 1;
|
||||
|
||||
if (denominatorDegrees == 1 &&
|
||||
denominatorMinutes == 1 &&
|
||||
denominatorSeconds == 1)
|
||||
{
|
||||
// use form DDD,MM,SSk
|
||||
coordinate = "%1,%2,%3%4";
|
||||
coordinate = coordinate.arg(numeratorDegrees).arg(numeratorMinutes).arg(numeratorSeconds).arg(directionReference);
|
||||
}
|
||||
else if (denominatorDegrees == 1 &&
|
||||
denominatorMinutes == 100 &&
|
||||
denominatorSeconds == 1)
|
||||
{
|
||||
// use form DDD,MM.mmk
|
||||
coordinate = "%1,%2%3";
|
||||
double minutes = (double)numeratorMinutes / (double)denominatorMinutes;
|
||||
minutes += (double)numeratorSeconds / 60.0;
|
||||
QString minutesString = QString::number(minutes, 'f', 8);
|
||||
|
||||
while (minutesString.endsWith('0') && !minutesString.endsWith(".0"))
|
||||
{
|
||||
minutesString.chop(1);
|
||||
}
|
||||
|
||||
coordinate = coordinate.arg(numeratorDegrees).arg(minutesString).arg(directionReference);
|
||||
}
|
||||
else if (denominatorDegrees == 0 ||
|
||||
denominatorMinutes == 0 ||
|
||||
denominatorSeconds == 0)
|
||||
{
|
||||
// Invalid. 1/0 is everything but 0. As is 0/0.
|
||||
return QString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// use form DDD,MM.mmk
|
||||
coordinate = "%1,%2%3";
|
||||
double degrees = (double)numeratorDegrees / (double)denominatorDegrees;
|
||||
double wholeDegrees = trunc(degrees);
|
||||
double minutes = (double)numeratorMinutes / (double)denominatorMinutes;
|
||||
minutes += (degrees - wholeDegrees) * 60.0;
|
||||
minutes += ((double)numeratorSeconds / (double)denominatorSeconds) / 60.0;
|
||||
QString minutesString = QString::number(minutes, 'f', 8);
|
||||
|
||||
while (minutesString.endsWith('0') && !minutesString.endsWith(".0"))
|
||||
{
|
||||
minutesString.chop(1);
|
||||
}
|
||||
|
||||
coordinate = coordinate.arg((int)wholeDegrees).arg(minutesString).arg(directionReference);
|
||||
}
|
||||
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
QString KExiv2::convertToGPSCoordinateString(const bool isLatitude, double coordinate)
|
||||
{
|
||||
if (coordinate < -360.0 || coordinate > 360.0)
|
||||
return QString();
|
||||
|
||||
QString coordinateString;
|
||||
|
||||
char directionReference;
|
||||
|
||||
if (isLatitude)
|
||||
{
|
||||
if (coordinate < 0)
|
||||
directionReference = 'S';
|
||||
else
|
||||
directionReference = 'N';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coordinate < 0)
|
||||
directionReference = 'W';
|
||||
else
|
||||
directionReference = 'E';
|
||||
}
|
||||
|
||||
// remove sign
|
||||
coordinate = fabs(coordinate);
|
||||
|
||||
int degrees = (int)floor(coordinate);
|
||||
// get fractional part
|
||||
coordinate = coordinate - (double)(degrees);
|
||||
// To minutes
|
||||
double minutes = coordinate * 60.0;
|
||||
|
||||
// use form DDD,MM.mmk
|
||||
coordinateString = "%1,%2%3";
|
||||
coordinateString = coordinateString.arg(degrees);
|
||||
coordinateString = coordinateString.arg(minutes, 0, 'f', 8).arg(directionReference);
|
||||
|
||||
return coordinateString;
|
||||
}
|
||||
|
||||
bool KExiv2::convertFromGPSCoordinateString(const QString& gpsString,
|
||||
long int* const numeratorDegrees, long int* const denominatorDegrees,
|
||||
long int* const numeratorMinutes, long int* const denominatorMinutes,
|
||||
long int* const numeratorSeconds, long int* const denominatorSeconds,
|
||||
char* const directionReference)
|
||||
{
|
||||
if (gpsString.isEmpty())
|
||||
return false;
|
||||
|
||||
*directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
|
||||
QString coordinate = gpsString.left(gpsString.length() - 1);
|
||||
QStringList parts = coordinate.split(',');
|
||||
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
// form DDD,MM.mmk
|
||||
*denominatorDegrees = 1;
|
||||
*denominatorMinutes = 1000000;
|
||||
*denominatorSeconds = 1;
|
||||
|
||||
*numeratorDegrees = parts[0].toLong();
|
||||
|
||||
double minutes = parts[1].toDouble();
|
||||
minutes *= 1000000;
|
||||
|
||||
*numeratorMinutes = (long)round(minutes);
|
||||
*numeratorSeconds = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (parts.size() == 3)
|
||||
{
|
||||
// use form DDD,MM,SSk
|
||||
*denominatorDegrees = 1;
|
||||
*denominatorMinutes = 1;
|
||||
*denominatorSeconds = 1;
|
||||
|
||||
*numeratorDegrees = parts[0].toLong();
|
||||
*numeratorMinutes = parts[1].toLong();
|
||||
*numeratorSeconds = parts[2].toLong();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KExiv2::convertFromGPSCoordinateString(const QString& gpsString, double* const degrees)
|
||||
{
|
||||
if (gpsString.isEmpty())
|
||||
return false;
|
||||
|
||||
char directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
|
||||
QString coordinate = gpsString.left(gpsString.length() - 1);
|
||||
QStringList parts = coordinate.split(',');
|
||||
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
// form DDD,MM.mmk
|
||||
*degrees = parts[0].toLong();
|
||||
*degrees += parts[1].toDouble() / 60.0;
|
||||
|
||||
if (directionReference == 'W' || directionReference == 'S')
|
||||
*degrees *= -1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (parts.size() == 3)
|
||||
{
|
||||
// use form DDD,MM,SSk
|
||||
|
||||
*degrees = parts[0].toLong();
|
||||
*degrees += parts[1].toLong() / 60.0;
|
||||
*degrees += parts[2].toLong() / 3600.0;
|
||||
|
||||
if (directionReference == 'W' || directionReference == 'S')
|
||||
*degrees *= -1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KExiv2::convertToUserPresentableNumbers(const QString& gpsString,
|
||||
int* const degrees, int* const minutes,
|
||||
double* const seconds, char* const directionReference)
|
||||
{
|
||||
if (gpsString.isEmpty())
|
||||
return false;
|
||||
|
||||
*directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
|
||||
QString coordinate = gpsString.left(gpsString.length() - 1);
|
||||
QStringList parts = coordinate.split(',');
|
||||
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
// form DDD,MM.mmk
|
||||
*degrees = parts[0].toInt();
|
||||
double fractionalMinutes = parts[1].toDouble();
|
||||
*minutes = (int)trunc(fractionalMinutes);
|
||||
*seconds = (fractionalMinutes - (double)(*minutes)) * 60.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (parts.size() == 3)
|
||||
{
|
||||
// use form DDD,MM,SSk
|
||||
*degrees = parts[0].toInt();
|
||||
*minutes = parts[1].toInt();
|
||||
*seconds = (double)parts[2].toInt();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void KExiv2::convertToUserPresentableNumbers(const bool isLatitude, double coordinate,
|
||||
int* const degrees, int* const minutes,
|
||||
double* const seconds, char* const directionReference)
|
||||
{
|
||||
if (isLatitude)
|
||||
{
|
||||
if (coordinate < 0)
|
||||
*directionReference = 'S';
|
||||
else
|
||||
*directionReference = 'N';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coordinate < 0)
|
||||
*directionReference = 'W';
|
||||
else
|
||||
*directionReference = 'E';
|
||||
}
|
||||
|
||||
// remove sign
|
||||
coordinate = fabs(coordinate);
|
||||
*degrees = (int)floor(coordinate);
|
||||
// get fractional part
|
||||
coordinate = coordinate - (double)(*degrees);
|
||||
// To minutes
|
||||
coordinate *= 60.0;
|
||||
*minutes = (int)floor(coordinate);
|
||||
// get fractional part
|
||||
coordinate = coordinate - (double)(*minutes);
|
||||
// To seconds
|
||||
coordinate *= 60.0;
|
||||
*seconds = coordinate;
|
||||
}
|
||||
|
||||
} // NameSpace KExiv2Iface
|
967
libkexiv2/libkexiv2/kexiv2image.cpp
Normal file
967
libkexiv2/libkexiv2/kexiv2image.cpp
Normal file
|
@ -0,0 +1,967 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-09-15
|
||||
* @brief Common metadata image information manipulation methods
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2.h"
|
||||
#include "kexiv2_p.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "rotationmatrix.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
bool KExiv2::setImageProgramId(const QString& program, const QString& version) const
|
||||
{
|
||||
try
|
||||
{
|
||||
QString software(program);
|
||||
software.append("-");
|
||||
software.append(version);
|
||||
|
||||
// Set program info into Exif.Image.ProcessingSoftware tag (only available with Exiv2 >= 0.14.0).
|
||||
|
||||
d->exifMetadata()["Exif.Image.ProcessingSoftware"] = std::string(software.toAscii().constData());
|
||||
|
||||
// See B.K.O #142564: Check if Exif.Image.Software already exist. If yes, do not touch this tag.
|
||||
|
||||
if (!d->exifMetadata().empty())
|
||||
{
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifKey key("Exif.Image.Software");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
if (it == exifData.end())
|
||||
d->exifMetadata()["Exif.Image.Software"] = std::string(software.toAscii().constData());
|
||||
}
|
||||
|
||||
// set program info into XMP tags.
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
if (!d->xmpMetadata().empty())
|
||||
{
|
||||
// Only create Xmp.xmp.CreatorTool if it do not exist.
|
||||
Exiv2::XmpData xmpData(d->xmpMetadata());
|
||||
Exiv2::XmpKey key("Xmp.xmp.CreatorTool");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
if (it == xmpData.end())
|
||||
setXmpTagString("Xmp.xmp.CreatorTool", software, false);
|
||||
}
|
||||
|
||||
setXmpTagString("Xmp.tiff.Software", software, false);
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// Set program info into IPTC tags.
|
||||
|
||||
d->iptcMetadata()["Iptc.Application2.Program"] = std::string(program.toAscii().constData());
|
||||
d->iptcMetadata()["Iptc.Application2.ProgramVersion"] = std::string(version.toAscii().constData());
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Program identity into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QSize KExiv2::getImageDimensions() const
|
||||
{
|
||||
try
|
||||
{
|
||||
long width=-1, height=-1;
|
||||
|
||||
// Try to get Exif.Photo tags
|
||||
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifKey key("Exif.Photo.PixelXDimension");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
if (it != exifData.end() && it->count())
|
||||
width = it->toLong();
|
||||
|
||||
Exiv2::ExifKey key2("Exif.Photo.PixelYDimension");
|
||||
Exiv2::ExifData::iterator it2 = exifData.findKey(key2);
|
||||
if (it2 != exifData.end() && it2->count())
|
||||
height = it2->toLong();
|
||||
|
||||
if (width != -1 && height != -1)
|
||||
return QSize(width, height);
|
||||
|
||||
// Try to get Exif.Image tags
|
||||
|
||||
width = -1;
|
||||
height = -1;
|
||||
|
||||
Exiv2::ExifKey key3("Exif.Image.ImageWidth");
|
||||
Exiv2::ExifData::iterator it3 = exifData.findKey(key3);
|
||||
if (it3 != exifData.end() && it3->count())
|
||||
width = it3->toLong();
|
||||
|
||||
Exiv2::ExifKey key4("Exif.Image.ImageLength");
|
||||
Exiv2::ExifData::iterator it4 = exifData.findKey(key4);
|
||||
if (it4 != exifData.end() && it4->count())
|
||||
height = it4->toLong();
|
||||
|
||||
if (width != -1 && height != -1)
|
||||
return QSize(width, height);
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
// Try to get Xmp.tiff tags
|
||||
|
||||
width = -1;
|
||||
height = -1;
|
||||
bool wOk = false;
|
||||
bool hOk = false;
|
||||
|
||||
QString str = getXmpTagString("Xmp.tiff.ImageWidth");
|
||||
if (!str.isEmpty())
|
||||
width = str.toInt(&wOk);
|
||||
|
||||
str = getXmpTagString("Xmp.tiff.ImageLength");
|
||||
if (!str.isEmpty())
|
||||
height = str.toInt(&hOk);
|
||||
|
||||
if (wOk && hOk)
|
||||
return QSize(width, height);
|
||||
|
||||
// Try to get Xmp.exif tags
|
||||
|
||||
width = -1;
|
||||
height = -1;
|
||||
wOk = false;
|
||||
hOk = false;
|
||||
|
||||
str = getXmpTagString("Xmp.exif.PixelXDimension");
|
||||
if (!str.isEmpty())
|
||||
width = str.toInt(&wOk);
|
||||
|
||||
str = getXmpTagString("Xmp.exif.PixelYDimension");
|
||||
if (!str.isEmpty())
|
||||
height = str.toInt(&hOk);
|
||||
|
||||
if (wOk && hOk)
|
||||
return QSize(width, height);
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot parse image dimensions tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QSize();
|
||||
}
|
||||
|
||||
bool KExiv2::setImageDimensions(const QSize& size, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Set Exif values.
|
||||
|
||||
// NOTE: see B.K.O #144604: need to cast to record an unsigned integer value.
|
||||
d->exifMetadata()["Exif.Image.ImageWidth"] = static_cast<uint32_t>(size.width());
|
||||
d->exifMetadata()["Exif.Image.ImageLength"] = static_cast<uint32_t>(size.height());
|
||||
d->exifMetadata()["Exif.Photo.PixelXDimension"] = static_cast<uint32_t>(size.width());
|
||||
d->exifMetadata()["Exif.Photo.PixelYDimension"] = static_cast<uint32_t>(size.height());
|
||||
|
||||
// Set Xmp values.
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
setXmpTagString("Xmp.tiff.ImageWidth", QString::number(size.width()), false);
|
||||
setXmpTagString("Xmp.tiff.ImageLength", QString::number(size.height()), false);
|
||||
setXmpTagString("Xmp.exif.PixelXDimension", QString::number(size.width()), false);
|
||||
setXmpTagString("Xmp.exif.PixelYDimension", QString::number(size.height()), false);
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set image dimensions using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
KExiv2::ImageOrientation KExiv2::getImageOrientation() const
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifData::iterator it;
|
||||
long orientation;
|
||||
ImageOrientation imageOrient = ORIENTATION_NORMAL;
|
||||
|
||||
// -- Standard Xmp tag --------------------------------
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
bool ok = false;
|
||||
QString str = getXmpTagString("Xmp.tiff.Orientation");
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
orientation = str.toLong(&ok);
|
||||
if (ok)
|
||||
{
|
||||
kDebug() << "Orientation => Xmp.tiff.Orientation => " << (int)orientation;
|
||||
return (ImageOrientation)orientation;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// Because some camera set a wrong standard exif orientation tag,
|
||||
// We need to check makernote tags in first!
|
||||
|
||||
// -- Minolta Cameras ----------------------------------
|
||||
|
||||
Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation");
|
||||
it = exifData.findKey(minoltaKey1);
|
||||
|
||||
if (it != exifData.end() && it->count())
|
||||
{
|
||||
orientation = it->toLong();
|
||||
kDebug() << "Orientation => Exif.MinoltaCs7D.Rotation => " << (int)orientation;
|
||||
switch(orientation)
|
||||
{
|
||||
case 76:
|
||||
imageOrient = ORIENTATION_ROT_90;
|
||||
break;
|
||||
case 82:
|
||||
imageOrient = ORIENTATION_ROT_270;
|
||||
break;
|
||||
}
|
||||
return imageOrient;
|
||||
}
|
||||
|
||||
Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation");
|
||||
it = exifData.findKey(minoltaKey2);
|
||||
|
||||
if (it != exifData.end() && it->count())
|
||||
{
|
||||
orientation = it->toLong();
|
||||
kDebug() << "Orientation => Exif.MinoltaCs5D.Rotation => " << (int)orientation;
|
||||
switch(orientation)
|
||||
{
|
||||
case 76:
|
||||
imageOrient = ORIENTATION_ROT_90;
|
||||
break;
|
||||
case 82:
|
||||
imageOrient = ORIENTATION_ROT_270;
|
||||
break;
|
||||
}
|
||||
return imageOrient;
|
||||
}
|
||||
|
||||
// -- Standard Exif tag --------------------------------
|
||||
|
||||
Exiv2::ExifKey keyStd("Exif.Image.Orientation");
|
||||
it = exifData.findKey(keyStd);
|
||||
|
||||
if (it != exifData.end() && it->count())
|
||||
{
|
||||
orientation = it->toLong();
|
||||
kDebug() << "Orientation => Exif.Image.Orientation => " << (int)orientation;
|
||||
return (ImageOrientation)orientation;
|
||||
}
|
||||
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot parse Exif Orientation tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return ORIENTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
bool KExiv2::setImageOrientation(ImageOrientation orientation, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (orientation < ORIENTATION_UNSPECIFIED || orientation > ORIENTATION_ROT_270)
|
||||
{
|
||||
kDebug() << "Image orientation value is not correct!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set Exif values.
|
||||
|
||||
d->exifMetadata()["Exif.Image.Orientation"] = static_cast<uint16_t>(orientation);
|
||||
kDebug() << "Exif.Image.Orientation tag set to: " << (int)orientation;
|
||||
|
||||
// Set Xmp values.
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
setXmpTagString("Xmp.tiff.Orientation", QString::number((int)orientation), false);
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// -- Minolta/Sony Cameras ----------------------------------
|
||||
|
||||
// Minolta and Sony camera store image rotation in Makernote.
|
||||
// We remove these information to prevent duplicate values.
|
||||
|
||||
Exiv2::ExifData::iterator it;
|
||||
|
||||
Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation");
|
||||
it = d->exifMetadata().findKey(minoltaKey1);
|
||||
if (it != d->exifMetadata().end())
|
||||
{
|
||||
d->exifMetadata().erase(it);
|
||||
kDebug() << "Removing Exif.MinoltaCs7D.Rotation tag";
|
||||
}
|
||||
|
||||
Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation");
|
||||
it = d->exifMetadata().findKey(minoltaKey2);
|
||||
if (it != d->exifMetadata().end())
|
||||
{
|
||||
d->exifMetadata().erase(it);
|
||||
kDebug() << "Removing Exif.MinoltaCs5D.Rotation tag";
|
||||
}
|
||||
|
||||
// -- Exif embedded thumbnail ----------------------------------
|
||||
|
||||
Exiv2::ExifKey thumbKey("Exif.Thumbnail.Orientation");
|
||||
it = d->exifMetadata().findKey(thumbKey);
|
||||
if (it != d->exifMetadata().end() && it->count())
|
||||
{
|
||||
RotationMatrix operation((KExiv2Iface::KExiv2::ImageOrientation)it->toLong());
|
||||
operation *= orientation;
|
||||
(*it) = static_cast<uint16_t>(operation.exifOrientation());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Exif Orientation tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
KExiv2::ImageColorWorkSpace KExiv2::getImageColorWorkSpace() const
|
||||
{
|
||||
// Check Exif values.
|
||||
|
||||
long exifColorSpace = -1;
|
||||
if (!getExifTagLong("Exif.Photo.ColorSpace", exifColorSpace))
|
||||
{
|
||||
#ifdef _XMP_SUPPORT_
|
||||
QVariant var = getXmpTagVariant("Xmp.exif.ColorSpace");
|
||||
if (!var.isNull())
|
||||
exifColorSpace = var.toInt();
|
||||
#endif // _XMP_SUPPORT_
|
||||
}
|
||||
|
||||
if (exifColorSpace == 1)
|
||||
{
|
||||
return WORKSPACE_SRGB; // as specified by standard
|
||||
}
|
||||
else if (exifColorSpace == 2)
|
||||
{
|
||||
return WORKSPACE_ADOBERGB; // not in the standard!
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exifColorSpace == 65535)
|
||||
{
|
||||
// A lot of cameras set the Exif.Iop.InteroperabilityIndex,
|
||||
// as documented for ExifTool
|
||||
QString interopIndex = getExifTagString("Exif.Iop.InteroperabilityIndex");
|
||||
if (!interopIndex.isNull())
|
||||
{
|
||||
if (interopIndex == "R03")
|
||||
return WORKSPACE_ADOBERGB;
|
||||
else if (interopIndex == "R98")
|
||||
return WORKSPACE_SRGB;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Text EXIF ColorSpace tag may just not be present (NEF files)
|
||||
|
||||
// Nikon camera set Exif.Photo.ColorSpace to uncalibrated or just skip this field,
|
||||
// then add additional information into the makernotes.
|
||||
// Exif.Nikon3.ColorSpace: 1 => sRGB, 2 => AdobeRGB
|
||||
long nikonColorSpace;
|
||||
if (getExifTagLong("Exif.Nikon3.ColorSpace", nikonColorSpace))
|
||||
{
|
||||
if (nikonColorSpace == 1)
|
||||
return WORKSPACE_SRGB;
|
||||
else if (nikonColorSpace == 2)
|
||||
return WORKSPACE_ADOBERGB;
|
||||
}
|
||||
// Exif.Nikon3.ColorMode is set to "MODE2" for AdobeRGB, but there are sometimes two ColorMode fields
|
||||
// in a NEF, with the first one "COLOR" and the second one "MODE2"; but in this case, ColorSpace (above) was set.
|
||||
if (getExifTagString("Exif.Nikon3.ColorMode").contains("MODE2"))
|
||||
return WORKSPACE_ADOBERGB;
|
||||
|
||||
//TODO: This makernote tag (0x00b4) must be added to libexiv2
|
||||
/*
|
||||
long canonColorSpace;
|
||||
if (getExifTagLong("Exif.Canon.ColorSpace", canonColorSpace))
|
||||
{
|
||||
if (canonColorSpace == 1)
|
||||
return WORKSPACE_SRGB;
|
||||
else if (canonColorSpace == 2)
|
||||
return WORKSPACE_ADOBERGB;
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO : add more Makernote parsing here ...
|
||||
|
||||
if (exifColorSpace == 65535)
|
||||
return WORKSPACE_UNCALIBRATED;
|
||||
}
|
||||
|
||||
return WORKSPACE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
bool KExiv2::setImageColorWorkSpace(ImageColorWorkSpace workspace, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Set Exif value.
|
||||
|
||||
d->exifMetadata()["Exif.Photo.ColorSpace"] = static_cast<uint16_t>(workspace);
|
||||
|
||||
// Set Xmp value.
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
setXmpTagString("Xmp.exif.ColorSpace", QString::number((int)workspace), false);
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Exif color workspace tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDateTime KExiv2::getImageDateTime() const
|
||||
{
|
||||
try
|
||||
{
|
||||
// In first, trying to get Date & time from Exif tags.
|
||||
|
||||
if (!d->exifMetadata().empty())
|
||||
{
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
{
|
||||
Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
|
||||
if (it != exifData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Exif.Photo.DateTimeOriginal => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
|
||||
if (it != exifData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Exif.Photo.DateTimeDigitized => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::ExifKey key("Exif.Image.DateTime");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
|
||||
if (it != exifData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Exif.Image.DateTime => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In second, trying to get Date & time from Xmp tags.
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
if (!d->xmpMetadata().empty())
|
||||
{
|
||||
Exiv2::XmpData xmpData(d->xmpMetadata());
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.exif.DateTimeOriginal");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.exif.DateTimeOriginal => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.exif.DateTimeDigitized");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.exif.DateTimeDigitized => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.photoshop.DateCreated");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.photoshop.DateCreated => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.xmp.CreateDate");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.xmp.CreateDate => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.tiff.DateTime");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.tiff.DateTime => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.xmp.ModifyDate");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.xmp.ModifyDate => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Exiv2::XmpKey key("Xmp.xmp.MetadataDate");
|
||||
Exiv2::XmpData::iterator it = xmpData.findKey(key);
|
||||
|
||||
if (it != xmpData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Xmp.xmp.MetadataDate => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// In third, trying to get Date & time from Iptc tags.
|
||||
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
|
||||
// Try creation Iptc date & time entries.
|
||||
|
||||
Exiv2::IptcKey keyDateCreated("Iptc.Application2.DateCreated");
|
||||
Exiv2::IptcData::iterator it = iptcData.findKey(keyDateCreated);
|
||||
|
||||
if (it != iptcData.end())
|
||||
{
|
||||
QString IptcDateCreated(it->toString().c_str());
|
||||
Exiv2::IptcKey keyTimeCreated("Iptc.Application2.TimeCreated");
|
||||
Exiv2::IptcData::iterator it2 = iptcData.findKey(keyTimeCreated);
|
||||
|
||||
if (it2 != iptcData.end())
|
||||
{
|
||||
QString IptcTimeCreated(it2->toString().c_str());
|
||||
QDate date = QDate::fromString(IptcDateCreated, Qt::ISODate);
|
||||
QTime time = QTime::fromString(IptcTimeCreated, Qt::ISODate);
|
||||
QDateTime dateTime = QDateTime(date, time);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Iptc.Application2.DateCreated => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try digitization Iptc date & time entries.
|
||||
|
||||
Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate");
|
||||
Exiv2::IptcData::iterator it3 = iptcData.findKey(keyDigitizationDate);
|
||||
|
||||
if (it3 != iptcData.end())
|
||||
{
|
||||
QString IptcDateDigitization(it3->toString().c_str());
|
||||
Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime");
|
||||
Exiv2::IptcData::iterator it4 = iptcData.findKey(keyDigitizationTime);
|
||||
|
||||
if (it4 != iptcData.end())
|
||||
{
|
||||
QString IptcTimeDigitization(it4->toString().c_str());
|
||||
QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate);
|
||||
QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate);
|
||||
QDateTime dateTime = QDateTime(date, time);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime => Iptc.Application2.DigitizationDate => " << dateTime;
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot parse Exif date & time tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
bool KExiv2::setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized, bool setProgramName) const
|
||||
{
|
||||
if(!dateTime.isValid())
|
||||
return false;
|
||||
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// In first we write date & time into Exif.
|
||||
|
||||
// DateTimeDigitized is set by slide scanners etc. when a picture is digitized.
|
||||
// DateTimeOriginal specifies the date/time when the picture was taken.
|
||||
// For digital cameras, these dates should be both set, and identical.
|
||||
// Reference: http://www.exif.org/Exif2-2.PDF, chapter 4.6.5, table 4, section F.
|
||||
|
||||
const std::string &exifdatetime(dateTime.toString(QString("yyyy:MM:dd hh:mm:ss")).toAscii().constData());
|
||||
d->exifMetadata()["Exif.Image.DateTime"] = exifdatetime;
|
||||
d->exifMetadata()["Exif.Photo.DateTimeOriginal"] = exifdatetime;
|
||||
if(setDateTimeDigitized)
|
||||
d->exifMetadata()["Exif.Photo.DateTimeDigitized"] = exifdatetime;
|
||||
|
||||
#ifdef _XMP_SUPPORT_
|
||||
|
||||
// In second we write date & time into Xmp.
|
||||
|
||||
const std::string &xmpdatetime(dateTime.toString(Qt::ISODate).toAscii().constData());
|
||||
|
||||
Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
|
||||
xmpTxtVal->read(xmpdatetime);
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeOriginal"), xmpTxtVal.get());
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.photoshop.DateCreated"), xmpTxtVal.get());
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.tiff.DateTime"), xmpTxtVal.get());
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.CreateDate"), xmpTxtVal.get());
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.MetadataDate"), xmpTxtVal.get());
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.ModifyDate"), xmpTxtVal.get());
|
||||
if(setDateTimeDigitized)
|
||||
d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeDigitized"), xmpTxtVal.get());
|
||||
|
||||
// Tag not updated:
|
||||
// "Xmp.dc.DateTime" is a sequence of date relevant of dublin core change.
|
||||
// This is not the picture date as well
|
||||
|
||||
#endif // _XMP_SUPPORT_
|
||||
|
||||
// In third we write date & time into Iptc.
|
||||
|
||||
const std::string &iptcdate(dateTime.date().toString(Qt::ISODate).toAscii().constData());
|
||||
const std::string &iptctime(dateTime.time().toString(Qt::ISODate).toAscii().constData());
|
||||
d->iptcMetadata()["Iptc.Application2.DateCreated"] = iptcdate;
|
||||
d->iptcMetadata()["Iptc.Application2.TimeCreated"] = iptctime;
|
||||
if(setDateTimeDigitized)
|
||||
{
|
||||
d->iptcMetadata()["Iptc.Application2.DigitizationDate"] = iptcdate;
|
||||
d->iptcMetadata()["Iptc.Application2.DigitizationTime"] = iptctime;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Date & Time into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDateTime KExiv2::getDigitizationDateTime(bool fallbackToCreationTime) const
|
||||
{
|
||||
try
|
||||
{
|
||||
// In first, trying to get Date & time from Exif tags.
|
||||
|
||||
if (!d->exifMetadata().empty())
|
||||
{
|
||||
// Try Exif date time digitized.
|
||||
|
||||
Exiv2::ExifData exifData(d->exifMetadata());
|
||||
Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized");
|
||||
Exiv2::ExifData::iterator it = exifData.findKey(key);
|
||||
|
||||
if (it != exifData.end())
|
||||
{
|
||||
QDateTime dateTime = QDateTime::fromString(it->toString().c_str(), Qt::ISODate);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "DateTime (Exif digitalized): " << dateTime.toString().toAscii().constData();
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In second, trying to get Date & time from Iptc tags.
|
||||
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
// Try digitization Iptc date time entries.
|
||||
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate");
|
||||
Exiv2::IptcData::iterator it = iptcData.findKey(keyDigitizationDate);
|
||||
|
||||
if (it != iptcData.end())
|
||||
{
|
||||
QString IptcDateDigitization(it->toString().c_str());
|
||||
|
||||
Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime");
|
||||
Exiv2::IptcData::iterator it2 = iptcData.findKey(keyDigitizationTime);
|
||||
|
||||
if (it2 != iptcData.end())
|
||||
{
|
||||
QString IptcTimeDigitization(it2->toString().c_str());
|
||||
|
||||
QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate);
|
||||
QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate);
|
||||
QDateTime dateTime = QDateTime(date, time);
|
||||
|
||||
if (dateTime.isValid())
|
||||
{
|
||||
kDebug() << "Date (IPTC digitalized): " << dateTime.toString().toAscii().constData();
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot parse Exif digitization date & time tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
if (fallbackToCreationTime)
|
||||
return getImageDateTime();
|
||||
else
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
bool KExiv2::getImagePreview(QImage& preview) const
|
||||
{
|
||||
try
|
||||
{
|
||||
// In first we trying to get from Iptc preview tag.
|
||||
if (preview.loadFromData(getIptcTagData("Iptc.Application2.Preview")) )
|
||||
return true;
|
||||
|
||||
// TODO : Added here Makernotes preview extraction when Exiv2 will be fixed for that.
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get image preview using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::setImagePreview(const QImage& preview, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
if (preview.isNull())
|
||||
{
|
||||
removeIptcTag("Iptc.Application2.Preview");
|
||||
removeIptcTag("Iptc.Application2.PreviewFormat");
|
||||
removeIptcTag("Iptc.Application2.PreviewVersion");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
QByteArray data;
|
||||
QBuffer buffer(&data);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
// A little bit compressed preview jpeg image to limit IPTC size.
|
||||
preview.save(&buffer, "JPEG");
|
||||
kDebug() << "JPEG image preview size: (" << preview.width() << " x "
|
||||
<< preview.height() << ") pixels - " << data.size() << " bytes";
|
||||
|
||||
Exiv2::DataValue val;
|
||||
val.read((Exiv2::byte *)data.data(), data.size());
|
||||
d->iptcMetadata()["Iptc.Application2.Preview"] = val;
|
||||
|
||||
// See http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf Appendix A for details.
|
||||
d->iptcMetadata()["Iptc.Application2.PreviewFormat"] = 11; // JPEG
|
||||
d->iptcMetadata()["Iptc.Application2.PreviewVersion"] = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get image preview using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // NameSpace KExiv2Iface
|
883
libkexiv2/libkexiv2/kexiv2iptc.cpp
Normal file
883
libkexiv2/libkexiv2/kexiv2iptc.cpp
Normal file
|
@ -0,0 +1,883 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-09-15
|
||||
* @brief Iptc manipulation methods
|
||||
*
|
||||
* @author Copyright (C) 2006-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2006-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2.h"
|
||||
#include "kexiv2_p.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
bool KExiv2::canWriteIptc(const QString& filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)
|
||||
(QFile::encodeName(filePath)));
|
||||
|
||||
Exiv2::AccessMode mode = image->checkMode(Exiv2::mdIptc);
|
||||
return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
std::string s(e.what());
|
||||
kError() << "Cannot check Iptc access mode using Exiv2 (Error #"
|
||||
<< e.code() << ": " << s.c_str() << ")";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::hasIptc() const
|
||||
{
|
||||
return !d->iptcMetadata().empty();
|
||||
}
|
||||
|
||||
bool KExiv2::clearIptc() const
|
||||
{
|
||||
try
|
||||
{
|
||||
d->iptcMetadata().clear();
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot clear Iptc data using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray KExiv2::getIptc(bool addIrbHeader) const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
Exiv2::IptcData& iptc = d->iptcMetadata();
|
||||
Exiv2::DataBuf c2;
|
||||
|
||||
if (addIrbHeader)
|
||||
{
|
||||
c2 = Exiv2::Photoshop::setIptcIrb(0, 0, iptc);
|
||||
}
|
||||
else
|
||||
{
|
||||
c2 = Exiv2::IptcParser::encode(d->iptcMetadata());
|
||||
}
|
||||
|
||||
QByteArray data((const char*)c2.pData_, c2.size_);
|
||||
return data;
|
||||
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
if (!d->filePath.isEmpty())
|
||||
{
|
||||
kError() << "From file " << d->filePath.toAscii().constData();
|
||||
}
|
||||
|
||||
d->printExiv2ExceptionError("Cannot get Iptc data using Exiv2 ",e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptc(const QByteArray& data) const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!data.isEmpty())
|
||||
{
|
||||
Exiv2::IptcParser::decode(d->iptcMetadata(), (const Exiv2::byte*)data.data(), data.size());
|
||||
return (!d->iptcMetadata().empty());
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
if (!d->filePath.isEmpty())
|
||||
{
|
||||
kError() << "From file " << d->filePath.toAscii().constData();
|
||||
}
|
||||
|
||||
d->printExiv2ExceptionError("Cannot set Iptc data using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
KExiv2::MetaDataMap KExiv2::getIptcTagsDataList(const QStringList& iptcKeysFilter, bool invertSelection) const
|
||||
{
|
||||
if (d->iptcMetadata().empty())
|
||||
return MetaDataMap();
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::IptcData iptcData = d->iptcMetadata();
|
||||
iptcData.sortByKey();
|
||||
|
||||
QString ifDItemName;
|
||||
MetaDataMap metaDataMap;
|
||||
|
||||
for (Exiv2::IptcData::iterator md = iptcData.begin(); md != iptcData.end(); ++md)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(md->key().c_str());
|
||||
|
||||
// Decode the tag value with a user friendly output.
|
||||
std::ostringstream os;
|
||||
os << *md;
|
||||
|
||||
QString value;
|
||||
|
||||
if (key == QString("Iptc.Envelope.CharacterSet"))
|
||||
{
|
||||
value = iptcData.detectCharset();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = QString::fromUtf8(os.str().c_str());
|
||||
}
|
||||
|
||||
// To make a string just on one line.
|
||||
value.replace('\n', ' ');
|
||||
|
||||
// Some Iptc key are redondancy. check if already one exist...
|
||||
MetaDataMap::iterator it = metaDataMap.find(key);
|
||||
|
||||
// We apply a filter to get only the Iptc tags that we need.
|
||||
|
||||
if (!iptcKeysFilter.isEmpty())
|
||||
{
|
||||
if (!invertSelection)
|
||||
{
|
||||
if (iptcKeysFilter.contains(key.section('.', 1, 1)))
|
||||
{
|
||||
if (it == metaDataMap.end())
|
||||
{
|
||||
metaDataMap.insert(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString v = *it;
|
||||
v.append(", ");
|
||||
v.append(value);
|
||||
metaDataMap.insert(key, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!iptcKeysFilter.contains(key.section('.', 1, 1)))
|
||||
{
|
||||
if (it == metaDataMap.end())
|
||||
{
|
||||
metaDataMap.insert(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString v = *it;
|
||||
v.append(", ");
|
||||
v.append(value);
|
||||
metaDataMap.insert(key, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // else no filter at all.
|
||||
{
|
||||
if (it == metaDataMap.end())
|
||||
{
|
||||
metaDataMap.insert(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString v = *it;
|
||||
v.append(", ");
|
||||
v.append(value);
|
||||
metaDataMap.insert(key, v);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return metaDataMap;
|
||||
}
|
||||
catch (Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot parse Iptc metadata using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return MetaDataMap();
|
||||
}
|
||||
|
||||
QString KExiv2::getIptcTagTitle(const char* iptcTagName)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string iptckey(iptcTagName);
|
||||
Exiv2::IptcKey ik(iptckey);
|
||||
return QString::fromLocal8Bit( Exiv2::IptcDataSets::dataSetTitle(ik.tag(), ik.record()) );
|
||||
}
|
||||
catch (Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get metadata tag title using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString KExiv2::getIptcTagDescription(const char* iptcTagName)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string iptckey(iptcTagName);
|
||||
Exiv2::IptcKey ik(iptckey);
|
||||
return QString::fromLocal8Bit( Exiv2::IptcDataSets::dataSetDesc(ik.tag(), ik.record()) );
|
||||
}
|
||||
catch (Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get metadata tag description using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool KExiv2::removeIptcTag(const char* iptcTagName, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::IptcData::iterator it = d->iptcMetadata().begin();
|
||||
int i = 0;
|
||||
|
||||
while(it != d->iptcMetadata().end())
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key == QString(iptcTagName))
|
||||
{
|
||||
it = d->iptcMetadata().erase(it);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
};
|
||||
|
||||
if (i > 0)
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot remove Iptc tag using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcTagData(const char* iptcTagName, const QByteArray& data, bool setProgramName) const
|
||||
{
|
||||
if (data.isEmpty())
|
||||
return false;
|
||||
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::DataValue val((Exiv2::byte *)data.data(), data.size());
|
||||
d->iptcMetadata()[iptcTagName] = val;
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Iptc tag data into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray KExiv2::getIptcTagData(const char* iptcTagName) const
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::IptcKey iptcKey(iptcTagName);
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.findKey(iptcKey);
|
||||
|
||||
if (it != iptcData.end())
|
||||
{
|
||||
char* const s = new char[(*it).size()];
|
||||
(*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
|
||||
QByteArray data(s, (*it).size());
|
||||
delete [] s;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError(QString("Cannot find Iptc key '%1' into image using Exiv2 ")
|
||||
.arg(iptcTagName), e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString KExiv2::getIptcTagString(const char* iptcTagName, bool escapeCR) const
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::IptcKey iptcKey(iptcTagName);
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.findKey(iptcKey);
|
||||
|
||||
if (it != iptcData.end())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << *it;
|
||||
QString tagValue(os.str().c_str());
|
||||
|
||||
if (escapeCR)
|
||||
tagValue.replace('\n', ' ');
|
||||
|
||||
return tagValue;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError(QString("Cannot find Iptc key '%1' into image using Exiv2 ")
|
||||
.arg(iptcTagName), e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcTagString(const char* iptcTagName, const QString& value, bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
d->iptcMetadata()[iptcTagName] = std::string(value.toUtf8().constData());
|
||||
|
||||
// Make sure we have set the charset to UTF-8
|
||||
d->iptcMetadata()["Iptc.Envelope.CharacterSet"] = "\33%G";
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Iptc tag string into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KExiv2::getIptcTagsStringList(const char* iptcTagName, bool escapeCR) const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
QStringList values;
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
|
||||
for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key == QString(iptcTagName))
|
||||
{
|
||||
QString tagValue = QString::fromUtf8(it->toString().c_str());
|
||||
|
||||
if (escapeCR)
|
||||
tagValue.replace('\n', ' ');
|
||||
|
||||
values.append(tagValue);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError(QString("Cannot find Iptc key '%1' into image using Exiv2 ")
|
||||
.arg(iptcTagName), e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcTagsStringList(const char* iptcTagName, int maxSize,
|
||||
const QStringList& oldValues, const QStringList& newValues,
|
||||
bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
QStringList oldvals = oldValues;
|
||||
QStringList newvals = newValues;
|
||||
|
||||
kDebug() << d->filePath.toAscii().constData() << " : " << iptcTagName
|
||||
<< " => " << newvals.join(",").toAscii().constData();
|
||||
|
||||
// Remove all old values.
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.begin();
|
||||
|
||||
while(it != iptcData.end())
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
QString val = QString::fromUtf8(it->toString().c_str());
|
||||
|
||||
// Also remove new values to avoid duplicates. They will be added again below.
|
||||
if ( key == QString(iptcTagName) &&
|
||||
(oldvals.contains(val) || newvals.contains(val))
|
||||
)
|
||||
it = iptcData.erase(it);
|
||||
else
|
||||
++it;
|
||||
};
|
||||
|
||||
// Add new values.
|
||||
|
||||
Exiv2::IptcKey iptcTag(iptcTagName);
|
||||
|
||||
for (QStringList::iterator it = newvals.begin(); it != newvals.end(); ++it)
|
||||
{
|
||||
QString key = *it;
|
||||
key.truncate(maxSize);
|
||||
|
||||
Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
|
||||
val->read(key.toUtf8().constData());
|
||||
iptcData.add(iptcTag, val.get());
|
||||
}
|
||||
|
||||
d->iptcMetadata() = iptcData;
|
||||
|
||||
// Make sure character set is UTF-8
|
||||
setIptcTagString("Iptc.Envelope.CharacterSet", "\33%G", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError(QString("Cannot set Iptc key '%1' into image using Exiv2 ")
|
||||
.arg(iptcTagName), e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KExiv2::getIptcKeywords() const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
QStringList keywords;
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
|
||||
for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key == QString("Iptc.Application2.Keywords"))
|
||||
{
|
||||
QString val = QString::fromUtf8(it->toString().c_str());
|
||||
keywords.append(val);
|
||||
}
|
||||
}
|
||||
|
||||
kDebug() << d->filePath << " ==> Read Iptc Keywords: " << keywords;
|
||||
|
||||
return keywords;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get Iptc Keywords from image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords,
|
||||
bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
QStringList oldkeys = oldKeywords;
|
||||
QStringList newkeys = newKeywords;
|
||||
|
||||
kDebug() << d->filePath << " ==> New Iptc Keywords: " << newkeys;
|
||||
|
||||
// Remove all old keywords.
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.begin();
|
||||
|
||||
while(it != iptcData.end())
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
QString val = QString::fromUtf8(it->toString().c_str());
|
||||
|
||||
// Also remove new keywords to avoid duplicates. They will be added again below.
|
||||
if ( key == QString("Iptc.Application2.Keywords") &&
|
||||
(oldKeywords.contains(val) || newKeywords.contains(val))
|
||||
)
|
||||
it = iptcData.erase(it);
|
||||
else
|
||||
++it;
|
||||
};
|
||||
|
||||
// Add new keywords. Note that Keywords Iptc tag is limited to 64 char but can be redondant.
|
||||
|
||||
Exiv2::IptcKey iptcTag("Iptc.Application2.Keywords");
|
||||
|
||||
for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
|
||||
{
|
||||
QString key = *it;
|
||||
key.truncate(64);
|
||||
|
||||
Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
|
||||
val->read(key.toUtf8().constData());
|
||||
iptcData.add(iptcTag, val.get());
|
||||
}
|
||||
|
||||
d->iptcMetadata() = iptcData;
|
||||
|
||||
// Make sure character set is UTF-8
|
||||
setIptcTagString("Iptc.Envelope.CharacterSet", "\33%G", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Iptc Keywords into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KExiv2::getIptcSubjects() const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
QStringList subjects;
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
|
||||
for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key == QString("Iptc.Application2.Subject"))
|
||||
{
|
||||
QString val(it->toString().c_str());
|
||||
subjects.append(val);
|
||||
}
|
||||
}
|
||||
|
||||
return subjects;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get Iptc Subjects from image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcSubjects(const QStringList& oldSubjects, const QStringList& newSubjects,
|
||||
bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
QStringList oldDef = oldSubjects;
|
||||
QStringList newDef = newSubjects;
|
||||
|
||||
// Remove all old subjects.
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.begin();
|
||||
|
||||
while(it != iptcData.end())
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
QString val = QString::fromUtf8(it->toString().c_str());
|
||||
|
||||
if (key == QString("Iptc.Application2.Subject") && oldDef.contains(val))
|
||||
it = iptcData.erase(it);
|
||||
else
|
||||
++it;
|
||||
};
|
||||
|
||||
// Add new subjects. Note that Keywords Iptc tag is limited to 236 char but can be redondant.
|
||||
|
||||
Exiv2::IptcKey iptcTag("Iptc.Application2.Subject");
|
||||
|
||||
for (QStringList::iterator it = newDef.begin(); it != newDef.end(); ++it)
|
||||
{
|
||||
QString key = *it;
|
||||
key.truncate(236);
|
||||
|
||||
Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
|
||||
val->read(key.toUtf8().constData());
|
||||
iptcData.add(iptcTag, val.get());
|
||||
}
|
||||
|
||||
d->iptcMetadata() = iptcData;
|
||||
|
||||
// Make sure character set is UTF-8
|
||||
setIptcTagString("Iptc.Envelope.CharacterSet", "\33%G", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Iptc Subjects into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KExiv2::getIptcSubCategories() const
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!d->iptcMetadata().empty())
|
||||
{
|
||||
QStringList subCategories;
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
|
||||
for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
|
||||
if (key == QString("Iptc.Application2.SuppCategory"))
|
||||
{
|
||||
QString val(it->toString().c_str());
|
||||
subCategories.append(val);
|
||||
}
|
||||
}
|
||||
|
||||
return subCategories;
|
||||
}
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get Iptc Sub Categories from image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool KExiv2::setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories,
|
||||
bool setProgramName) const
|
||||
{
|
||||
if (!setProgramId(setProgramName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
QStringList oldkeys = oldSubCategories;
|
||||
QStringList newkeys = newSubCategories;
|
||||
|
||||
// Remove all old Sub Categories.
|
||||
Exiv2::IptcData iptcData(d->iptcMetadata());
|
||||
Exiv2::IptcData::iterator it = iptcData.begin();
|
||||
|
||||
while(it != iptcData.end())
|
||||
{
|
||||
QString key = QString::fromLocal8Bit(it->key().c_str());
|
||||
QString val = QString::fromUtf8(it->toString().c_str());
|
||||
|
||||
if (key == QString("Iptc.Application2.SuppCategory") && oldSubCategories.contains(val))
|
||||
it = iptcData.erase(it);
|
||||
else
|
||||
++it;
|
||||
};
|
||||
|
||||
// Add new Sub Categories. Note that SubCategories Iptc tag is limited to 32
|
||||
// characters but can be redondant.
|
||||
|
||||
Exiv2::IptcKey iptcTag("Iptc.Application2.SuppCategory");
|
||||
|
||||
for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
|
||||
{
|
||||
QString key = *it;
|
||||
key.truncate(32);
|
||||
|
||||
Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
|
||||
val->read(key.toUtf8().constData());
|
||||
iptcData.add(iptcTag, val.get());
|
||||
}
|
||||
|
||||
d->iptcMetadata() = iptcData;
|
||||
|
||||
// Make sure character set is UTF-8
|
||||
setIptcTagString("Iptc.Envelope.CharacterSet", "\33%G", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot set Iptc Sub Categories into image using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
KExiv2::TagsMap KExiv2::getIptcTagsList() const
|
||||
{
|
||||
try
|
||||
{
|
||||
QList<const Exiv2::DataSet*> tags;
|
||||
tags << Exiv2::IptcDataSets::envelopeRecordList()
|
||||
<< Exiv2::IptcDataSets::application2RecordList();
|
||||
|
||||
TagsMap tagsMap;
|
||||
for (QList<const Exiv2::DataSet*>::iterator it = tags.begin(); it != tags.end(); ++it)
|
||||
{
|
||||
do
|
||||
{
|
||||
QString key = QLatin1String( Exiv2::IptcKey( (*it)->number_, (*it)->recordId_ ).key().c_str() );
|
||||
QStringList values;
|
||||
values << (*it)->name_ << (*it)->title_ << (*it)->desc_;
|
||||
tagsMap.insert(key, values);
|
||||
++(*it);
|
||||
}
|
||||
while((*it)->number_ != 0xffff);
|
||||
}
|
||||
return tagsMap;
|
||||
}
|
||||
catch(Exiv2::Error& e)
|
||||
{
|
||||
d->printExiv2ExceptionError("Cannot get Iptc Tags list using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
|
||||
return TagsMap();
|
||||
}
|
||||
|
||||
} // NameSpace KExiv2Iface
|
214
libkexiv2/libkexiv2/kexiv2previews.cpp
Normal file
214
libkexiv2/libkexiv2/kexiv2previews.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-11-14
|
||||
* @brief Embedded preview loading
|
||||
*
|
||||
* @author Copyright (C) 2009-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2009-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "kexiv2previews.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2_p.h"
|
||||
#include "kexiv2.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KExiv2Previews::Private
|
||||
{
|
||||
public:
|
||||
|
||||
Private()
|
||||
{
|
||||
manager = 0;
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
delete manager;
|
||||
}
|
||||
|
||||
void load(Exiv2::Image::AutoPtr image_)
|
||||
{
|
||||
image = image_;
|
||||
|
||||
image->readMetadata();
|
||||
|
||||
manager = new Exiv2::PreviewManager(*image);
|
||||
Exiv2::PreviewPropertiesList props = manager->getPreviewProperties();
|
||||
|
||||
// reverse order of list, which is smallest-first
|
||||
Exiv2::PreviewPropertiesList::reverse_iterator it;
|
||||
|
||||
for (it = props.rbegin() ; it != props.rend() ; ++it)
|
||||
{
|
||||
properties << *it;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Exiv2::Image::AutoPtr image;
|
||||
Exiv2::PreviewManager* manager;
|
||||
QList<Exiv2::PreviewProperties> properties;
|
||||
};
|
||||
|
||||
KExiv2Previews::KExiv2Previews(const QString& filePath)
|
||||
: d(new Private)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(filePath)));
|
||||
d->load(image);
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
KExiv2::Private::printExiv2ExceptionError("Cannot load metadata using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
}
|
||||
|
||||
KExiv2Previews::KExiv2Previews(const QByteArray& imgData)
|
||||
: d(new Private)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size());
|
||||
d->load(image);
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
KExiv2::Private::printExiv2ExceptionError("Cannot load metadata using Exiv2 ", e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
}
|
||||
}
|
||||
|
||||
KExiv2Previews::~KExiv2Previews()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KExiv2Previews::isEmpty()
|
||||
{
|
||||
return d->properties.isEmpty();
|
||||
}
|
||||
|
||||
QSize KExiv2Previews::originalSize() const
|
||||
{
|
||||
if (d->image.get())
|
||||
return QSize(d->image->pixelWidth(), d->image->pixelHeight());
|
||||
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QString KExiv2Previews::originalMimeType() const
|
||||
{
|
||||
if (d->image.get())
|
||||
return d->image->mimeType().c_str();
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
int KExiv2Previews::count()
|
||||
{
|
||||
return d->properties.size();
|
||||
}
|
||||
|
||||
int KExiv2Previews::dataSize(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return 0;
|
||||
|
||||
return d->properties[index].size_;
|
||||
}
|
||||
|
||||
int KExiv2Previews::width(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return 0;
|
||||
|
||||
return d->properties[index].width_;
|
||||
}
|
||||
|
||||
int KExiv2Previews::height(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return 0;
|
||||
|
||||
return d->properties[index].height_;
|
||||
}
|
||||
|
||||
QString KExiv2Previews::mimeType(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return 0;
|
||||
|
||||
return QString::fromLatin1(d->properties[index].mimeType_.c_str());
|
||||
}
|
||||
|
||||
QString KExiv2Previews::fileExtension(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return 0;
|
||||
|
||||
return QString::fromLatin1(d->properties[index].extension_.c_str());
|
||||
}
|
||||
|
||||
QByteArray KExiv2Previews::data(int index)
|
||||
{
|
||||
if (index < 0 || index >= size()) return QByteArray();
|
||||
|
||||
kDebug() << "index: " << index;
|
||||
kDebug() << "d->properties: " << count();
|
||||
|
||||
try
|
||||
{
|
||||
Exiv2::PreviewImage image = d->manager->getPreviewImage(d->properties[index]);
|
||||
return QByteArray((const char*)image.pData(), image.size());
|
||||
}
|
||||
catch( Exiv2::Error& e )
|
||||
{
|
||||
KExiv2::Private::printExiv2ExceptionError("Cannot load metadata using Exiv2 ", e);
|
||||
return QByteArray();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
kError() << "Default exception from Exiv2";
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
QImage KExiv2Previews::image(int index)
|
||||
{
|
||||
QByteArray previewData = data(index);
|
||||
QImage image;
|
||||
|
||||
if (!image.loadFromData(previewData))
|
||||
return QImage();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
106
libkexiv2/libkexiv2/kexiv2previews.h
Normal file
106
libkexiv2/libkexiv2/kexiv2previews.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-11-14
|
||||
* @brief Embedded preview loading
|
||||
*
|
||||
* @author Copyright (C) 2009-2014 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2009-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef KEXIV2PREVIEWS
|
||||
#define KEXIV2PREVIEWS
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSize>
|
||||
#include <QtCore/QString>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT KExiv2Previews
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Open the given file and scan for embedded preview images
|
||||
*/
|
||||
KExiv2Previews(const QString& filePath);
|
||||
|
||||
/**
|
||||
* Open the given image data and scan the image for embedded preview images.
|
||||
*/
|
||||
KExiv2Previews(const QByteArray& imgData);
|
||||
~KExiv2Previews();
|
||||
|
||||
/// Returns the pixel size of the original image, as read from the file (not the metadata).
|
||||
QSize originalSize() const;
|
||||
|
||||
/// Returns the mimeType of the original image, detected from the file's content.
|
||||
QString originalMimeType() const;
|
||||
|
||||
/// Returns if there are any preview images available
|
||||
bool isEmpty();
|
||||
|
||||
/// Returns how many embedded previews are available
|
||||
int count();
|
||||
int size() { return count(); }
|
||||
|
||||
/**
|
||||
* For each contained preview image, return the size
|
||||
* of the image data in bytes, width and height of the preview,
|
||||
* the mimeType and the file extension.
|
||||
* Ensure that index < count().
|
||||
* Previews are sorted by width*height, largest first.
|
||||
*/
|
||||
int dataSize(int index = 0);
|
||||
int width(int index = 0);
|
||||
int height(int index = 0);
|
||||
QString mimeType(int index = 0);
|
||||
QString fileExtension(int index = 0);
|
||||
|
||||
/**
|
||||
* Retrieve the image data for the specified embedded preview image
|
||||
*/
|
||||
QByteArray data(int index = 0);
|
||||
|
||||
/**
|
||||
* Loads the data of the specified preview and creates a QImage
|
||||
* from this data. Returns a null QImage if the loading failed.
|
||||
*/
|
||||
QImage image(int index = 0);
|
||||
|
||||
private:
|
||||
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif // KEXIV2PREVIEWS
|
1220
libkexiv2/libkexiv2/kexiv2xmp.cpp
Normal file
1220
libkexiv2/libkexiv2/kexiv2xmp.cpp
Normal file
File diff suppressed because it is too large
Load diff
47
libkexiv2/libkexiv2/libkexiv2_export.h
Normal file
47
libkexiv2/libkexiv2/libkexiv2_export.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is part of the KDE project
|
||||
*
|
||||
* @brief Helper for exporting functions/classes from the shared library
|
||||
*
|
||||
* @author Copyright (C) 2007 David Faure <faure@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef LIBKEXIV2_EXPORT_H
|
||||
#define LIBKEXIV2_EXPORT_H
|
||||
|
||||
/* needed for KDE_EXPORT and KDE_IMPORT macros */
|
||||
#include <kdemacros.h>
|
||||
|
||||
#ifndef KEXIV2_EXPORT
|
||||
# if defined(MAKE_KEXIV2_LIB)
|
||||
/* We are building this library */
|
||||
# define KEXIV2_EXPORT KDE_EXPORT
|
||||
# else
|
||||
/* We are using this library */
|
||||
# define KEXIV2_EXPORT KDE_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# ifndef KEXIV2_EXPORT_DEPRECATED
|
||||
# define KEXIV2_EXPORT_DEPRECATED KDE_DEPRECATED KEXIV2_EXPORT
|
||||
# endif
|
||||
|
||||
#endif
|
109
libkexiv2/libkexiv2/msgtextedit.cpp
Normal file
109
libkexiv2/libkexiv2/msgtextedit.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-07-15
|
||||
* @brief a text edit widget with click message.
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QColor>
|
||||
#include <QPalette>
|
||||
#include <QPainter>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "moc_msgtextedit.cpp"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class MsgTextEdit::Private
|
||||
{
|
||||
public:
|
||||
|
||||
Private(){}
|
||||
|
||||
QString message;
|
||||
};
|
||||
|
||||
MsgTextEdit::MsgTextEdit(QWidget* parent)
|
||||
: KTextEdit(parent), d(new Private)
|
||||
{
|
||||
setAcceptRichText(false);
|
||||
}
|
||||
|
||||
MsgTextEdit::~MsgTextEdit()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QString MsgTextEdit::clickMessage() const
|
||||
{
|
||||
return d->message;
|
||||
}
|
||||
|
||||
void MsgTextEdit::setClickMessage(const QString& msg)
|
||||
{
|
||||
d->message = msg;
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void MsgTextEdit::setText(const QString& txt)
|
||||
{
|
||||
KTextEdit::setText(txt);
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void MsgTextEdit::paintEvent(QPaintEvent* e)
|
||||
{
|
||||
KTextEdit::paintEvent(e);
|
||||
|
||||
if (toPlainText().isEmpty() && !hasFocus())
|
||||
{
|
||||
QPainter p(viewport());
|
||||
QPen tmp = p.pen();
|
||||
p.setPen(palette().color(QPalette::Disabled, QPalette::Text));
|
||||
QRect cr = contentsRect();
|
||||
p.drawText(cr, Qt::AlignTop, d->message);
|
||||
p.setPen(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void MsgTextEdit::dropEvent(QDropEvent* e)
|
||||
{
|
||||
viewport()->update();
|
||||
KTextEdit::dropEvent(e);
|
||||
}
|
||||
|
||||
void MsgTextEdit::focusInEvent(QFocusEvent* e)
|
||||
{
|
||||
viewport()->update();
|
||||
KTextEdit::focusInEvent(e);
|
||||
}
|
||||
|
||||
void MsgTextEdit::focusOutEvent(QFocusEvent* e)
|
||||
{
|
||||
viewport()->update();
|
||||
KTextEdit::focusOutEvent(e);
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
74
libkexiv2/libkexiv2/msgtextedit.h
Normal file
74
libkexiv2/libkexiv2/msgtextedit.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-07-15
|
||||
* @brief a text edit widget with click message.
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef MSGTEXTEDIT_H
|
||||
#define MSGTEXTEDIT_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtCore/QString>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <ktextedit.h>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT MsgTextEdit : public KTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MsgTextEdit(QWidget* parent);
|
||||
~MsgTextEdit();
|
||||
|
||||
void setClickMessage(const QString& msg);
|
||||
QString clickMessage() const;
|
||||
|
||||
void setText(const QString& txt);
|
||||
|
||||
protected:
|
||||
|
||||
void paintEvent(QPaintEvent*);
|
||||
void dropEvent(QDropEvent*);
|
||||
void focusInEvent(QFocusEvent*);
|
||||
void focusOutEvent(QFocusEvent*);
|
||||
|
||||
private:
|
||||
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif /* MSGTEXTEDIT_H */
|
340
libkexiv2/libkexiv2/rotationmatrix.cpp
Normal file
340
libkexiv2/libkexiv2/rotationmatrix.cpp
Normal file
|
@ -0,0 +1,340 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-08-03
|
||||
* @brief Tools for combining rotation operations
|
||||
*
|
||||
* @author Copyright (C) 2006-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2004-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "rotationmatrix.h"
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <kdebug.h>
|
||||
|
||||
// local includes
|
||||
|
||||
#include "version.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
/**
|
||||
If the picture is displayed according to the exif orientation tag,
|
||||
the user will request rotating operations relative to what he sees,
|
||||
and that is the picture rotated according to the EXIF tag.
|
||||
So the operation requested and the given EXIF angle must be combined.
|
||||
E.g. if orientation is "6" (rotate 90 clockwiseto show correctly)
|
||||
and the user selects 180 clockwise, the operation is 270.
|
||||
If the user selected 270, the operation would be None (and clearing the exif tag).
|
||||
|
||||
This requires to describe the transformations in a model which
|
||||
cares for both composing (180+90=270) and eliminating (180+180=no action),
|
||||
as well as the non-commutative nature of the operations (vflip+90 is not 90+vflip)
|
||||
|
||||
All 2D transformations can be described by a 2x3 matrix, see QWRotationMatrix.
|
||||
All transformations needed here - rotate 90, 180, 270, flipV, flipH -
|
||||
can be described in a 2x2 matrix with the values 0,1,-1
|
||||
(because flipping is expressed by changing the sign only,
|
||||
and sine and cosine of 90, 180 and 270 are either 0,1 or -1).
|
||||
|
||||
x' = m11 x + m12 y
|
||||
y' = m21 x + m22 y
|
||||
|
||||
Moreover, all combinations of these rotate/flip operations result in one of the eight
|
||||
matrices defined below.
|
||||
(I did not proof that mathematically, but empirically)
|
||||
|
||||
static const RotationMatrix identity; //( 1, 0, 0, 1)
|
||||
static const RotationMatrix rotate90; //( 0, -1, 1, 0)
|
||||
static const RotationMatrix rotate180; //(-1, 0, 0, -1)
|
||||
static const RotationMatrix rotate270; //( 0, 1, -1, 0)
|
||||
static const RotationMatrix flipHorizontal; //(-1, 0, 0, 1)
|
||||
static const RotationMatrix flipVertical; //( 1, 0, 0, -1)
|
||||
static const RotationMatrix rotate90flipHorizontal; //( 0, 1, 1, 0), first rotate, then flip
|
||||
static const RotationMatrix rotate90flipVertical; //( 0, -1, -1, 0), first rotate, then flip
|
||||
|
||||
*/
|
||||
|
||||
namespace Matrix
|
||||
{
|
||||
|
||||
static const RotationMatrix identity ( 1, 0, 0, 1);
|
||||
static const RotationMatrix rotate90 ( 0, -1, 1, 0);
|
||||
static const RotationMatrix rotate180 (-1, 0, 0, -1);
|
||||
static const RotationMatrix rotate270 ( 0, 1, -1, 0);
|
||||
static const RotationMatrix flipHorizontal (-1, 0, 0, 1);
|
||||
static const RotationMatrix flipVertical ( 1, 0, 0, -1);
|
||||
static const RotationMatrix rotate90flipHorizontal ( 0, 1, 1, 0);
|
||||
static const RotationMatrix rotate90flipVertical ( 0, -1, -1, 0);
|
||||
|
||||
RotationMatrix matrix(RotationMatrix::TransformationAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case RotationMatrix::NoTransformation:
|
||||
return identity;
|
||||
case RotationMatrix::FlipHorizontal:
|
||||
return flipHorizontal;
|
||||
case RotationMatrix::FlipVertical:
|
||||
return flipVertical;
|
||||
case RotationMatrix::Rotate90:
|
||||
return rotate90;
|
||||
case RotationMatrix::Rotate180:
|
||||
return rotate180;
|
||||
case RotationMatrix::Rotate270:
|
||||
return rotate270;
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
RotationMatrix matrix(KExiv2::ImageOrientation exifOrientation)
|
||||
{
|
||||
switch (exifOrientation)
|
||||
{
|
||||
case KExiv2::ORIENTATION_NORMAL:
|
||||
return identity;
|
||||
case KExiv2::ORIENTATION_HFLIP:
|
||||
return flipHorizontal;
|
||||
case KExiv2::ORIENTATION_ROT_180:
|
||||
return rotate180;
|
||||
case KExiv2::ORIENTATION_VFLIP:
|
||||
return flipVertical;
|
||||
case KExiv2::ORIENTATION_ROT_90_HFLIP:
|
||||
return rotate90flipHorizontal;
|
||||
case KExiv2::ORIENTATION_ROT_90:
|
||||
return rotate90;
|
||||
case KExiv2::ORIENTATION_ROT_90_VFLIP:
|
||||
return rotate90flipVertical;
|
||||
case KExiv2::ORIENTATION_ROT_270:
|
||||
return rotate270;
|
||||
case KExiv2::ORIENTATION_UNSPECIFIED:
|
||||
return identity;
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Matrix
|
||||
|
||||
RotationMatrix::RotationMatrix()
|
||||
{
|
||||
set( 1, 0, 0, 1 );
|
||||
}
|
||||
|
||||
RotationMatrix::RotationMatrix(TransformationAction action)
|
||||
{
|
||||
*this = Matrix::matrix(action);
|
||||
}
|
||||
|
||||
RotationMatrix::RotationMatrix(KExiv2::ImageOrientation exifOrientation)
|
||||
{
|
||||
*this = Matrix::matrix(exifOrientation);
|
||||
}
|
||||
|
||||
RotationMatrix::RotationMatrix(int m11, int m12, int m21, int m22)
|
||||
{
|
||||
set(m11, m12, m21, m22);
|
||||
}
|
||||
|
||||
void RotationMatrix::set(int m11, int m12, int m21, int m22)
|
||||
{
|
||||
m[0][0]=m11;
|
||||
m[0][1]=m12;
|
||||
m[1][0]=m21;
|
||||
m[1][1]=m22;
|
||||
}
|
||||
|
||||
bool RotationMatrix::isNoTransform() const
|
||||
{
|
||||
return *this == Matrix::identity;
|
||||
}
|
||||
|
||||
RotationMatrix& RotationMatrix::operator*=(const RotationMatrix& ma)
|
||||
{
|
||||
set( ma.m[0][0]*m[0][0] + ma.m[0][1]*m[1][0], ma.m[0][0]*m[0][1] + ma.m[0][1]*m[1][1],
|
||||
ma.m[1][0]*m[0][0] + ma.m[1][1]*m[1][0], ma.m[1][0]*m[0][1] + ma.m[1][1]*m[1][1] );
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool RotationMatrix::operator==(const RotationMatrix& ma) const
|
||||
{
|
||||
return m[0][0]==ma.m[0][0] &&
|
||||
m[0][1]==ma.m[0][1] &&
|
||||
m[1][0]==ma.m[1][0] &&
|
||||
m[1][1]==ma.m[1][1];
|
||||
}
|
||||
|
||||
bool RotationMatrix::operator!=(const RotationMatrix& ma) const
|
||||
{
|
||||
return !(*this==ma);
|
||||
}
|
||||
|
||||
RotationMatrix& RotationMatrix::operator*=(TransformationAction action)
|
||||
{
|
||||
return (*this *= Matrix::matrix(action));
|
||||
}
|
||||
|
||||
RotationMatrix& RotationMatrix::operator*=(QList<TransformationAction> actions)
|
||||
{
|
||||
foreach(const TransformationAction& action, actions)
|
||||
{
|
||||
*this *= Matrix::matrix(action);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
RotationMatrix& RotationMatrix::operator*=(KExiv2::ImageOrientation exifOrientation)
|
||||
{
|
||||
return (*this *= Matrix::matrix(exifOrientation));
|
||||
}
|
||||
|
||||
/** Converts the mathematically correct description
|
||||
into the primitive operations that can be carried out losslessly.
|
||||
*/
|
||||
QList<RotationMatrix::TransformationAction> RotationMatrix::transformations() const
|
||||
{
|
||||
QList<TransformationAction> transforms;
|
||||
|
||||
if (*this == Matrix::rotate90)
|
||||
{
|
||||
transforms << Rotate90;
|
||||
}
|
||||
else if (*this == Matrix::rotate180)
|
||||
{
|
||||
transforms << Rotate180;
|
||||
}
|
||||
else if (*this == Matrix::rotate270)
|
||||
{
|
||||
transforms << Rotate270;
|
||||
}
|
||||
else if (*this == Matrix::flipHorizontal)
|
||||
{
|
||||
transforms << FlipHorizontal;
|
||||
}
|
||||
else if (*this == Matrix::flipVertical)
|
||||
{
|
||||
transforms << FlipVertical;
|
||||
}
|
||||
else if (*this == Matrix::rotate90flipHorizontal)
|
||||
{
|
||||
//first rotate, then flip!
|
||||
transforms << Rotate90;
|
||||
transforms << FlipHorizontal;
|
||||
}
|
||||
else if (*this == Matrix::rotate90flipVertical)
|
||||
{
|
||||
//first rotate, then flip!
|
||||
transforms << Rotate90;
|
||||
transforms << FlipVertical;
|
||||
}
|
||||
return transforms;
|
||||
}
|
||||
|
||||
KExiv2::ImageOrientation RotationMatrix::exifOrientation() const
|
||||
{
|
||||
if (*this == Matrix::identity)
|
||||
{
|
||||
return KExiv2::ORIENTATION_NORMAL;
|
||||
}
|
||||
if (*this == Matrix::rotate90)
|
||||
{
|
||||
return KExiv2::ORIENTATION_ROT_90;
|
||||
}
|
||||
else if (*this == Matrix::rotate180)
|
||||
{
|
||||
return KExiv2::ORIENTATION_ROT_180;
|
||||
}
|
||||
else if (*this == Matrix::rotate270)
|
||||
{
|
||||
return KExiv2::ORIENTATION_ROT_270;
|
||||
}
|
||||
else if (*this == Matrix::flipHorizontal)
|
||||
{
|
||||
return KExiv2::ORIENTATION_HFLIP;
|
||||
}
|
||||
else if (*this == Matrix::flipVertical)
|
||||
{
|
||||
return KExiv2::ORIENTATION_VFLIP;
|
||||
}
|
||||
else if (*this == Matrix::rotate90flipHorizontal)
|
||||
{
|
||||
return KExiv2::ORIENTATION_ROT_90_HFLIP;
|
||||
}
|
||||
else if (*this == Matrix::rotate90flipVertical)
|
||||
{
|
||||
return KExiv2::ORIENTATION_ROT_90_VFLIP;
|
||||
}
|
||||
return KExiv2::ORIENTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
QMatrix RotationMatrix::toMatrix() const
|
||||
{
|
||||
return toMatrix(exifOrientation());
|
||||
}
|
||||
|
||||
QMatrix RotationMatrix::toMatrix(KExiv2::ImageOrientation orientation)
|
||||
{
|
||||
QMatrix matrix;
|
||||
|
||||
switch (orientation)
|
||||
{
|
||||
case KExiv2::ORIENTATION_NORMAL:
|
||||
case KExiv2::ORIENTATION_UNSPECIFIED:
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_HFLIP:
|
||||
matrix.scale(-1, 1);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_ROT_180:
|
||||
matrix.rotate(180);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_VFLIP:
|
||||
matrix.scale(1, -1);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_ROT_90_HFLIP:
|
||||
matrix.scale(-1, 1);
|
||||
matrix.rotate(90);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_ROT_90:
|
||||
matrix.rotate(90);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_ROT_90_VFLIP:
|
||||
matrix.scale(1, -1);
|
||||
matrix.rotate(90);
|
||||
break;
|
||||
|
||||
case KExiv2::ORIENTATION_ROT_270:
|
||||
matrix.rotate(270);
|
||||
break;
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
118
libkexiv2/libkexiv2/rotationmatrix.h
Normal file
118
libkexiv2/libkexiv2/rotationmatrix.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-08-03
|
||||
* @brief Tools for combining rotation operations
|
||||
*
|
||||
* @author Copyright (C) 2006-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2004-2012 by Marcel Wiesweg
|
||||
* <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef LIBKEXIV2_ROTATIONMATRIX_H
|
||||
#define LIBKEXIV2_ROTATIONMATRIX_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtGui/QMatrix>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT RotationMatrix
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/** This describes single transform primitives.
|
||||
* Note some of the defined Exif rotation flags combine
|
||||
* two of these actions.
|
||||
* The enum values correspond to those defined
|
||||
* as JXFORM_CODE in the often used the JPEG tool transupp.h.
|
||||
*/
|
||||
enum TransformationAction
|
||||
{
|
||||
NoTransformation = 0, /// no transformation
|
||||
FlipHorizontal = 1, /// horizontal flip
|
||||
FlipVertical = 2, /// vertical flip
|
||||
Rotate90 = 5, /// 90-degree clockwise rotation
|
||||
Rotate180 = 6, /// 180-degree rotation
|
||||
Rotate270 = 7 /// 270-degree clockwise (or 90 ccw)
|
||||
};
|
||||
|
||||
/// Constructs the identity matrix (the matrix describing no transformation)
|
||||
RotationMatrix();
|
||||
/// Returns the matrix corresponding to the given TransformationAction
|
||||
RotationMatrix(TransformationAction action);
|
||||
/// Returns the matrix corresponding to the given TransformationAction
|
||||
RotationMatrix(KExiv2::ImageOrientation exifOrientation);
|
||||
|
||||
bool operator==(const RotationMatrix& ma) const;
|
||||
bool operator!=(const RotationMatrix& ma) const;
|
||||
|
||||
/// Returns true of this matrix describes no transformation (is the identity matrix)
|
||||
bool isNoTransform() const;
|
||||
|
||||
RotationMatrix& operator*=(const RotationMatrix& ma);
|
||||
|
||||
/// Applies the given transform to this matrix
|
||||
RotationMatrix& operator*=(TransformationAction action);
|
||||
|
||||
/// Applies the given transform actions to this matrix
|
||||
RotationMatrix& operator*=(QList<TransformationAction> actions);
|
||||
|
||||
/// Applies the given Exif orientation flag to this matrix
|
||||
RotationMatrix& operator*=(KExiv2::ImageOrientation exifOrientation);
|
||||
|
||||
/** Returns the actions described by this matrix. The order matters.
|
||||
* Not all possible matrices are supported, but all those that can be combined
|
||||
* by Exif rotation flags and the transform actions above.
|
||||
* If isNoTransform() or the matrix is not supported returns an empty list. */
|
||||
QList<TransformationAction> transformations() const;
|
||||
|
||||
/** Returns the Exif orienation flag describing this matrix.
|
||||
* Returns ORIENTATION_UNSPECIFIED if no flag matches this matrix.
|
||||
*/
|
||||
KExiv2::ImageOrientation exifOrientation() const;
|
||||
|
||||
/// Returns a QMatrix representing this matrix
|
||||
QMatrix toMatrix() const;
|
||||
|
||||
/// Returns a QMatrix for the given Exif orientation
|
||||
static QMatrix toMatrix(KExiv2::ImageOrientation orientation);
|
||||
|
||||
RotationMatrix(int m11, int m12, int m21, int m22);
|
||||
|
||||
protected:
|
||||
|
||||
void set(int m11, int m12, int m21, int m22);
|
||||
|
||||
protected:
|
||||
|
||||
int m[2][2];
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif // LIBKEXIV2_ROTATIONMATRIX_H
|
601
libkexiv2/libkexiv2/subjectwidget.cpp
Normal file
601
libkexiv2/libkexiv2/subjectwidget.cpp
Normal file
|
@ -0,0 +1,601 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-10-15
|
||||
* @brief IPTC subjects editor.
|
||||
*
|
||||
* @author Copyright (C) 2006-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2009-2012 by Andi Clemens
|
||||
* <a href="mailto:andi dot clemens at googlemail dot com">andi dot clemens at googlemail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "moc_subjectwidget.cpp"
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QFile>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QValidator>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <kcombobox.h>
|
||||
#include <kdialog.h>
|
||||
#include <kglobal.h>
|
||||
#include <khbox.h>
|
||||
#include <kiconloader.h>
|
||||
#include <klineedit.h>
|
||||
#include <klistwidget.h>
|
||||
#include <klocale.h>
|
||||
#include <kstandarddirs.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class SubjectWidget::Private
|
||||
{
|
||||
public:
|
||||
|
||||
enum EditionMode
|
||||
{
|
||||
STANDARD = 0,
|
||||
CUSTOM
|
||||
};
|
||||
|
||||
Private()
|
||||
{
|
||||
addSubjectButton = 0;
|
||||
delSubjectButton = 0;
|
||||
repSubjectButton = 0;
|
||||
subjectsBox = 0;
|
||||
iprLabel = 0;
|
||||
refLabel = 0;
|
||||
nameLabel = 0;
|
||||
matterLabel = 0;
|
||||
detailLabel = 0;
|
||||
btnGroup = 0;
|
||||
stdBtn = 0;
|
||||
customBtn = 0;
|
||||
refCB = 0;
|
||||
optionsBox = 0;
|
||||
}
|
||||
|
||||
typedef QMap<QString, SubjectData> SubjectCodesMap;
|
||||
|
||||
SubjectCodesMap subMap;
|
||||
|
||||
QStringList subjectsList;
|
||||
|
||||
QWidget* optionsBox;
|
||||
|
||||
QPushButton* addSubjectButton;
|
||||
QPushButton* delSubjectButton;
|
||||
QPushButton* repSubjectButton;
|
||||
|
||||
QLabel* iprLabel;
|
||||
QLabel* refLabel;
|
||||
QLabel* nameLabel;
|
||||
QLabel* matterLabel;
|
||||
QLabel* detailLabel;
|
||||
|
||||
QButtonGroup* btnGroup;
|
||||
|
||||
QRadioButton* stdBtn;
|
||||
QRadioButton* customBtn;
|
||||
|
||||
KComboBox* refCB;
|
||||
|
||||
KListWidget* subjectsBox;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
SubjectWidget::SubjectWidget(QWidget* parent)
|
||||
: QWidget(parent), d(new Private)
|
||||
{
|
||||
// Load subject codes provided by IPTC/NAA as xml file.
|
||||
// See http://iptc.cms.apa.at/std/topicset/topicset.iptc-subjectcode.xml for details.
|
||||
|
||||
KGlobal::dirs()->addResourceDir("iptcschema", KStandardDirs::installPath("data") +
|
||||
QString("libkexiv2/data"));
|
||||
QString path = KGlobal::dirs()->findResource("iptcschema", "topicset.iptc-subjectcode.xml");
|
||||
|
||||
if (!loadSubjectCodesFromXML(KUrl(path)))
|
||||
kDebug() << "Cannot load IPTC/NAA subject codes XML database";
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
// Subject Reference Number only accept digit.
|
||||
QRegExp refDigitRx("^[0-9]{8}$");
|
||||
QValidator *refValidator = new QRegExpValidator(refDigitRx, this);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_subjectsCheck = new QCheckBox(i18n("Use structured definition of the subject matter:"), this);
|
||||
d->optionsBox = new QWidget;
|
||||
d->btnGroup = new QButtonGroup(this);
|
||||
d->stdBtn = new QRadioButton;
|
||||
d->customBtn = new QRadioButton;
|
||||
d->refCB = new KComboBox;
|
||||
QLabel* codeLink = new QLabel(i18n("Use standard "
|
||||
"<b><a href='http://www.iptc.org/site/NewsCodes'>"
|
||||
"reference code</a></b>"));
|
||||
codeLink->setOpenExternalLinks(true);
|
||||
codeLink->setWordWrap(false);
|
||||
|
||||
// By default, check box is not visible. (digiKam do not use it, kipi-plugins yes).
|
||||
m_subjectsCheck->setVisible(false);
|
||||
|
||||
QLabel* customLabel = new QLabel(i18n("Use custom definition"));
|
||||
|
||||
d->btnGroup->addButton(d->stdBtn, Private::STANDARD);
|
||||
d->btnGroup->addButton(d->customBtn, Private::CUSTOM);
|
||||
d->btnGroup->setExclusive(true);
|
||||
d->stdBtn->setChecked(true);
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it = d->subMap.begin();
|
||||
it != d->subMap.end(); ++it)
|
||||
d->refCB->addItem(it.key());
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_iprEdit = new KLineEdit;
|
||||
m_iprEdit->setClearButtonShown(true);
|
||||
m_iprEdit->setMaxLength(32);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_refEdit = new KLineEdit;
|
||||
m_refEdit->setClearButtonShown(true);
|
||||
m_refEdit->setValidator(refValidator);
|
||||
m_refEdit->setMaxLength(8);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_nameEdit = new KLineEdit;
|
||||
m_nameEdit->setClearButtonShown(true);
|
||||
m_nameEdit->setMaxLength(64);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_matterEdit = new KLineEdit;
|
||||
m_matterEdit->setClearButtonShown(true);
|
||||
m_matterEdit->setMaxLength(64);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_detailEdit = new KLineEdit;
|
||||
m_detailEdit->setClearButtonShown(true);
|
||||
m_detailEdit->setMaxLength(64);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
d->iprLabel = new QLabel(i18nc("Information Provider Reference: "
|
||||
"A name, registered with the IPTC/NAA, "
|
||||
"identifying the provider that guarantees "
|
||||
"the uniqueness of the UNO", "I.P.R:"));
|
||||
d->refLabel = new QLabel(i18n("Reference:"));
|
||||
d->nameLabel = new QLabel(i18n("Name:"));
|
||||
d->matterLabel = new QLabel(i18n("Matter:"));
|
||||
d->detailLabel = new QLabel(i18n("Detail:"));
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
d->subjectsBox = new KListWidget;
|
||||
d->subjectsBox->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
|
||||
d->addSubjectButton = new QPushButton(i18n("&Add"));
|
||||
d->delSubjectButton = new QPushButton(i18n("&Delete"));
|
||||
d->repSubjectButton = new QPushButton(i18n("&Replace"));
|
||||
d->addSubjectButton->setIcon(SmallIcon("list-add"));
|
||||
d->delSubjectButton->setIcon(SmallIcon("edit-delete"));
|
||||
d->repSubjectButton->setIcon(SmallIcon("view-refresh"));
|
||||
d->delSubjectButton->setEnabled(false);
|
||||
d->repSubjectButton->setEnabled(false);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
m_note = new QLabel;
|
||||
m_note->setMaximumWidth(150);
|
||||
m_note->setOpenExternalLinks(true);
|
||||
m_note->setWordWrap(true);
|
||||
m_note->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
QGridLayout* optionsBoxLayout = new QGridLayout;
|
||||
optionsBoxLayout->addWidget(d->stdBtn, 0, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(codeLink, 0, 1, 1, 2);
|
||||
optionsBoxLayout->addWidget(d->refCB, 0, 3, 1, 1);
|
||||
optionsBoxLayout->addWidget(d->customBtn, 1, 0, 1, 4);
|
||||
optionsBoxLayout->addWidget(customLabel, 1, 1, 1, 4);
|
||||
optionsBoxLayout->addWidget(d->iprLabel, 2, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(m_iprEdit, 2, 1, 1, 4);
|
||||
optionsBoxLayout->addWidget(d->refLabel, 3, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(m_refEdit, 3, 1, 1, 1);
|
||||
optionsBoxLayout->addWidget(d->nameLabel, 4, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(m_nameEdit, 4, 1, 1, 4);
|
||||
optionsBoxLayout->addWidget(d->matterLabel, 5, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(m_matterEdit, 5, 1, 1, 4);
|
||||
optionsBoxLayout->addWidget(d->detailLabel, 6, 0, 1, 1);
|
||||
optionsBoxLayout->addWidget(m_detailEdit, 6, 1, 1, 4);
|
||||
optionsBoxLayout->setColumnStretch(4, 10);
|
||||
optionsBoxLayout->setMargin(0);
|
||||
optionsBoxLayout->setSpacing(KDialog::spacingHint());
|
||||
d->optionsBox->setLayout(optionsBoxLayout);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
QGridLayout* mainLayout = new QGridLayout;
|
||||
mainLayout->setAlignment( Qt::AlignTop );
|
||||
mainLayout->addWidget(m_subjectsCheck, 0, 0, 1, 4);
|
||||
mainLayout->addWidget(d->optionsBox, 1, 0, 1, 4);
|
||||
mainLayout->addWidget(d->subjectsBox, 2, 0, 5, 3);
|
||||
mainLayout->addWidget(d->addSubjectButton, 2, 3, 1, 1);
|
||||
mainLayout->addWidget(d->delSubjectButton, 3, 3, 1, 1);
|
||||
mainLayout->addWidget(d->repSubjectButton, 4, 3, 1, 1);
|
||||
mainLayout->addWidget(m_note, 5, 3, 1, 1);
|
||||
mainLayout->setRowStretch(6, 10);
|
||||
mainLayout->setColumnStretch(2, 1);
|
||||
mainLayout->setMargin(0);
|
||||
mainLayout->setSpacing(KDialog::spacingHint());
|
||||
setLayout(mainLayout);
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
connect(d->subjectsBox, SIGNAL(itemSelectionChanged()),
|
||||
this, SLOT(slotSubjectSelectionChanged()));
|
||||
|
||||
connect(d->addSubjectButton, SIGNAL(clicked()),
|
||||
this, SLOT(slotAddSubject()));
|
||||
|
||||
connect(d->delSubjectButton, SIGNAL(clicked()),
|
||||
this, SLOT(slotDelSubject()));
|
||||
|
||||
connect(d->repSubjectButton, SIGNAL(clicked()),
|
||||
this, SLOT(slotRepSubject()));
|
||||
|
||||
connect(d->btnGroup, SIGNAL(buttonReleased(int)),
|
||||
this, SLOT(slotEditOptionChanged(int)));
|
||||
|
||||
connect(d->refCB, SIGNAL(activated(int)),
|
||||
this, SLOT(slotRefChanged()));
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
connect(m_subjectsCheck, SIGNAL(toggled(bool)),
|
||||
this, SLOT(slotSubjectsToggled(bool)));
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
connect(m_subjectsCheck, SIGNAL(toggled(bool)),
|
||||
this, SIGNAL(signalModified()));
|
||||
|
||||
connect(d->addSubjectButton, SIGNAL(clicked()),
|
||||
this, SIGNAL(signalModified()));
|
||||
|
||||
connect(d->delSubjectButton, SIGNAL(clicked()),
|
||||
this, SIGNAL(signalModified()));
|
||||
|
||||
connect(d->repSubjectButton, SIGNAL(clicked()),
|
||||
this, SIGNAL(signalModified()));
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
slotEditOptionChanged(d->btnGroup->id(d->btnGroup->checkedButton()));
|
||||
}
|
||||
|
||||
SubjectWidget::~SubjectWidget()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void SubjectWidget::slotSubjectsToggled(bool b)
|
||||
{
|
||||
d->optionsBox->setEnabled(b);
|
||||
d->subjectsBox->setEnabled(b);
|
||||
d->addSubjectButton->setEnabled(b);
|
||||
d->delSubjectButton->setEnabled(b);
|
||||
d->repSubjectButton->setEnabled(b);
|
||||
slotEditOptionChanged(d->btnGroup->id(d->btnGroup->checkedButton()));
|
||||
}
|
||||
|
||||
void SubjectWidget::slotEditOptionChanged(int b)
|
||||
{
|
||||
if (b == Private::CUSTOM)
|
||||
{
|
||||
d->refCB->setEnabled(false);
|
||||
m_iprEdit->setEnabled(true);
|
||||
m_refEdit->setEnabled(true);
|
||||
m_nameEdit->setEnabled(true);
|
||||
m_matterEdit->setEnabled(true);
|
||||
m_detailEdit->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->refCB->setEnabled(true);
|
||||
m_iprEdit->setEnabled(false);
|
||||
m_refEdit->setEnabled(false);
|
||||
m_nameEdit->setEnabled(false);
|
||||
m_matterEdit->setEnabled(false);
|
||||
m_detailEdit->setEnabled(false);
|
||||
slotRefChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SubjectWidget::slotRefChanged()
|
||||
{
|
||||
QString key = d->refCB->currentText();
|
||||
QString name, matter, detail;
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it = d->subMap.begin();
|
||||
it != d->subMap.end(); ++it)
|
||||
{
|
||||
if (key == it.key())
|
||||
{
|
||||
name = it.value().name;
|
||||
matter = it.value().matter;
|
||||
detail = it.value().detail;
|
||||
}
|
||||
}
|
||||
|
||||
m_refEdit->setText(key);
|
||||
m_nameEdit->setText(name);
|
||||
m_matterEdit->setText(matter);
|
||||
m_detailEdit->setText(detail);
|
||||
}
|
||||
|
||||
QString SubjectWidget::buildSubject() const
|
||||
{
|
||||
QString subject = m_iprEdit->text();
|
||||
subject.append(":");
|
||||
subject.append(m_refEdit->text());
|
||||
subject.append(":");
|
||||
subject.append(m_nameEdit->text());
|
||||
subject.append(":");
|
||||
subject.append(m_matterEdit->text());
|
||||
subject.append(":");
|
||||
subject.append(m_detailEdit->text());
|
||||
return subject;
|
||||
}
|
||||
|
||||
void SubjectWidget::slotDelSubject()
|
||||
{
|
||||
QListWidgetItem* item = d->subjectsBox->currentItem();
|
||||
if (!item) return;
|
||||
d->subjectsBox->takeItem(d->subjectsBox->row(item));
|
||||
delete item;
|
||||
}
|
||||
|
||||
void SubjectWidget::slotRepSubject()
|
||||
{
|
||||
QString newSubject = buildSubject();
|
||||
if (newSubject.isEmpty()) return;
|
||||
|
||||
if (!d->subjectsBox->selectedItems().isEmpty())
|
||||
{
|
||||
d->subjectsBox->selectedItems()[0]->setText(newSubject);
|
||||
m_iprEdit->clear();
|
||||
m_refEdit->clear();
|
||||
m_nameEdit->clear();
|
||||
m_matterEdit->clear();
|
||||
m_detailEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SubjectWidget::slotSubjectSelectionChanged()
|
||||
{
|
||||
if (!d->subjectsBox->selectedItems().isEmpty())
|
||||
{
|
||||
QString subject = d->subjectsBox->selectedItems()[0]->text();
|
||||
m_iprEdit->setText(subject.section(':', 0, 0));
|
||||
m_refEdit->setText(subject.section(':', 1, 1));
|
||||
m_nameEdit->setText(subject.section(':', 2, 2));
|
||||
m_matterEdit->setText(subject.section(':', 3, 3));
|
||||
m_detailEdit->setText(subject.section(':', 4, 4));
|
||||
d->delSubjectButton->setEnabled(true);
|
||||
d->repSubjectButton->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->delSubjectButton->setEnabled(false);
|
||||
d->repSubjectButton->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SubjectWidget::slotAddSubject()
|
||||
{
|
||||
QString newSubject = buildSubject();
|
||||
if (newSubject.isEmpty()) return;
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0 ; i < d->subjectsBox->count(); i++)
|
||||
{
|
||||
QListWidgetItem* item = d->subjectsBox->item(i);
|
||||
if (newSubject == item->text())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
d->subjectsBox->insertItem(d->subjectsBox->count(), newSubject);
|
||||
m_iprEdit->clear();
|
||||
m_refEdit->clear();
|
||||
m_nameEdit->clear();
|
||||
m_matterEdit->clear();
|
||||
m_detailEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool SubjectWidget::loadSubjectCodesFromXML(const KUrl& url)
|
||||
{
|
||||
QFile xmlfile(url.toLocalFile());
|
||||
|
||||
if (!xmlfile.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
QDomDocument xmlDoc("NewsML");
|
||||
if (!xmlDoc.setContent(&xmlfile))
|
||||
return false;
|
||||
|
||||
QDomElement xmlDocElem = xmlDoc.documentElement();
|
||||
if (xmlDocElem.tagName()!="NewsML")
|
||||
return false;
|
||||
|
||||
for (QDomNode nbE1 = xmlDocElem.firstChild();
|
||||
!nbE1.isNull(); nbE1 = nbE1.nextSibling())
|
||||
{
|
||||
QDomElement newsItemElement = nbE1.toElement();
|
||||
if (newsItemElement.isNull()) continue;
|
||||
if (newsItemElement.tagName() != "NewsItem") continue;
|
||||
|
||||
for (QDomNode nbE2 = newsItemElement.firstChild();
|
||||
!nbE2.isNull(); nbE2 = nbE2.nextSibling())
|
||||
{
|
||||
QDomElement topicSetElement = nbE2.toElement();
|
||||
if (topicSetElement.isNull()) continue;
|
||||
if (topicSetElement.tagName() != "TopicSet") continue;
|
||||
|
||||
for (QDomNode nbE3 = topicSetElement.firstChild();
|
||||
!nbE3.isNull(); nbE3 = nbE3.nextSibling())
|
||||
{
|
||||
QDomElement topicElement = nbE3.toElement();
|
||||
if (topicElement.isNull()) continue;
|
||||
if (topicElement.tagName() != "Topic") continue;
|
||||
|
||||
QString type, name, matter, detail, ref;
|
||||
for (QDomNode nbE4 = topicElement.firstChild();
|
||||
!nbE4.isNull(); nbE4 = nbE4.nextSibling())
|
||||
{
|
||||
QDomElement topicSubElement = nbE4.toElement();
|
||||
if (topicSubElement.isNull()) continue;
|
||||
|
||||
if (topicSubElement.tagName() == "TopicType")
|
||||
type = topicSubElement.attribute("FormalName");
|
||||
|
||||
if (topicSubElement.tagName() == "FormalName")
|
||||
ref = topicSubElement.text();
|
||||
|
||||
if (topicSubElement.tagName() == "Description" &&
|
||||
topicSubElement.attribute("Variant") == "Name")
|
||||
{
|
||||
if (type == "Subject")
|
||||
name = topicSubElement.text();
|
||||
else if (type == "SubjectMatter")
|
||||
matter = topicSubElement.text();
|
||||
else if (type == "SubjectDetail")
|
||||
detail = topicSubElement.text();
|
||||
}
|
||||
}
|
||||
|
||||
d->subMap.insert(ref, SubjectData(name, matter, detail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Subject Name everywhere on the map.
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it = d->subMap.begin();
|
||||
it != d->subMap.end(); ++it)
|
||||
{
|
||||
QString name, keyPrefix;
|
||||
if (it.key().endsWith(QLatin1String("00000")))
|
||||
{
|
||||
keyPrefix = it.key().left(3);
|
||||
name = it.value().name;
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it2 = d->subMap.begin();
|
||||
it2 != d->subMap.end(); ++it2)
|
||||
{
|
||||
if (it2.key().startsWith(keyPrefix) &&
|
||||
!it2.key().endsWith(QLatin1String("00000")))
|
||||
{
|
||||
it2.value().name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Subject Matter Name everywhere on the map.
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it = d->subMap.begin();
|
||||
it != d->subMap.end(); ++it)
|
||||
{
|
||||
QString matter, keyPrefix;
|
||||
if (it.key().endsWith(QLatin1String("000")))
|
||||
{
|
||||
keyPrefix = it.key().left(5);
|
||||
matter = it.value().matter;
|
||||
|
||||
for (Private::SubjectCodesMap::Iterator it2 = d->subMap.begin();
|
||||
it2 != d->subMap.end(); ++it2)
|
||||
{
|
||||
if (it2.key().startsWith(keyPrefix) &&
|
||||
!it2.key().endsWith(QLatin1String("000")))
|
||||
{
|
||||
it2.value().matter = matter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SubjectWidget::setSubjectsList(const QStringList& list)
|
||||
{
|
||||
d->subjectsList = list;
|
||||
|
||||
blockSignals(true);
|
||||
d->subjectsBox->clear();
|
||||
m_subjectsCheck->setChecked(false);
|
||||
if (!d->subjectsList.isEmpty())
|
||||
{
|
||||
d->subjectsBox->insertItems(0, d->subjectsList);
|
||||
m_subjectsCheck->setChecked(true);
|
||||
}
|
||||
|
||||
blockSignals(false);
|
||||
slotSubjectsToggled(m_subjectsCheck->isChecked());
|
||||
}
|
||||
|
||||
QStringList SubjectWidget::subjectsList() const
|
||||
{
|
||||
QStringList newSubjects;
|
||||
|
||||
for (int i = 0 ; i < d->subjectsBox->count(); i++)
|
||||
{
|
||||
QListWidgetItem* item = d->subjectsBox->item(i);
|
||||
newSubjects.append(item->text());
|
||||
}
|
||||
|
||||
return newSubjects;
|
||||
}
|
||||
|
||||
} // namespace KExiv2Iface
|
122
libkexiv2/libkexiv2/subjectwidget.h
Normal file
122
libkexiv2/libkexiv2/subjectwidget.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/** ===========================================================
|
||||
* @file
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2006-10-15
|
||||
* @brief IPTC subjects editor.
|
||||
*
|
||||
* @author Copyright (C) 2006-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
* @author Copyright (C) 2009-2012 by Andi Clemens
|
||||
* <a href="mailto:andi dot clemens at googlemail dot com">andi dot clemens at googlemail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef SUBJECTWIDGET_H
|
||||
#define SUBJECTWIDGET_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QtGui/QButtonGroup>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtGui/QCheckBox>
|
||||
#include <QtGui/QLabel>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include <kurl.h>
|
||||
#include <klineedit.h>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "libkexiv2_export.h"
|
||||
|
||||
namespace KExiv2Iface
|
||||
{
|
||||
|
||||
class KEXIV2_EXPORT SubjectData
|
||||
{
|
||||
public:
|
||||
|
||||
SubjectData(const QString& n, const QString& m, const QString& d)
|
||||
{
|
||||
name = n;
|
||||
matter = m;
|
||||
detail = d;
|
||||
}
|
||||
|
||||
QString name; // English and Ascii Name of subject.
|
||||
QString matter; // English and Ascii Matter Name of subject.
|
||||
QString detail; // English and Ascii Detail Name of subject.
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
class KEXIV2_EXPORT SubjectWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
SubjectWidget(QWidget* parent);
|
||||
~SubjectWidget();
|
||||
|
||||
void setSubjectsList(const QStringList& list);
|
||||
QStringList subjectsList() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
void signalModified();
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
virtual void slotSubjectsToggled(bool);
|
||||
virtual void slotRefChanged();
|
||||
virtual void slotEditOptionChanged(int);
|
||||
virtual void slotSubjectSelectionChanged();
|
||||
virtual void slotAddSubject();
|
||||
virtual void slotDelSubject();
|
||||
virtual void slotRepSubject();
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool loadSubjectCodesFromXML(const KUrl& url);
|
||||
virtual QString buildSubject() const;
|
||||
|
||||
protected:
|
||||
|
||||
QLabel* m_note;
|
||||
|
||||
QCheckBox* m_subjectsCheck;
|
||||
|
||||
KLineEdit* m_iprEdit;
|
||||
KLineEdit* m_refEdit;
|
||||
KLineEdit* m_nameEdit;
|
||||
KLineEdit* m_matterEdit;
|
||||
KLineEdit* m_detailEdit;
|
||||
|
||||
private:
|
||||
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
} // namespace KExiv2Iface
|
||||
|
||||
#endif // SUBJECTWIDGET_H
|
14066
libkexiv2/libkexiv2/topicset.iptc-subjectcode.xml
Normal file
14066
libkexiv2/libkexiv2/topicset.iptc-subjectcode.xml
Normal file
File diff suppressed because it is too large
Load diff
32
libkexiv2/libkexiv2/version.h.cmake
Normal file
32
libkexiv2/libkexiv2/version.h.cmake
Normal file
|
@ -0,0 +1,32 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2007-02-06
|
||||
* @brief Exiv2 library interface for KDE
|
||||
*
|
||||
* @author Copyright (C) 2007-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef KEXIV2_VERSION_H
|
||||
#define KEXIV2_VERSION_H
|
||||
|
||||
static const char kexiv2_version[] = "${KEXIV2_LIB_VERSION_STRING}";
|
||||
|
||||
#define KEXIV2_VERSION ${KEXIV2_LIB_VERSION_ID}
|
||||
|
||||
#endif // KEXIV2_VERSION_H
|
53
libkexiv2/tests/CMakeLists.txt
Normal file
53
libkexiv2/tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
# ===========================================================
|
||||
#
|
||||
# This file is a part of digiKam project
|
||||
# <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
#
|
||||
# @date 2006-09-15
|
||||
# @brief Exiv2 library interface for KDE
|
||||
#
|
||||
# @author Copyright (C) 2006-2012 by Gilles Caulier
|
||||
# <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
#
|
||||
# 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, 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.
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../libkexiv2)
|
||||
|
||||
SET(setiptcpreview_SRCS setiptcpreview.cpp)
|
||||
KDE4_ADD_EXECUTABLE(setiptcpreview NOGUI ${setiptcpreview_SRCS})
|
||||
TARGET_LINK_LIBRARIES(setiptcpreview kexiv2 ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(loadfromba_SRCS loadfromba.cpp)
|
||||
KDE4_ADD_EXECUTABLE(loadfromba NOGUI ${loadfromba_SRCS})
|
||||
TARGET_LINK_LIBRARIES(loadfromba kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(erasetag_SRCS erasetag.cpp)
|
||||
KDE4_ADD_EXECUTABLE(erasetag NOGUI ${erasetag_SRCS})
|
||||
TARGET_LINK_LIBRARIES(erasetag kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(printtagslist_SRCS printtagslist.cpp)
|
||||
KDE4_ADD_EXECUTABLE(printtagslist NOGUI ${printtagslist_SRCS})
|
||||
TARGET_LINK_LIBRARIES(printtagslist kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(usexmpsidecar_SRCS usexmpsidecar.cpp)
|
||||
KDE4_ADD_EXECUTABLE(usexmpsidecar NOGUI ${usexmpsidecar_SRCS})
|
||||
TARGET_LINK_LIBRARIES(usexmpsidecar kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(readimagewritexmpsidecar_SRCS readimagewritexmpsidecar.cpp)
|
||||
KDE4_ADD_EXECUTABLE(readimagewritexmpsidecar NOGUI ${readimagewritexmpsidecar_SRCS})
|
||||
TARGET_LINK_LIBRARIES(readimagewritexmpsidecar kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
||||
|
||||
SET(setxmpface_SRCS setxmpface.cpp)
|
||||
KDE4_ADD_EXECUTABLE(setxmpface NOGUI ${setxmpface_SRCS})
|
||||
TARGET_LINK_LIBRARIES(setxmpface kexiv2 ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
|
66
libkexiv2/tests/erasetag.cpp
Normal file
66
libkexiv2/tests/erasetag.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-06-11
|
||||
* @brief a command line tool to tag from photo
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdebug.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
kDebug() << "erasetag - erase tag from from image";
|
||||
kDebug() << "Usage: <image>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString filePath(argv[1]);
|
||||
|
||||
KExiv2 meta;
|
||||
meta.load(filePath);
|
||||
meta.setWriteRawFiles(true);
|
||||
bool b = meta.removeExifTag("Exif.OlympusIp.BlackLevel", false);
|
||||
kDebug() << "Exif.OlympusIp.BlackLevel found = " << b;
|
||||
|
||||
QByteArray ba = meta.getExifTagData("Exif.OlympusIp.BlackLevel");
|
||||
kDebug() << "Exif.OlympusIp.BlackLevel removed = " << ba.isEmpty();
|
||||
|
||||
if (b)
|
||||
{
|
||||
meta.applyChanges();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
72
libkexiv2/tests/loadfromba.cpp
Normal file
72
libkexiv2/tests/loadfromba.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-06-11
|
||||
* @brief a command line tool to load metadata from byte array
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QByteArray>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdebug.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
kDebug() << "loadfromba - test to load metadata from image as byte array";
|
||||
kDebug() << "Usage: <image>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString filePath(argv[1]);
|
||||
QString baFile("ba.dat");
|
||||
|
||||
QImage image(filePath);
|
||||
image.save(baFile, "PNG");
|
||||
|
||||
QFile file(baFile);
|
||||
if ( !file.open(QIODevice::ReadOnly) )
|
||||
return false;
|
||||
|
||||
QByteArray data;
|
||||
data.resize(file.size());
|
||||
QDataStream stream( &file );
|
||||
stream.readRawData(data.data(), data.size());
|
||||
file.close();
|
||||
|
||||
KExiv2 meta;
|
||||
meta.loadFromData(data);
|
||||
|
||||
return 0;
|
||||
}
|
89
libkexiv2/tests/printtagslist.cpp
Normal file
89
libkexiv2/tests/printtagslist.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-07-12
|
||||
* @brief a command line tool to print all tags list supported by Exiv2
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
KExiv2 meta;
|
||||
|
||||
qDebug() << "-- Standard Exif Tags -------------------------------------------------------------";
|
||||
KExiv2::TagsMap exiftags = meta.getStdExifTagsList();
|
||||
for (KExiv2::TagsMap::const_iterator it = exiftags.constBegin(); it != exiftags.constEnd(); ++it )
|
||||
{
|
||||
QString key = it.key();
|
||||
QStringList values = it.value();
|
||||
QString name = values[0];
|
||||
QString title = values[1];
|
||||
QString desc = values[2];
|
||||
qDebug() << key << " :: " << name << " :: " << title << " :: " << desc;
|
||||
}
|
||||
|
||||
qDebug() << "-- Makernote Tags -----------------------------------------------------------------";
|
||||
KExiv2::TagsMap mntags = meta.getMakernoteTagsList();
|
||||
for (KExiv2::TagsMap::const_iterator it = mntags.constBegin(); it != mntags.constEnd(); ++it )
|
||||
{
|
||||
QString key = it.key();
|
||||
QStringList values = it.value();
|
||||
QString name = values[0];
|
||||
QString title = values[1];
|
||||
QString desc = values[2];
|
||||
qDebug() << key << " :: " << name << " :: " << title << " :: " << desc;
|
||||
}
|
||||
|
||||
qDebug() << "-- Standard Iptc Tags -----------------------------------------------------------------";
|
||||
KExiv2::TagsMap iptctags = meta.getIptcTagsList();
|
||||
for (KExiv2::TagsMap::const_iterator it = iptctags.constBegin(); it != iptctags.constEnd(); ++it )
|
||||
{
|
||||
QString key = it.key();
|
||||
QStringList values = it.value();
|
||||
QString name = values[0];
|
||||
QString title = values[1];
|
||||
QString desc = values[2];
|
||||
qDebug() << key << " :: " << name << " :: " << title << " :: " << desc;
|
||||
}
|
||||
|
||||
qDebug() << "-- Standard Xmp Tags -----------------------------------------------------------------";
|
||||
KExiv2::TagsMap xmptags = meta.getXmpTagsList();
|
||||
for (KExiv2::TagsMap::const_iterator it = xmptags.constBegin(); it != xmptags.constEnd(); ++it )
|
||||
{
|
||||
QString key = it.key();
|
||||
QStringList values = it.value();
|
||||
QString name = values[0];
|
||||
QString title = values[1];
|
||||
QString desc = values[2];
|
||||
qDebug() << key << " :: " << name << " :: " << title << " :: " << desc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
61
libkexiv2/tests/readimagewritexmpsidecar.cpp
Normal file
61
libkexiv2/tests/readimagewritexmpsidecar.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2010-06-27
|
||||
* @brief a command line tool to test XMP sidecar functionality
|
||||
*
|
||||
* @author Copyright (C) 2010 by Jakob Malm
|
||||
* <a href="mailto:jakob dot malm at gmail dot com">jakob dot malm at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdebug.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
kDebug() << "readimagewritecmpsidecar - read metadata from image and write to XMP sidecar";
|
||||
kDebug() << "Usage: <image>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString filePath(argv[1]);
|
||||
|
||||
KExiv2 meta;
|
||||
|
||||
// Read metadata from the image
|
||||
meta.load(filePath);
|
||||
|
||||
// Write metadata to XMP sidecar
|
||||
meta.setMetadataWritingMode(KExiv2::WRITETOSIDECARONLY);
|
||||
meta.save(filePath);
|
||||
|
||||
return 0;
|
||||
}
|
82
libkexiv2/tests/setiptcpreview.cpp
Normal file
82
libkexiv2/tests/setiptcpreview.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2009-02-04
|
||||
* @brief a command line tool to set IPTC Preview
|
||||
*
|
||||
* @author Copyright (C) 2009-2012 by Gilles Caulier
|
||||
* <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QMatrix>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdeversion.h"
|
||||
|
||||
#include "qdebug.h"
|
||||
#define PRINT_DEBUG qDebug()
|
||||
#define ENDL
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
PRINT_DEBUG << "setiptcpreview - update/add jpeg iptc preview to image" ENDL;
|
||||
PRINT_DEBUG << "Usage: <image>" ENDL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
QImage preview;
|
||||
QString filePath(argv[1]);
|
||||
KExiv2 meta(filePath);
|
||||
|
||||
QImage image(filePath);
|
||||
QMatrix matrix;
|
||||
matrix.rotate(90);
|
||||
image = image.transformed(matrix);
|
||||
|
||||
QSize previewSize = image.size();
|
||||
previewSize.scale(1280, 1024, Qt::KeepAspectRatio);
|
||||
|
||||
// Ensure that preview is not upscaled
|
||||
if (previewSize.width() >= (int)image.width())
|
||||
preview = image.copy();
|
||||
else
|
||||
preview = image.scaled(previewSize.width(), previewSize.height(), Qt::IgnoreAspectRatio).copy();
|
||||
|
||||
meta.setImagePreview(preview);
|
||||
meta.applyChanges();
|
||||
|
||||
QImage preview2;
|
||||
KExiv2 meta2(filePath);
|
||||
meta2.getImagePreview(preview2);
|
||||
preview2.save("preview.png", "PNG");
|
||||
|
||||
return 0;
|
||||
}
|
188
libkexiv2/tests/setxmpface.cpp
Normal file
188
libkexiv2/tests/setxmpface.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2013-02-21
|
||||
* @brief a command line tool to set faces in Picassa format
|
||||
*
|
||||
* @author Copyright (C) 2013 by Munteanu Veaceslav
|
||||
* <a href="mailto:slavuttici at gmail dot com">slavuttici at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdebug.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
bool setFaceTags(KExiv2& meta,const char* xmpTagName,const QMap<QString,QRectF>& faces,
|
||||
bool setProgramName)
|
||||
{
|
||||
|
||||
Q_UNUSED(setProgramName);
|
||||
meta.setXmpTagString(xmpTagName,QString(),KExiv2::XmpTagType(1),false);
|
||||
|
||||
QString qxmpTagName(xmpTagName);
|
||||
QString nameTagKey = qxmpTagName + QString("[%1]/mwg-rs:Name");
|
||||
QString typeTagKey = qxmpTagName + QString("[%1]/mwg-rs:Type");
|
||||
QString areaTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area");
|
||||
QString areaxTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:x");
|
||||
QString areayTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:y");
|
||||
QString areawTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:w");
|
||||
QString areahTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:h");
|
||||
QString areanormTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:unit");
|
||||
|
||||
QMap<QString,QRectF>::const_iterator it = faces.constBegin();
|
||||
int i =1;
|
||||
while(it != faces.constEnd())
|
||||
{
|
||||
qreal x,y,w,h;
|
||||
it.value().getRect(&x,&y,&w,&h);
|
||||
/** Set tag name **/
|
||||
meta.setXmpTagString(nameTagKey.arg(i).toLatin1(),it.key(),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set tag type as Face **/
|
||||
meta.setXmpTagString(typeTagKey.arg(i).toLatin1(),QString("Face"),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set tag Area, with xmp type struct **/
|
||||
meta.setXmpTagString(areaTagKey.arg(i).toLatin1(),QString(),
|
||||
KExiv2::XmpTagType(2),false);
|
||||
/** Set stArea:x inside Area structure **/
|
||||
meta.setXmpTagString(areaxTagKey.arg(i).toLatin1(),QString::number(x),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set stArea:y inside Area structure **/
|
||||
meta.setXmpTagString(areayTagKey.arg(i).toLatin1(),QString::number(y),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set stArea:w inside Area structure **/
|
||||
meta.setXmpTagString(areawTagKey.arg(i).toLatin1(),QString::number(w),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set stArea:h inside Area structure **/
|
||||
meta.setXmpTagString(areahTagKey.arg(i).toLatin1(),QString::number(h),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
/** Set stArea:unit inside Area structure as normalized **/
|
||||
meta.setXmpTagString(areanormTagKey.arg(i).toLatin1(),QString("normalized"),
|
||||
KExiv2::XmpTagType(0),false);
|
||||
|
||||
++it;
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void removeFaceTags(KExiv2& meta,const char* xmpTagName)
|
||||
{
|
||||
QString qxmpTagName(xmpTagName);
|
||||
QString regionTagKey = qxmpTagName + QString("[%1]");
|
||||
QString nameTagKey = qxmpTagName + QString("[%1]/mwg-rs:Name");
|
||||
QString typeTagKey = qxmpTagName + QString("[%1]/mwg-rs:Type");
|
||||
QString areaTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area");
|
||||
QString areaxTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:x");
|
||||
QString areayTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:y");
|
||||
QString areawTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:w");
|
||||
QString areahTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:h");
|
||||
QString areanormTagKey = qxmpTagName + QString("[%1]/mwg-rs:Area/stArea:unit");
|
||||
|
||||
meta.removeXmpTag(xmpTagName,false);
|
||||
bool dirty= true;
|
||||
int i=1;
|
||||
while(dirty)
|
||||
{
|
||||
dirty = false;
|
||||
dirty |=meta.removeXmpTag(regionTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(nameTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(typeTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areaTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areaxTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areayTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areawTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areahTagKey.arg(i).toLatin1(),false);
|
||||
dirty |=meta.removeXmpTag(areanormTagKey.arg(i).toLatin1(),false);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 3)
|
||||
{
|
||||
kDebug() << "Adding a face rectangle to image";
|
||||
kDebug() << "Usage: <add/remove> <image>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString filePath(argv[2]);
|
||||
|
||||
KExiv2Iface::KExiv2::initializeExiv2();
|
||||
KExiv2 meta;
|
||||
meta.load(filePath);
|
||||
meta.setWriteRawFiles(true);
|
||||
|
||||
/** Add a random rectangle with facetag Bob **/
|
||||
QString name = "Bob Marley";
|
||||
float x =0.5;
|
||||
float y =0.5;
|
||||
float w = 60;
|
||||
float h = 60;
|
||||
|
||||
QRectF rect(x,y,w,h);
|
||||
|
||||
QMap<QString, QRectF> faces;
|
||||
|
||||
faces[name] = rect;
|
||||
|
||||
QString name2 = "Hello Kitty!";
|
||||
QRectF rect2(0.4,0.4,30,30);
|
||||
|
||||
faces[name2] = rect2;
|
||||
|
||||
bool g = meta.supportXmp();
|
||||
|
||||
kDebug() << "Image support XMP" << g;
|
||||
|
||||
const QString bag = "Xmp.mwg-rs.Regions/mwg-rs:RegionList";
|
||||
|
||||
QString op(argv[1]);
|
||||
|
||||
if(op == "add")
|
||||
setFaceTags(meta,bag.toLatin1(),faces,false);
|
||||
else
|
||||
removeFaceTags(meta,bag.toLatin1());
|
||||
|
||||
meta.applyChanges();
|
||||
|
||||
QString recoverName = "Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name";
|
||||
|
||||
KExiv2 meta2;
|
||||
meta2.load(filePath);
|
||||
meta2.setWriteRawFiles(true);
|
||||
|
||||
QString nameR = meta2.getXmpTagString(recoverName.toLatin1(),false);
|
||||
|
||||
kDebug() << "Saved name is:" << nameR;
|
||||
|
||||
KExiv2Iface::KExiv2::cleanupExiv2();
|
||||
return 0;
|
||||
}
|
60
libkexiv2/tests/usexmpsidecar.cpp
Normal file
60
libkexiv2/tests/usexmpsidecar.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
/** ===========================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* <a href="http://www.digikam.org">http://www.digikam.org</a>
|
||||
*
|
||||
* @date 2010-06-27
|
||||
* @brief a command line tool to test XMP sidecar functionality
|
||||
*
|
||||
* @author Copyright (C) 2010 by Jakob Malm
|
||||
* <a href="mailto:jakob dot malm at gmail dot com">jakob dot malm at gmail dot com</a>
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
// KDE includes
|
||||
|
||||
#include "kdebug.h"
|
||||
|
||||
// Local includes
|
||||
|
||||
#include "kexiv2.h"
|
||||
|
||||
using namespace KExiv2Iface;
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
kDebug() << "usexmpsidecar - read from and write to XMP sidecar";
|
||||
kDebug() << "Usage: <image>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString filePath(argv[1]);
|
||||
|
||||
KExiv2 meta;
|
||||
meta.setUseXMPSidecar4Reading(true);
|
||||
meta.load(filePath);
|
||||
// print some metadata
|
||||
// add some metadata
|
||||
// write changed metadata
|
||||
// perhaps check to see if image file or XMP sidecar file was changed
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue