package.xml 0000664 0001750 0001750 00000160501 13160240461 011277 0 ustar jan jan
Horde_Imagepear.horde.orgHorde Image APIAn Image utility API, with backends for:
* GD
* GIF
* PNG
* SVG
* SWF
* ImageMagick convert command line tool
* Imagick ExtensionMichael J Rubinskymrubinskmrubinsk@horde.orgyesChuck Hagenbuchchuckchuck@horde.orgyes2017-09-192.5.22.5.0stablestableLGPL-2.1
* [jan] SECURITY: Fix remote code execution vulnerability in Im driver (Thomas Jarosch <thomas.jarosch@intra2net.com>.
5.3.08.0.0alpha18.0.0alpha11.7.0Horde_Exceptionpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Streampear.horde.org1.6.22.0.0alpha12.0.0alpha1Horde_Supportpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Translationpear.horde.org2.2.03.0.0alpha13.0.0alpha1Horde_Utilpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Testpear.horde.org2.1.03.0.0alpha13.0.0alpha1XML_SVGpear.php.netimagickpecl.php.net2.0.04.0.0alpha14.0.0alpha1imagickgdjsonzlib0.0.10.0.1alphaalpha2004-01-01LGPL-2.1
Initial release as a PEAR package
1.0.0alpha11.0.0alphaalpha2011-03-08LGPL-2.1
* First alpha release for Horde 4.
1.0.0beta11.0.0betabeta2011-03-16LGPL-2.1
* First beta release for Horde 4.
1.0.0RC11.0.0betabeta2011-03-22LGPL-2.1
* First release candidate for Horde 4.
1.0.0RC21.0.0betabeta2011-03-29LGPL-2.1
* Second release candidate for Horde 4.
1.0.01.0.0stablestable2011-04-06LGPL-2.1
* First stable release for Horde 4.
1.0.11.0.0stablestable2011-05-18LGPL-2.1
* [jan] Updated Spanish translation.
1.0.21.0.0stablestable2011-06-01LGPL-2.1
* [jan] Updated Slovak translation.
1.0.31.0.0stablestable2011-07-05LGPL-2.1
* [jan] Update Lithuanian translation.
* [jan] Update Brazilian Portuguese translation.
1.0.41.0.0stablestable2011-07-27LGPL-2.1
* [jan] Update Latvian translation.
1.0.51.0.0stablestable2011-10-15LGPL-2.1
* [mjr] Added support for Lens and LensID EXIF fields)
* [mjr] Ensure string values are parsed as strings (Bug #10646).
* [mjr] Add basic unit tests for EXIF data
1.0.61.0.0stablestable2011-10-19LGPL-2.1
* [mjr] Fix parsing GPS values for locales that do not use the decimal separator (Bug #10643).
1.0.71.0.0stablestable2011-11-03LGPL-2.1
* [mjr] Add a version of the convert command for center cropping with ImageMagick versions less than 6.3.8 (Bug #10687).
1.0.81.0.0stablestable2011-11-22LGPL-2.1
* [jan] Update Croatian translation.
* [jan] Update Latvian translation.
1.0.91.0.0stablestable2011-12-06LGPL-2.1
* [jan] Update Japanese translation (Hiromi Kimura <hiromi@tac.tsukuba.ac.jp>).
1.0.101.0.0stablestable2012-02-22LGPL-2.1
* [jan] Update Spanish translation (Manuel P. Ayala <mayala@unex.es>).
2.0.0alpha11.0.0alphastable2012-07-05LGPL-2.1
* First alpha release for Horde 5.
2.0.0beta11.0.0betastable2012-07-19LGPL-2.1
* First beta release for Horde 5.
2.0.0beta21.0.0betastable2012-10-12LGPL-2.1
* [jan] Update Japanese translation.
2.0.01.0.0stablestable2012-10-30LGPL-2.1
* First stable release for Horde 5.
2.0.11.0.0stablestable2012-11-19LGPL-2.1
* [mms] Use new Horde_Test layout.
2.0.21.0.0stablestable2013-01-09LGPL-2.1
* [jan] Update Basque translation (Ibon Igartua <ibon.igartua@ehu.es>).
2.0.31.0.0stablestable2013-01-29LGPL-2.1
* [mjr] Fix image watermarking for Imagick and ImageMagick drivers.
2.0.41.0.0stablestable2013-03-05LGPL-2.1
* [jan] Improve unit tests.
2.0.51.0.0stablestable2013-07-13LGPL-2.1
* [mjr] Fix generating center image crops on Solaris systems that still use older Bourne shell.
2.0.61.0.0stablestable2014-04-03LGPL-2.1
* [jan] Fix converting to grayscale with imagick driver.
* [jan] Add optional dependency on imagick PECL extension.
2.0.71.0.0stablestable2014-04-09LGPL-2.1
* [mjr] Update maximum allowed version for the PECL Imagick extension.
2.0.81.0.0stablestable2014-05-21LGPL-2.1
* [jan] Update Hungarian translation (Andras Galos <galosa@netinform.hu>).
2.0.91.0.0stablestable2014-06-10LGPL-2.1
* [mjr] Add "Title" and "Description" XMP fields.
2.1.02.0.0stablestable2014-06-17LGPL-2.1
* [mjr] Add Horde_Image_Exif::getDescriptionFields() and Horde_Image_Exif::getTitleFields().
2.2.02.2.0stablestable2015-02-10LGPL-2.1
* [mjr] Improve stream handling in bundled EXIF driver (Request #13788).
* [mms] Horde_Image_Exif_Bundled now supports reading data from a PHP stream, rather than a file.
2.3.02.3.0stablestable2015-04-13LGPL-2.1
* [jan] Fix transparent rectangles in SVG backend.
* [jan] Fix setting background color in SVG backend.
* [jan] Add 'ratio' parameter to Horde_Image_Effect_Imagick_LiquidResize.
* [jan] Fix catching exceptions from imagick extension.
* [jan] Complete the backends' capabilities properties.
* [jan] Fix SVG and SWF backends.
* [jan] Fix setting background color in Horde_Image_Swf constructor.
* [jan] Send Content-Type header in Horde_Image_Svg#display().
* [jan] Improve arc rendering with Im backend.
* [jan] Enable antialiasing in GD backend if available.
* [jan] Implement Horde_Image_Imagick::arc().
* [jan] Fix rounded rectangle drawing with GD backend.
* [jan] Fix transparency issues with GD backend.
* [jan] Fix autoloading of effect classes.
* [jan] Fix border effect with GD driver.
* [jan] Don't error out in effects if a logger hasn't been set.
* [jan] Make getImageAtIndex() always return an image.
* [jan] Don't error our when calling unsupported manipulation methods.
* [jan] Make Horde_Image::arcPoints() work with any angles.
* [jan] Fix some HTML color name to RGB value mappings.
* [jan] Add Horde_Image_Rgb class and remove $horde_image_rgb_colors global.
* [jan] Add Null driver for basics like just displaying the image.
* [mjr] Fix incorrect color renderings in certain situations.
2.3.12.3.0stablestable2015-04-28LGPL-2.1
* [jan] Fix issues with certain locales like Turkish.
2.3.22.3.0stablestable2015-07-31LGPL-2.1
* [jan] Updated UK translation.
2.3.32.3.0stablestable2015-09-07LGPL-2.1
* [cjh] Fix EXIF data not being returned in Bundled driver.
2.3.42.3.0stablestable2016-02-01LGPL-2.1
* [jan] Mark PHP 7 as supported.
2.3.52.3.0stablestable2016-03-21LGPL-2.1
* [mjr] Fix for PHP 7.1.
2.3.62.3.0stablestable2016-07-12LGPL-2.1
* [jan] Fix parsing GPS exif data of a certain format.
2.4.02.4.0stablestable2017-03-20LGPL-2.1
* [jan] Add blur effect.
2.4.12.4.0stablestable2017-04-11LGPL-2.1
* [mjr] Fix returning stream data from NULL image driver (Bug #14608).
2.5.02.5.0stablestable2017-06-21LGPL-2.1
* [mjr] SECURITY: Prevent DOS attack by preventing an infinite loop in certain conditions (CVE-2017-9773, reported by Fariskhi Vidyan <farislab@gmail.com>).
* [mjr] SECURITY: Prevent RCE attacks by properly sanitizing shell arguments (CVE-2017-9774, reported by Fariskhi Vidyan <farislab@gmail.com>).
* [jan] Add blur effect.
2.5.12.5.0stablestable2017-06-25LGPL-2.1
* [mjr] SECURITY: Fix more potential places for command injections.
2.5.22.5.0stablestable2017-09-19LGPL-2.1
* [jan] SECURITY: Fix remote code execution vulnerability in Im driver (Thomas Jarosch <thomas.jarosch@intra2net.com>.
Horde_Image-2.5.2/doc/Horde/Image/COPYING 0000664 0001750 0001750 00000057656 13160240461 015672 0 ustar jan jan GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 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.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
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 Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
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 other code 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.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
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, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) 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.
d) 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.
e) 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 materials to be 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 with
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 Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
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
Horde_Image-2.5.2/doc/Horde/Image/COPYING-EXIF 0000664 0001750 0001750 00000000603 13160240461 016377 0 ustar jan jan This library contains Exifer code which is originally licensed under GPL-2.0
The original author Jake Olefsky of Exif/Parser/* gave explicit approval that
the code can be included into the Horde code base.
Class for dealing with Exif data using a bundled PHP library based on the
Exifer code written by and Copyright 2003 Jake Olefsky
See: http://www.offsky.com/software/exif/index.php
Horde_Image-2.5.2/doc/Horde/Image/UPGRADING 0000664 0001750 0001750 00000001107 13160240461 016056 0 ustar jan jan =======================
Upgrading Horde_Image
=======================
:Contact: dev@lists.horde.org
.. contents:: Contents
.. section-numbering::
This lists the API changes between releases of the package.
Upgrading to 2.3.0
==================
- Horde_Image_Effect_Gd_Border
Replaces and fixes the generic border effect implementation.
- Horde_Image_Effect_Imagick_LiquidResize
A 'ratio' parameter has been added.
- Horde_Image_Rgb
This class holds a map from HTML color names to RGB values and replace the
global $horde_image_rgb_colors variable.
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/Blur.php 0000664 0001750 0001750 00000005527 13160240461 017771 0 ustar jan jan
* @author Jan Schneider
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Blur image effect.
*
* Original version from Martijn Frazer based on
* https://stackoverflow.com/a/20264482
*
* @author Philip Ronan
* @author Martijn Frazer
* @author Jan Schneider
* @category Horde
* @copyright 2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_Blur extends Horde_Image_Effect
{
/**
* Valid parameters:
* - factor: (integer) Blur strength.
*
* @var array
*/
protected $_params = array(
'factor' => 3,
);
/**
* Applies the effect.
*/
public function apply()
{
// Blur factor has to be an integer.
$blurFactor = round($this->_params['factor']);
$img = $this->_image->_im;
$originalWidth = imagesx($img);
$originalHeight = imagesy($img);
$smallestWidth = ceil($originalWidth * pow(0.5, $blurFactor));
$smallestHeight = ceil($originalHeight * pow(0.5, $blurFactor));
// For the first run, the previous image is the original input.
$prevImage = $img;
$prevWidth = $originalWidth;
$prevHeight = $originalHeight;
// Scale way down and gradually scale back up, blurring all the way.
for ($i = 0; $i < $blurFactor; $i++) {
// Determine dimensions of next image.
$nextWidth = $smallestWidth * pow(2, $i);
$nextHeight = $smallestHeight * pow(2, $i);
// Resize previous image to next size.
$nextImage = imagecreatetruecolor($nextWidth, $nextHeight);
imagecopyresized(
$nextImage, $prevImage,
0, 0, 0, 0,
$nextWidth, $nextHeight, $prevWidth, $prevHeight
);
// Apply blur filter.
imagefilter($nextImage, IMG_FILTER_GAUSSIAN_BLUR);
// Now the new image becomes the previous image for the next step.
$prevImage = $nextImage;
$prevWidth = $nextWidth;
$prevHeight = $nextHeight;
}
// Scale back to original size and blur one more time
imagecopyresized(
$img, $nextImage,
0, 0, 0, 0,
$originalWidth, $originalHeight, $nextWidth, $nextHeight
);
imagefilter($img, IMG_FILTER_GAUSSIAN_BLUR);
// Clean up
imagedestroy($prevImage);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/Border.php 0000664 0001750 0001750 00000004712 13160240461 020275 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image border decorator for the Horde_Image package.
*
* @author Jan Schneider
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_Border extends Horde_Image_Effect_Border
{
/**
* Draws the border.
*
* This draws the configured border to the provided image. Beware, that
* every pixel inside the border clipping will be overwritten with the
* background color.
*/
public function apply()
{
$type = $this->_image->getType();
$dimension = $this->_image->getDimensions();
$newWidth = $dimension['width'] + 2;
$newHeight = $dimension['height'] + 2;
$im = $this->_image->create($dimension['width'], $dimension['height']);
$this->_image->call('imagesavealpha', array($im, true));
$this->_image->call('imagealphablending', array($im, false));
$this->_image->call(
'imagecopy',
array(
$im, $this->_image->_im,
0, 0, 0, 0,
$dimension['width'], $dimension['height']
)
);
$this->_image->resize(
$dimension['width'] + 2,
$dimension['height'] + 2,
false
);
$this->_image->call(
'imagefilledrectangle',
array(
$this->_image->_im,
0, 0, $dimension['width'] + 1, $dimension['height'] + 1,
$this->_image->call(
'imagecolorallocatealpha',
array($this->_image->_im, 0, 0, 0, 127)
)
)
);
$this->_image->rectangle(
0,
0,
$dimension['width'] + 1,
$dimension['height'] + 1,
$this->_params['bordercolor']
);
$this->_image->call(
'imagecopy',
array(
$this->_image->_im, $im,
1, 1, 0, 0,
$dimension['width'], $dimension['height']
)
);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/DropShadow.php 0000664 0001750 0001750 00000013134 13160240461 021130 0 ustar jan jan
* @author Michael J. Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for adding a drop shadow.
*
* This algorithm is from the phpThumb project available at
* http://phpthumb.sourceforge.net and all credit for this script should go to
* James Heinrich . Modifications made to the code to
* fit it within the Horde framework and to adjust for our coding standards.
*
* @author James Heinrich
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_DropShadow extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* @TODO
*
* @var array
*/
protected $_params = array(
'distance' => 5,
'width' => 2,
'hexcolor' => '000000',
'angle' => 215,
'fade' => 10
);
/**
* Applies the effect.
*/
public function apply()
{
$distance = $this->_params['distance'];
$width = $this->_params['width'];
$hexcolor = $this->_params['hexcolor'];
$angle = $this->_params['angle'];
$fade = $this->_params['fade'];
$width_shadow = cos(deg2rad($angle)) * ($distance + $width);
$height_shadow = sin(deg2rad($angle)) * ($distance + $width);
$gdimg = $this->_image->_im;
$imgX = $this->_image->call('imageSX', array($gdimg));
$imgY = $this->_image->call('imageSY', array($gdimg));
$offset['x'] = cos(deg2rad($angle)) * ($distance + $width - 1);
$offset['y'] = sin(deg2rad($angle)) * ($distance + $width - 1);
$tempImageWidth = $imgX + abs($offset['x']);
$tempImageHeight = $imgY + abs($offset['y']);
$temp = $this->_image->create($tempImageWidth, $tempImageHeight);
$this->_image->call('imageAlphaBlending', array($temp, false));
$this->_image->call('imageSaveAlpha', array($temp, true));
$transparent1 = $this->_image->call(
'imageColorAllocateAlpha',
array($temp, 0, 0, 0, 127)
);
$this->_image->call('imageFill', array($temp, 0, 0, $transparent1));
for ($x = 0; $x < $imgX; $x++) {
for ($y = 0; $y < $imgY; $y++) {
$colorat = $this->_image->call(
'imageColorAt',
array($gdimg, $x, $y)
);
$PixelMap[$x][$y] = $this->_image->call(
'imageColorsForIndex',
array($gdimg, $colorat)
);
}
}
/* Creates the shadow */
$r = hexdec(substr($hexcolor, 0, 2));
$g = hexdec(substr($hexcolor, 2, 2));
$b = hexdec(substr($hexcolor, 4, 2));
/* Essentially masks the original image and creates the shadow */
for ($x = 0; $x < $tempImageWidth; $x++) {
for ($y = 0; $y < $tempImageHeight; $y++) {
if ((!isset($PixelMap[$x][$y]['alpha']) ||
$PixelMap[$x][$y]['alpha'] > 0) &&
isset($PixelMap[$x + $offset['x']][$y + $offset['y']]['alpha']) &&
$PixelMap[$x + $offset['x']][$y + $offset['y']]['alpha'] < 127) {
$thisColor = $this->_image->call(
'imageColorAllocateAlpha',
array(
$temp,
$r, $g, $b,
$PixelMap[$x + $offset['x']][$y + $offset['y']]['alpha']
)
);
$this->_image->call(
'imageSetPixel',
array($temp, $x, $y, $thisColor)
);
}
}
}
/* Overlays the original image */
$this->_image->call('imageAlphaBlending', array($temp, true));
for ($x = 0; $x < $imgX; $x++) {
for ($y = 0; $y < $imgY; $y++) {
if ($PixelMap[$x][$y]['alpha'] < 127) {
$thisColor = $this->_image->call(
'imageColorAllocateAlpha',
array(
$temp,
$PixelMap[$x][$y]['red'],
$PixelMap[$x][$y]['green'],
$PixelMap[$x][$y]['blue'],
$PixelMap[$x][$y]['alpha']
)
);
$this->_image->call(
'imageSetPixel',
array($temp, $x, $y, $thisColor)
);
}
}
}
$this->_image->call('imageSaveAlpha', array($gdimg, true));
$this->_image->call('imageAlphaBlending', array($gdimg, false));
// Merge the shadow and the original into the original.
$this->_image->call(
'imageCopyResampled',
array(
$gdimg, $temp,
0, 0, 0, 0,
$imgX, $imgY,
$this->_image->call('imageSX', array($temp)),
$this->_image->call('imageSY', array($temp))
)
);
$this->_image->call('imageDestroy', array($temp));
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/RoundCorners.php 0000664 0001750 0001750 00000007750 13160240461 021510 0 ustar jan jan
* @author Michael J. Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for round image corners.
*
* This algorithm is from the phpThumb project available at
* http://phpthumb.sourceforge.net and all credit for this script should go to
* James Heinrich . Modifications made to the code to
* fit it within the Horde framework and to adjust for our coding standards.
*
* @author James Heinrich
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (integer) Radius of rounded corners.
*
* @var array
*/
protected $_params = array('radius' => 10);
/**
* Applies the effect.
*/
public function apply()
{
// Original comments from phpThumb project:
// generate mask at twice desired resolution and downsample afterwards
// for easy antialiasing mask is generated as a white double-size
// elipse on a triple-size black background and copy-paste-resampled
// onto a correct-size mask image as 4 corners due to errors when the
// entire mask is resampled at once (gray edges)
$radius_x = $radius_y = $this->_params['radius'];
$gdimg = $this->_image->_im;
$imgX = round($this->_image->call('imageSX', array($gdimg)));
$imgY = round($this->_image->call('imageSY', array($gdimg)));
$maskTriple = $this->_image->create(
round($radius_x * 6),
round($radius_y * 6)
);
$mask = $this->_image->create($imgX, $imgY);
$color_transparent = $this->_image->call(
'imageColorAllocate',
array($maskTriple, 255, 255, 255)
);
$this->_image->call(
'imageFilledEllipse',
array(
$maskTriple,
$radius_x * 3, $radius_y * 3,
$radius_x * 4, $radius_y * 4,
$color_transparent
)
);
$this->_image->call(
'imageFilledRectangle',
array($mask, 0, 0, $imgX, $imgY, $color_transparent));
$this->_image->call(
'imageCopyResampled',
array(
$mask, $maskTriple,
0, 0, $radius_x, $radius_y,
$radius_x, $radius_y, $radius_x * 2, $radius_y * 2
)
);
$this->_image->call(
'imageCopyResampled',
array(
$mask, $maskTriple,
0, $imgY - $radius_y,
$radius_x, $radius_y * 3,
$radius_x, $radius_y,
$radius_x * 2, $radius_y * 2
)
);
$this->_image->call(
'imageCopyResampled',
array(
$mask, $maskTriple,
$imgX - $radius_x, $imgY - $radius_y,
$radius_x * 3, $radius_y * 3,
$radius_x, $radius_y,
$radius_x * 2, $radius_y * 2
)
);
$this->_image->call(
'imageCopyResampled',
array(
$mask, $maskTriple,
$imgX - $radius_x, 0,
$radius_x * 3, $radius_y,
$radius_x, $radius_y,
$radius_x * 2, $radius_y * 2
)
);
$this->_image->applyMask($mask);
$this->_image->call('imageDestroy', array($mask));
$this->_image->call('imageDestroy', array($maskTriple));
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/TextWatermark.php 0000664 0001750 0001750 00000013206 13160240461 021660 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for watermarking images with text.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
* - text: [REQUIRED] (string) The text of the watermark.
* - halign: (string) The horizontal placement
* - valign: (string) The vertical placement
* - font: (string) The font name or family to use
* - fontsize: (string) The size of the font to use (small, medium,
* large, giant)
*
* @var array
*/
protected $_params = array(
'halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small'
);
/**
* Applies the effect.
*/
public function apply()
{
$color = $this->_image->call(
'imageColorClosest',
array($this->_image->_im, 255, 255, 255)
);
$shadow = $this->_image->call(
'imageColorClosest',
array($this->_image->_im, 0, 0, 0)
);
// Shadow offset in pixels.
$drop = 1;
// Maximum text width.
$maxwidth = 200;
// Amount of space to leave between the text and the image border.
$padding = 10;
$f = $this->_image->getFont($this->_params['fontsize']);
$fontwidth = $this->_image->call('imageFontWidth', array($f));
$fontheight = $this->_image->call('imageFontHeight', array($f));
// So that shadow is not off the image with right align and bottom
// valign.
$margin = floor($padding + $drop) / 2;
if ($maxwidth) {
$maxcharsperline = floor(($maxwidth - ($margin * 2)) / $fontwidth);
$text = wordwrap($this->_params['text'], $maxcharsperline, "\n", 1);
}
// Split $text into individual lines.
$lines = explode("\n", $text);
switch ($this->_params['valign']) {
case 'center':
$y = ($this->_image->call('imageSY', array($this->_image->_im))
- ($fontheight * count($lines)))
/ 2;
break;
case 'bottom':
$y = $this->_image->call('imageSY', array($this->_image->_im))
- (($fontheight * count($lines)) + $margin);
break;
default:
$y = $margin;
break;
}
switch ($this->_params['halign']) {
case 'right':
foreach ($lines as $line) {
$this->_image->call(
'imageString',
array(
$this->_image->_im,
$f,
$this->_image->call('imageSX', array($this->_image->_im))
- $fontwidth * strlen($line) - $margin + $drop,
$y + $drop,
$line,
$shadow
)
);
$this->_image->call(
'imageString',
array(
$this->_image->_im,
$f,
$this->_image->call('imageSX', array($this->_image->_im))
- $fontwidth * strlen($line) - $margin,
$y,
$line,
$color
)
);
$y += $fontheight;
}
break;
case 'center':
foreach ($lines as $line) {
$this->_image->call(
'imageString',
array(
$this->_image->_im,
$f,
floor(($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) / 2)
+ $drop,
$y + $drop,
$line,
$shadow
)
);
$this->_image->call(
'imageString',
array(
$this->_image->_im,
$f,
floor(($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) / 2),
$y,
$line,
$color
)
);
$y += $fontheight;
}
break;
default:
foreach ($lines as $line) {
$this->_image->call(
'imageString',
array(
$this->_image->_im,
$f,
$margin + $drop,
$y + $drop,
$line,
$shadow
)
);
$this->_image->call(
'imageString',
array($this->_image->_im, $f, $margin, $y, $line, $color)
);
$y += $fontheight;
}
break;
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Gd/Unsharpmask.php 0000664 0001750 0001750 00000016501 13160240461 021353 0 ustar jan jan
* Copyright 2007-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Torstein Hønsi
* @author Michael J. Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Unsharp mask Image effect.
*
* Unsharp mask algorithm by Torstein Hønsi 2003
* from: http://www.vikjavev.com/hovudsida/umtestside.php
*
* @author Torstein Hønsi
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2003 Torstein Hønsi
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Gd_Unsharpmask extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (float) Thickness of the sharpened edge. Should be greater
* than sigma (or 0, and imagick will attempt to auto choose).
* In general, radius should be roughly output dpi / 150. So
* for display purposes a radius of 0.5 is suggested.
* - amount: (float) Amount of the difference between original and the
* blur image that gets added back to the original. Can be
* thought of as the "strength" of the effect. Too high may
* cause blocking of shadows and highlights. Given a decimal
* value indicating percentage, e.g. 1.2 = 120%
* - threshold: (float) Determines how large the brightness delta between
* adjacent pixels needs to be to sharpen the edge. Larger
* values == less sharpening. Useful for preventing noisy
* images from being oversharpened.
*
* @var array
*/
protected $_params = array(
'amount' => 0,
'radius' => 0,
'threshold' => 0
);
/**
* Applies the effect.
*/
public function apply()
{
$amount = $this->_params['amount'];
$radius = $this->_params['radius'];
$threshold = $this->_params['threshold'];
// Attempt to calibrate the parameters to Photoshop:
$amount = min($amount, 500);
$amount = $amount * 0.016;
if ($amount == 0) {
return true;
}
$radius = min($radius, 50);
$radius = $radius * 2;
$threshold = min($threshold, 255);
$radius = abs(round($radius)); // Only integers make sense.
if ($radius == 0) {
return true;
}
$img = $this->_image->_im;
$w = ImageSX($img);
$h = ImageSY($img);
$imgCanvas = ImageCreateTrueColor($w, $h);
$imgCanvas2 = ImageCreateTrueColor($w, $h);
$imgBlur = ImageCreateTrueColor($w, $h);
$imgBlur2 = ImageCreateTrueColor($w, $h);
ImageCopy($imgCanvas, $img, 0, 0, 0, 0, $w, $h);
ImageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
// Gaussian blur matrix:
//
// 1 2 1
// 2 4 2
// 1 2 1
//
//////////////////////////////////////////////////
// Move copies of the image around one pixel at the time and merge them
// with weight according to the matrix. The same matrix is simply
// repeated for higher radii.
for ($i = 0; $i < $radius; $i++) {
// up left
ImageCopy ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1);
// down right
ImageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50);
// down left
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333);
// up right
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25);
// left
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333);
// right
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25);
// up
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20 );
// down
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667);
// center
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50);
ImageCopy ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
// During the loop above the blurred copy darkens, possibly due to
// a roundoff error. Therefore the sharp picture has to go through
// the same loop to produce a similar image for comparison. This is
// not a good thing, as processing time increases heavily.
ImageCopy ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopy ($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
}
// Calculate the difference between the blurred pixels and the original
// and set the pixels
for ($x = 0; $x < $w; $x++) {
for ($y = 0; $y < $h; $y++) {
$rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgCanvas, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
$bBlur = ($rgbBlur & 0xFF);
// When the masked pixels differ less from the original than
// the threshold specifies, they are set to their original
// value.
$rNew = (abs($rOrig - $rBlur) >= $threshold)
? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig))
: $rOrig;
$gNew = (abs($gOrig - $gBlur) >= $threshold)
? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig))
: $gOrig;
$bNew = (abs($bOrig - $bBlur) >= $threshold)
? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig))
: $bOrig;
if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
ImageSetPixel($img, $x, $y, $pixCol);
}
}
}
ImageDestroy($imgCanvas);
ImageDestroy($imgCanvas2);
ImageDestroy($imgBlur);
ImageDestroy($imgBlur2);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/Blur.php 0000664 0001750 0001750 00000002040 13160240461 017767 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Blur image effect.
*
* @author Jan Schneider
* @category Horde
* @copyright 2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_Blur extends Horde_Image_Effect
{
/**
* Valid parameters:
* - factor: (integer) Blur strength.
*
* @var array
*/
protected $_params = array(
'factor' => 3,
);
/**
* Applies the effect.
*/
public function apply()
{
$this->_image->addPostSrcOperation(
'-blur 0x'
. (0.75 * $this->_params['factor'] ** 2 - 0.25 * $this->_params['factor'] + 1)
);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/Border.php 0000775 0001750 0001750 00000002330 13160240461 020305 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image border decorator for the Horde_Image package.
*
* @author Chuck Hagenbuch
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_Border extends Horde_Image_Effect_Border
{
/**
* Draws the border.
*
* This draws the configured border to the provided image. Beware, that
* every pixel inside the border clipping will be overwritten with the
* background color.
*/
public function apply()
{
$this->_image->addPostSrcOperation(sprintf(
'-bordercolor "%s" %s -border %d',
escapeshellarg($this->_params['bordercolor']),
(!empty($this->_params['preserve']) ? '-compose Copy' : ''),
$this->_params['borderwidth']
));
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/CenterCrop.php 0000664 0001750 0001750 00000003464 13160240461 021142 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect easily creating small, center-cropped thumbnails.
*
* Requires IM version 6.3.8-3 or greater.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_CenterCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
* - width: (integer) Crop width.
* - height: (integer) Crop height.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.8') < 0) {
$initialCrop = $this->_params->width * 2;
$command = sprintf(
'-resize x%d -resize \'%dx<\' -resize 50% -gravity center -crop %dx%d+0+0 +repage',
$initialCrop, $initialCrop, $this->_params->width, $this->params->height
);
} else {
$command = sprintf(
'-thumbnail %dx%d\^ -gravity center -extent %dx%d',
$this->_params->width, $this->_params->height, $this->_params->width, $this->_params->height
);
}
$this->_image->addPostSrcOperation($command);
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/Composite.php 0000775 0001750 0001750 00000004616 13160240461 021043 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Simple composite effect for composing multiple images. This effect assumes
* that all images being passed in are already the desired size.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_Composite extends Horde_Image_Effect
{
/**
* Valid parameters:
* - images: (array) An array of Horde_Image objects to overlay.
* - gravity: (string) The gravity describing the placement. One of None,
* Center, East, Forget, NorthEast, North, NorthWest,
* SouthEast, South, SouthWest, West
* - x and y: (integer) Coordinates for the overlay placement.
*
* EITHER gravity OR coordinates may be set. If both are provided, the
* behaviour is undefined.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
$ops = $geometry = $gravity = '';
if (isset($this->_params['gravity'])) {
$gravity = ' -gravity ' . escapeshellarg($this->_params['gravity']);
}
if (isset($this->_params['x']) && isset($this->_params['y'])) {
$geometry = ' -geometry +' . (integer)$this->_params['x']
. '+' . (integer)$this->_params['y'] . ' ';
}
if (isset($this->_params['compose'])) {
// The -matte ensures that the destination (background) image has
// an alpha channel - to avoid black holes in the image.
$compose = ' -compose ' . escapeshellarg($this->_params['compose']) . ' -matte';
}
foreach ($this->_params['images'] as $image) {
$temp = $image->toFile();
$this->_image->addFileToClean($temp);
$ops .= ' ' . $temp . $gravity . $compose . ' -composite';
}
$this->_image->addOperation($geometry);
$this->_image->addPostSrcOperation($ops);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/DropShadow.php 0000664 0001750 0001750 00000003372 13160240461 021146 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for adding a drop shadow.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_DropShadow extends Horde_Image_Effect
{
/**
* Valid parameters: Most are currently ignored for the im version
* of this effect.
*
* @TODO
*
* @var array
*/
protected $_params = array(
'distance' => 5, // This is used as the x and y offset
'width' => 2,
'hexcolor' => '000000',
'angle' => 215,
'fade' => 3, // Sigma value
'padding' => 0,
'background' => 'none'
);
/**
* Applies the effect.
*/
public function apply()
{
$size = $this->_image->getDimensions();
$this->_image->addPostSrcOperation(
'\( +clone -background black -shadow 80x' . (integer)$this->_params['fade']
. '+' . (integer)$this->_params['distance']
. '+' . (integer)$this->_params['distance']
. ' \) +swap -background ' . escapeshellarg($this->_params['background']) . ' -flatten +repage -bordercolor '
. escapeshellarg($this->_params['background'])
. ' -border ' . (integer)$this->_params['padding']
);
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/LiquidResize.php 0000664 0001750 0001750 00000003310 13160240461 021475 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for applying content aware image resizing.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_LiquidResize extends Horde_Image_Effect
{
/**
* Valid parameters:
* - width: (integer) The target width.
* - height: (integer) The target height.
* - ratio: (boolean) Keep aspect ratio.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
$resWidth = $this->_params->width * 2;
$resHeight = $this->_params->height * 2;
$this->_image->addOperation("-size {$resWidth}x{$resHeight}");
if ($this->_params->get('ratio', true)) {
$this->_image->addPostSrcOperation(sprintf(
'-liquid-rescale %dx%d',
$this->_params->width, $this->_params->height
));
} else {
$this->_image->addPostSrcOperation(sprintf(
'-liquid-rescale %dx%d!',
$this->_params->width, $this->_params->height
));
}
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/PhotoStack.php 0000664 0001750 0001750 00000020737 13160240461 021157 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Effect for composing multiple images into a single image.
*
* The technique for the Polaroid-like stack using the Imagick extension is
* credited to Mikko Koppanen and is documented at http://valokuva.org
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_PhotoStack extends Horde_Image_Effect
{
/**
* Valid parameters for the stack effect:
* - images: (array) An array of Horde_Image objects to stack. Images
* are stacked in a FIFO manner, so that the top-most image is
* the last one in this array.
* - type: (string) Determines the style for the composition.
* 'plain' or 'polaroid' are supported.
* - resize_height: (integer) The height that each individual thumbnail
* should be resized to before composing on the image.
* - padding: (integer) How much padding should we ensure is left around
* the active image area?
* - background: (string) The background canvas color - this is used as
* the color to set any padding to.
* - bordercolor: (string) If using type 'plain' this sets the color of
* the border that each individual thumbnail gets.
* - borderwidth: (integer) If using type 'plain' this sets the width of
* the border on each individual thumbnail.
* - offset: (integer) If using type 'plain' this determines the amount
* of x and y offset to give each successive image when it is
* placed on the top of the stack.
*
* @var array
*/
protected $_params = array(
'type' => 'plain',
'resize_height' => '150',
'padding' => 0,
'background' => 'none',
'bordercolor' => '#333',
'borderwidth' => 1,
'borderrounding' => 10,
'offset' => 5
);
/**
* Applies the effect.
*/
public function apply()
{
$i = 1;
$cnt = count($this->_params['images']);
if ($cnt <= 0) {
throw new Horde_Image_Exception('No Images provided.');
}
// Start out fresh.
$this->_image->raw();
switch ($this->_params['type']) {
case 'plain':
case 'rounded':
// Get top image dimensions, then force each bottom image to the
// same dimensions.
$this->_params['images'][$cnt - 1]->resize(
$this->_params['resize_height'],
$this->_params['resize_height'],
true
);
$size = $this->_params['images'][$cnt - 1]->getDimensions();
$xo = $yo = count($this->_params['images'])
* $this->_params['offset'];
$ops = '';
$haveBottom = false;
foreach ($this->_params['images'] as $image) {
$image->resize($size['height'], $size['width'], false);
$xo -= $this->_params['offset'];
$yo -= $this->_params['offset'];
if ($this->_params['type'] == 'rounded') {
$temp = $this->_roundBorder($image);
} else {
$temp = $image->toFile();
}
$this->_image->addFileToClean($temp);
$ops .= ' \( ' . $temp . ' -background none -thumbnail '
. $size['width'] . 'x' . $size['height']
. '! -repage +' . $xo . '+' . $yo
. ($this->_params['type'] == 'plain' ? ' -bordercolor "#333" -border 1 ' : ' ' )
. ((!$haveBottom) ? '\( +clone -shadow 80x3+4+4 \) +swap -mosaic' : '')
. ' \) ';
$haveBottom = true;
}
// The first -background none option below is only honored in
// convert versions before 6.4 it seems. Without it specified as
// none here, all stacks come out with a white background.
$this->_image->addPostSrcOperation(
$ops . ' -background ' . escapeshellarg($this->_params['background'])
. ' -mosaic -bordercolor ' . escapeshellarg($this->_params['background'])
. ' -border ' . (integer)$this->_params['padding']);
break;
case 'polaroid':
// Check for im version > 6.3.2
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.2') >= 0) {
$ops = '';
foreach ($this->_params['images'] as $image) {
$temp = $image->toFile();
// Remember the temp files so we can nuke them later.
$this->_image->addFileToClean($temp);
// Don't rotate the top image.
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$ops .= ' \( ' . $temp
. ' -geometry +'
. mt_rand(1, $this->_params['resize_height'])
. '+' . mt_rand(1, $this->_params['resize_height'])
. ' -thumbnail \'' . (integer)$this->_params['resize_height']
. 'x' . (integer)$this->_params['resize_height']
. '>\' -bordercolor Snow -border 1 -polaroid '
. $angle . ' \) ';
}
$this->_image->addPostSrcOperation(
'-background ' . escapeshellarg($this->_params['background']) . ' ' . $ops
. '-mosaic -bordercolor ' . escapeshellarg($this->_params['background'])
. ' -border ' . (integer)$this->_params['padding']);
} else {
// An attempt at a -polaroid command free version of this
// effect based on various examples and ideas at
// http://imagemagick.org
$ops = '';
foreach ($this->_params['images'] as $image) {
$temp = $image->toFile();
$this->_image->addFileToClean($temp);
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$ops .= '\( ' . $temp . ' -thumbnail \''
. (integer)$this->_params['resize_height']
. 'x' . (integer)$this->_params['resize_height']
. '>\' -bordercolor "#eee" -border 4 -bordercolor grey90 -border 1 -bordercolor none -background none -rotate '
. $angle . ' -background none \( +clone -shadow 60x4+4+4 \) +swap -background none -flatten \) ';
}
$this->_image->addPostSrcOperation(
'-background none ' . $ops
. '-mosaic -trim +repage -bordercolor '
. escapeshellarg($this->_params['background'])
. ' -border ' . (integer)$this->_params['padding']);
}
break;
}
}
private function _roundBorder($image)
{
$context = array(
'tmpdir' => $this->_image->getTmpDir(),
'convert' => $this->_image->getConvertPath()
);
$size = $image->getDimensions();
$new = new Horde_Image_Im(array('data' => $image->raw()), $context);
$new->addEffect(
'RoundCorners',
array(
'border' => 2,
'bordercolor' => '#111',
'background' => 'none'
)
);
$new->applyEffects();
return $new->toFile();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/PolaroidImage.php 0000775 0001750 0001750 00000004430 13160240461 021607 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Effect for creating a polaroid looking image.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_PolaroidImage extends Horde_Image_Effect
{
/**
* Valid parameters for the polaroid effect:
* - background: (string) The color of the image background.
* - angle: (integer) Angle to rotate the image.
* - shadowcolor: (string) The color of the image shadow.
*
* @var array
*/
protected $_params = array(
'background' => 'none',
'angle' => 0,
'shadowcolor' => 'black'
);
/**
* Applies the effect.
*/
public function apply()
{
// Check for im version > 6.3.2
$this->_image->_imagick = null;
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.2') >= 0) {
$this->_image->addPostSrcOperation(sprintf(
'-bordercolor "#eee" -background none -polaroid %d \( +clone -fill %s -draw \'color 0,0 reset\' \) +swap +flatten',
$this->_params['angle'],
escapeshellarg($this->_params['background'])
));
} else {
$size = $this->_image->getDimensions();
$this->_image->addPostSrcOperation(sprintf(
'-bordercolor \"#eee\" -border 8 -bordercolor grey90 -border 1 -bordercolor none -background none -rotate %d \( +clone -shadow 60x1.5+1+1 -rotate 90 -wave 1x%s -rotate 90 \) +swap -rotate 90 -wave 1x%s -rotate -90 -flatten \( +clone -fill %s -draw \'color 0,0 reset \' \) +swap -flatten',
$this->_params['angle'],
$size['height'] * 2,
$size['height'] * 2,
escapeshellarg($this->_params['background'])
));
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/RoundCorners.php 0000664 0001750 0001750 00000003532 13160240461 021515 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for round image corners.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (integer) Radius of rounded corners.
*
* @var array
*/
protected $_params = array(
'radius' => 10,
'background' => 'none',
'border' => 0,
'bordercolor' => 'none'
);
/**
* Applies the effect.
*/
public function apply()
{
// Get image dimensions
$dimensions = $this->_image->getDimensions();
$height = $dimensions['height'];
$width = $dimensions['width'];
$round = (integer)$this->_params['radius'];
$background = escapeshellarg($this->_params['background']);
$this->_image->addOperation(
"-size {$width}x{$height} xc:$background "
. "-fill $background -draw \"matte 0,0 reset\" -tile"
);
$this->_image->roundedRectangle(
round($round / 2),
round($round / 2),
$width - round($round / 2) - 2,
$height - round($round / 2) - 2,
$round + 2,
'none',
'white'
);
// Reset width/height since these might have changed
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/TextWatermark.php 0000664 0001750 0001750 00000005204 13160240461 021672 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for watermarking images with text.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
* - text: [REQUIRED] (string) The text of the watermark.
* - halign: (string) The horizontal placement
* - valign: (string) The vertical placement
* - font: (string) The font name or family to use
* - fontsize: (string) The size of the font to use (small, medium,
* large, giant)
*
* @var array
*/
protected $_params = array(
'halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small'
);
/**
* Applies the effect.
*/
public function apply()
{
/* Determine placement on image */
switch ($this->_params['valign']) {
case 'bottom':
$v = 'south';
break;
case 'center':
$v = 'center';
break;
default:
$v = 'north';
}
switch ($this->_params['halign']) {
case 'right':
$h = 'east';
break;
case 'center':
$h = 'center';
break;
default:
$h = 'west';
}
if (($v == 'center' && $h != 'center') ||
($v == 'center' && $h == 'center')) {
$gravity = $h;
} elseif ($h == 'center' && $v != 'center') {
$gravity = $v;
} else {
$gravity = $v . $h;
}
/* Determine font point size */
$point = Horde_Image::getFontSize($this->_params['fontsize']);
$this->_image->raw();
$this->_image->addPostSrcOperation(
' -font ' . $this->_params['font'] . ' -pointsize ' . $point
. ' \( +clone -resize 1x1 -fx 1-intensity -threshold 50% -scale 32x32 -write mpr:color +delete \) -tile mpr:color -gravity '
. $gravity . ' -annotate +20+10 "' . escapeshellarg($this->_params['text']) . '"'
);
$this->_image->raw();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Im/Unsharpmask.php 0000664 0001750 0001750 00000004726 13160240461 021374 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Unsharp mask Image effect.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Im_Unsharpmask extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (float) Thickness of the sharpened edge. Should be greater
* than sigma (or 0, and imagick will attempt to auto choose).
* In general, radius should be roughly output dpi / 150. So
* for display purposes a radius of 0.5 is suggested.
* - amount: (float) Amount of the difference between original and the
* blur image that gets added back to the original. Can be
* thought of as the "strength" of the effect. Too high may
* cause blocking of shadows and highlights. Given a decimal
* value indicating percentage, e.g. 1.2 = 120%
* - threshold: (float) Determines how large the brightness delta between
* adjacent pixels needs to be to sharpen the edge. Larger
* values == less sharpening. Useful for preventing noisy
* images from being oversharpened.
*
* @var array
*/
protected $_params = array(
'radius' => 0.5,
'amount' => 1,
'threshold' => 0.05
);
/**
* Applies the effect.
*/
public function apply()
{
/* Calculate appropriate sigma:
* Determines how the sharpening is graduated away from the center
* pixel of the sharpened edge. In general, if radius < 1, then sigma =
* radius else sigma = sqrt(radius) */
$this->_params['sigma'] = ($this->_params['radius'] < 1)
? $this->_params['radius']
: sqrt($this->_params['radius']);
$this->_image->addPostSrcOperation(sprintf(
'-unsharp %Fx%F+%F+%F',
$this->_params['radius'], $this->_params['sigma'], $this->_params['amount'], $this->_params['threshold']
));
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/Blur.php 0000664 0001750 0001750 00000002232 13160240461 020771 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Blur image effect.
*
* @author Jan Schneider
* @category Horde
* @copyright 2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_Blur extends Horde_Image_Effect
{
/**
* Valid parameters:
* - factor: (integer) Blur strength.
*
* @var array
*/
protected $_params = array(
'factor' => 3,
);
/**
* Applies the effect.
*/
public function apply()
{
try {
$this->_image->imagick->blurImage(
0,
0.75 * $this->_params['factor'] ** 2 - 0.25 * $this->_params['factor'] + 1
);
} catch (Imagick_Exception $e) {
throw new Horde_Image_Exception($e);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/Border.php 0000775 0001750 0001750 00000003316 13160240461 021311 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image border decorator for the Horde_Image package.
*
* @author Chuck Hagenbuch
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_Border extends Horde_Image_Effect_Border
{
/**
* Draws the border.
*
* This draws the configured border to the provided image. Beware, that
* every pixel inside the border clipping will be overwritten with the
* background color.
*/
public function apply()
{
if ($this->_params['preserve']) {
Horde_Image_Imagick::frameImage(
$this->_image->imagick,
$this->_params['bordercolor'],
$this->_params['borderwidth'],
$this->_params['borderwidth']
);
} else {
try {
$this->_image->imagick->borderImage(
new ImagickPixel($this->_params['bordercolor']),
$this->_params['borderwidth'],
$this->_params['borderwidth']
);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/CenterCrop.php 0000664 0001750 0001750 00000002473 13160240461 022140 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect easily creating small, center-cropped thumbnails.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_CenterCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
* - width: (integer) Crop width.
* - height: (integer Crop height.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
try {
$this->_image->imagick->cropThumbnailImage(
$this->_params->width, $this->_params->height
);
$this->_image->clearGeometry();
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/Composite.php 0000775 0001750 0001750 00000003764 13160240461 022045 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Simple composite effect for composing multiple images. This effect assumes
* that all images being passed in are already the desired size.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_Composite extends Horde_Image_Effect
{
/**
* Valid parameters:
* - images: (array) An array of Horde_Image objects to overlay.
* - x and y: (integer) Coordinates for the overlay placement.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
try {
foreach ($this->_params['images'] as $image) {
$topimg = new Imagick();
$topimg->clear();
$topimg->readImageBlob($image->raw());
/* Calculate center for composite (gravity center) */
$geometry = $this->_image->imagick->getImageGeometry();
$x = $geometry['width'] / 2;
$y = $geometry['height'] / 2;
if (isset($this->_params['x']) && isset($this->_params['y'])) {
$x = $this->_params['x'];
$y = $this->_params['y'];
}
$this->_image->_imagick->compositeImage(
$topimg,
Imagick::COMPOSITE_OVER,
$x, $y
);
}
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/DropShadow.php 0000664 0001750 0001750 00000006454 13160240461 022151 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for adding a drop shadow.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_DropShadow extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* @TODO
*
* @var array
*/
protected $_params = array(
'distance' => 5, // This is used as the x and y offset
'width' => 2, // ignored
'hexcolor' => '000000', // ignored
'angle' => 215, // ignored
'fade' => 3, // Sigma value
'padding' => 0,
'background' => 'none'
);
/**
* Applies the effect.
*/
public function apply()
{
// There is what *I* call a bug in the magickwand interface of Im that
// Imagick is compiled against. The X and Y parameters are ignored, and
// the distance of the shadow is determined *solely* by the sigma value
// which makes it pretty much impossible to have Imagick shadows look
// identical to Im shadows...
try {
$shadow = $this->_image->cloneImagickObject();
$shadow->setImageBackgroundColor(new ImagickPixel('black'));
$shadow->shadowImage(
80,
$this->_params['fade'],
$this->_params['distance'],
$this->_params['distance']
);
// If we explicitly request a background color, we need to compose
// an image of the background color with the shadow since the
// shadow is always generated with transparent background.
if ($this->_params['background'] != 'none') {
$size = $shadow->getImageGeometry();
$new = new Imagick();
$new->newImage($size['width'], $size['height'], new ImagickPixel($this->_params['background']));
$new->setImageFormat($this->_image->getType());
$new->compositeImage($shadow, Imagick::COMPOSITE_OVER, 0, 0);
$shadow->clear();
$shadow->addImage($new);
$new->destroy();
}
$shadow->compositeImage(
$this->_image->imagick, Imagick::COMPOSITE_OVER, 0, 0
);
if ($this->_params['padding']) {
$shadow->borderImage(
$this->_params['background'],
$this->_params['padding'],
$this->_params['padding']
);
}
$this->_image->imagick->clear();
$this->_image->imagick->addImage($shadow);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$shadow->destroy();
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/LiquidResize.php 0000664 0001750 0001750 00000004234 13160240461 022502 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for applying content aware image resizing.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_LiquidResize extends Horde_Image_Effect
{
/**
* Valid parameters:
* - width: (integer) The target width.
* - height: (integer) The target height.
* - ratio: (boolean) Keep aspect ratio.
* - delta_x: (integer) How much the seam may move on x axis (A value of
* 0 causes the seam to be straight).
* - rigidity: (integer) Introduces a bias for non-straight seams.
* Typically zero
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
// Only supported if ImageMagick is compiled against lqr library.
if (!method_exists($this->_image->imagick, 'liquidRescaleImage')) {
throw new Horde_Image_Exception(
'Missing support for lqr in ImageMagick.'
);
}
$this->_params = new Horde_Support_Array($this->_params);
if ($this->_params->get('ratio', true)) {
$dim = $this->_image->getDimensions();
$this->_params->width = round(
$this->_params->height * $dim['width'] / $dim['height']
);
}
try {
$this->_image->imagick->liquidRescaleImage(
$this->_params->width,
$this->_params->height,
$this->_params->delta_x,
$this->_params->rigidity
);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/PhotoStack.php 0000664 0001750 0001750 00000023756 13160240461 022162 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Effect for composing multiple images into a single image.
*
* The technique for the Polaroid-like stack using the Imagick extension is
* credited to Mikko Koppanen and is documented at http://valokuva.org
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_PhotoStack extends Horde_Image_Effect
{
/**
* Valid parameters for the stack effect:
* - images: (array) An array of Horde_Image objects to stack. Images
* are stacked in a FIFO manner, so that the top-most image is
* the last one in this array.
* - type: (string) Determines the style for the composition.
* 'plain' or 'polaroid' are supported.
* - resize_height: (integer) The height that each individual thumbnail
* should be resized to before composing on the image.
* - padding: (integer) How much padding should we ensure is left around
* the active image area?
* - background: (string) The background canvas color - this is used as
* the color to set any padding to.
* - bordercolor: (string) If using type 'plain' this sets the color of
* the border that each individual thumbnail gets.
* - borderwidth: (integer) If using type 'plain' this sets the width of
* the border on each individual thumbnail.
* - offset: (integer) If using type 'plain' this determines the amount
* of x and y offset to give each successive image when it is
* placed on the top of the stack.
*
* @var array
*/
protected $_params = array(
'type' => 'plain',
'resize_height' => '150',
'padding' => 0,
'background' => 'none',
'bordercolor' => '#333',
'borderwidth' => 1,
'borderrounding' => 10,
'offset' => 5
);
/**
* Applies the effect.
*/
public function apply()
{
$i = 1;
$cnt = count($this->_params['images']);
if ($cnt <= 0) {
throw new Horde_Image_Exception('No Images provided.');
}
if (!method_exists($this->_image->imagick, 'polaroidImage') ||
!method_exists($this->_image->imagick, 'trimImage')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library to use the PhotoStack effect.');
}
$imgs = array();
$length = 0;
try {
switch ($this->_params['type']) {
case 'plain':
case 'rounded':
$haveBottom = false;
// First, we need to resize the top image to get the dimensions
// for the rest of the stack.
$topimg = new Imagick();
$topimg->clear();
$topimg->readImageBlob(
$this->_params['images'][$cnt - 1]->raw()
);
$topimg->thumbnailImage(
$this->_params['resize_height'],
$this->_params['resize_height'],
true);
if ($this->_params['type'] == 'rounded') {
$topimg = $this->_roundBorder($topimg);
}
$size = $topimg->getImageGeometry();
foreach ($this->_params['images'] as $image) {
$imgk= new Imagick();
$imgk->clear();
$imgk->readImageBlob($image->raw());
// Either resize the thumbnail to match the top image or we
// *are* the top image already.
if ($i++ <= $cnt) {
$imgk->thumbnailImage(
$size['width'], $size['height'], false
);
} else {
$imgk->destroy();
$imgk = $this->_image->cloneImagickObject($topimg);
}
if ($this->_params['type'] == 'rounded') {
$imgk = $this->_roundBorder($imgk);
} else {
$imgk->borderImage(
$this->_params['bordercolor'],
$this->_params['borderwidth'],
$this->_params['borderwidth']
);
}
// Only shadow the bottom image for 'plain' stacks
if (!$haveBottom) {
$shad = $this->_image->cloneImagickObject($imgk);
$shad->setImageBackgroundColor(
new ImagickPixel('black')
);
$shad->shadowImage(80, 4, 0, 0);
$shad->compositeImage(
$imgk, Imagick::COMPOSITE_OVER, 0, 0
);
$imgk->clear();
$imgk->addImage($shad);
$shad->destroy();
$haveBottom = true;
}
// Get the geometry of the image and remember the largest.
$geo = $imgk->getImageGeometry();
$length = max(
$length,
sqrt(pow($geo['height'], 2) + pow($geo['width'], 2))
);
$imgs[] = $imgk;
}
break;
case 'polaroid':
foreach ($this->_params['images'] as $image) {
// @TODO: instead of doing $image->raw(), we might be able
// to clone the imagick object if we can do it
// cleanly might be faster, less memory intensive?
$imgk = new Imagick();
$imgk->clear();
$imgk->readImageBlob($image->raw());
$imgk->thumbnailImage(
$this->_params['resize_height'],
$this->_params['resize_height'],
true
);
$imgk->setImageBackgroundColor('black');
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$result = $imgk->polaroidImage(new ImagickDraw(), $angle);
// Get the geometry of the image and remember the largest.
$geo = $imgk->getImageGeometry();
$length = max(
$length,
sqrt(pow($geo['height'], 2) + pow($geo['width'], 2))
);
$imgs[] = $imgk;
}
break;
}
// Make sure the background canvas is large enough to hold it all.
$this->_image->imagick->thumbnailImage(
$length * 1.5 + 20,
$length * 1.5 + 20
);
// x and y offsets.
$xo = $yo = (count($imgs) + 1) * $this->_params['offset'];
foreach ($imgs as $image) {
if ($this->_params['type'] == 'polaroid') {
$xo = mt_rand(1, $this->_params['resize_height'] / 2);
$yo = mt_rand(1, $this->_params['resize_height'] / 2);
} elseif ($this->_params['type'] == 'plain' ||
$this->_params['type'] == 'rounded') {
$xo -= $this->_params['offset'];
$yo -= $this->_params['offset'];
}
$this->_image->imagick->compositeImage(
$image, Imagick::COMPOSITE_OVER, $xo, $yo
);
$image->removeImage();
$image->destroy();
}
// Trim the canvas before resizing to keep the thumbnails as large
// as possible.
$this->_image->imagick->trimImage(0);
if ($this->_params['padding'] ||
$this->_params['background'] != 'none') {
$this->_image->imagick->borderImage(
new ImagickPixel($this->_params['background']),
$this->_params['padding'],
$this->_params['padding']);
}
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
private function _roundBorder($image)
{
$context = array('tmpdir' => $this->_image->getTmpDir());
$size = $image->getImageGeometry();
$new = new Horde_Image_Imagick(array(), $context);
$new->loadString($image->getImageBlob());
$image->destroy();
$new->addEffect(
'RoundCorners',
array('border' => 2, 'bordercolor' => '#111')
);
$new->applyEffects();
$return = new Imagick();
$return->newImage(
$size['width'] + $this->_params['borderwidth'],
$size['height'] + $this->_params['borderwidth'],
$this->_params['bordercolor']
);
$return->setImageFormat($this->_image->getType());
$return->clear();
$return->readImageBlob($new->raw());
return $return;
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/PolaroidImage.php 0000775 0001750 0001750 00000005373 13160240461 022615 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Effect for creating a polaroid looking image.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_PolaroidImage extends Horde_Image_Effect
{
/**
* Valid parameters for the polaroid effect:
* - background: (string) The color of the image background.
* - angle: (integer) Angle to rotate the image.
* - shadowcolor: (string) The color of the image shadow.
*
* @var array
*/
protected $_params = array(
'background' => 'none',
'angle' => 0,
'shadowcolor' => 'black'
);
/**
* Applies the effect.
*/
public function apply()
{
if (!method_exists($this->_image->imagick, 'polaroidImage') ||
!method_exists($this->_image->imagick, 'trimImage')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library to use the PolaroidImage effect.');
}
try {
// This determines the color of the underlying shadow.
$this->_image->imagick->setImageBackgroundColor(
new ImagickPixel($this->_params['shadowcolor'])
);
$this->_image->imagick->polaroidImage(
new ImagickDraw(), $this->_params['angle']
);
// We need to create a new image to composite the polaroid over.
// (yes, even if it's a transparent background evidently)
$size = $this->_image->getDimensions();
$imk = new Imagick();
$imk->newImage(
$size['width'], $size['height'], $this->_params['background']
);
$imk->setImageFormat($this->_image->getType());
$result = $imk->compositeImage(
$this->_image->imagick, Imagick::COMPOSITE_OVER, 0, 0
);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($imk);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$imk->destroy();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/RoundCorners.php 0000664 0001750 0001750 00000007013 13160240461 022512 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for round image corners.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (integer) Radius of rounded corners.
*
* @var array
*/
protected $_params = array(
'radius' => 10,
'background' => 'none',
'border' => 0,
'bordercolor' => 'none'
);
public function apply()
{
if (!method_exists($this->_image->imagick, 'roundCorners')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library (> 6.2.8) to use the RoundCorners effect.');
}
$round = $this->_params['radius'];
try {
$this->_image->imagick->roundCorners($round, $round);
// Using a border?
if ($this->_params['bordercolor'] != 'none' &&
$this->_params['border'] > 0) {
$size = $this->_image->getDimensions();
$new = new Imagick();
$new->newImage(
$size['width'] + $this->_params['border'],
$size['height'] + $this->_params['border'],
$this->_params['bordercolor']
);
$new->setImageFormat($this->_image->getType());
$new->roundCorners($round, $round);
$new->compositeImage(
$this->_image->imagick,
Imagick::COMPOSITE_OVER,
round($this->_params['border'] / 2),
round($this->_params['border'] / 2)
);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($new);
$new->destroy();
}
// If we have a background other than 'none' we need to compose two
// images together to make sure we *have* a background. We can't
// use border because we don't want to extend the image area, just
// fill in the parts removed by the rounding.
if ($this->_params['background'] != 'none') {
$size = $this->_image->getDimensions();
$new = new Imagick();
$new->newImage(
$size['width'],
$size['height'],
$this->_params['background']
);
$new->setImageFormat($this->_image->getType());
$new->compositeImage(
$this->_image->imagick,
Imagick::COMPOSITE_OVER,
0, 0
);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($new);
$new->destroy();
}
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
// Reset width/height since these might have changed
$this->_image->clearGeometry();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/SmartCrop.php 0000664 0001750 0001750 00000013657 13160240461 022014 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for determining the best crop based on the center of edginess.
*
* Based on ideas and code by Jue Wang
* http://jueseph.com/2010/06/opticrop-usage-and-implementation/
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
* - width: (integer) Target width.
* - height: (integer) Target height.
*
* @var array
*/
protected $_params = array();
/**
* Applies the effect.
*/
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
// Existing geometry
$geometry = $this->_image->getDimensions();
$w0 = $geometry['width'];
$h0 = $geometry['height'];
$w = $this->_params->width;
$h = $this->_params->height;
// @TODO: Parameterize these
$r = 1; // radius of edge filter
$nk = 9; // scale count: number of crop sizes to try
$gamma = 0.2; // edge normalization parameter -- see documentation
// Target AR
$ar = $this->_params->width / $this->_params->height;
// Existing AR
$ar0 = $w0 / $h0;
$this->_logger->debug(
sprintf("SmartCrop: %d x %d => %d x %d ", $w0, $h0, $w, $h)
);
$this->_logger->debug('OAR: ' . $ar0);
$this->_logger->debug('TAR: ' . $ar);
try {
// Compute COE
$img = $this->_image->cloneImagickObject();
$img->edgeImage($r);
$img->modulateImage(100,0,100);
$img->blackThresholdImage("#0f0f0f");
$xcenter = $ycenter = $sum = 0;
$n = 100000;
for ($k = 0; $k < $n; $k++) {
$i = mt_rand(0, $w0 - 1);
$j = mt_rand(0, $h0 - 1);
$pixel = $img->getImagePixelColor($i, $j);
$val = $pixel->getColor();
$val = $val['b'];
$sum += $val;
$xcenter = $xcenter + ($i + 1) * $val;
$ycenter = $ycenter + ($j + 1) * $val;
}
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$xcenter /= $sum;
$ycenter /= $sum;
$this->_logger->debug('COE: ' . $xcenter . 'x' . $ycenter);
// crop source img to target AR
if ($w0 / $h0 > $ar) {
// source AR wider than target
// crop width to target AR
$wcrop0 = round($ar * $h0);
$hcrop0 = $h0;
} else {
// crop height to target AR
$wcrop0 = $w0;
$hcrop0 = round($w0 / $ar);
}
// crop parameters for all scales and translations
$params = array();
// crop at different scales
$hgap = $hcrop0 - $h;
$hinc = ($nk == 1) ? 0 : $hgap / ($nk - 1);
$wgap = $wcrop0 - $w;
$winc = ($nk == 1) ? 0 : $wgap / ($nk - 1);
// find window with highest normalized edginess
$n = 10000;
$maxbetanorm = 0;
$maxfile = '';
$maxparam = array('w' => 0, 'h' => 0, 'x' => 0, 'y' => 0);
try {
for ($k = 0; $k < $nk; $k++) {
$hcrop = round($hcrop0 - $k * $hinc);
$wcrop = round($wcrop0 - $k * $winc);
$xcrop = $xcenter - $wcrop / 2;
$ycrop = $ycenter - $hcrop / 2;
if ($xcrop < 0) {
$xcrop = 0;
}
if ($xcrop + $wcrop > $w0) {
$xcrop = $w0 - $wcrop;
}
if ($ycrop < 0) {
$ycrop = 0;
}
if ($ycrop+$hcrop > $h0) {
$ycrop = $h0 - $hcrop;
}
$this->_logger->debug("crop: $wcrop, $hcrop, $xcrop, $ycrop");
$beta = 0;
for ($c = 0; $c < $n; $c++) {
$i = mt_rand(0, $wcrop - 1);
$j = mt_rand(0, $hcrop - 1);
$pixel = $img->getImagePixelColor($xcrop + $i, $ycrop + $j);
$val = $pixel->getColor();
$beta += $val['b'];// & 0xFF;
}
$area = $wcrop * $hcrop;
$betanorm = $beta / ($n * pow($area, $gamma - 1));
// best image found, save the params
if ($betanorm > $maxbetanorm) {
$this->_logger->debug('Found best');
$maxbetanorm = $betanorm;
$maxparam['w'] = $wcrop;
$maxparam['h'] = $hcrop;
$maxparam['x'] = $xcrop;
$maxparam['y'] = $ycrop;
}
}
$this->_logger->debug('Cropping');
// Crop to best
$this->_image->imagick->cropImage($maxparam['w'],
$maxparam['h'],
$maxparam['x'],
$maxparam['y']);
$this->_image->imagick->scaleImage($w, $h);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$img->destroy();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/TextWatermark.php 0000664 0001750 0001750 00000004471 13160240461 022676 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image effect for watermarking images with text.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
* - text: [REQUIRED] (string) The text of the watermark.
* - halign: (string) The horizontal placement
* - valign: (string) The vertical placement
* - font: (string) The font name or family to use
* - fontsize: (string) The size of the font to use (small, medium,
* large, giant)
*
* @var array
*/
protected $_params = array(
'halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small'
);
/**
* Applies the effect.
*/
public function apply()
{
/* Determine placement on image */
switch ($this->_params['valign']) {
case 'bottom':
$v = 'south';
break;
case 'center':
$v = 'center';
break;
default:
$v = 'north';
}
switch ($this->_params['halign']) {
case 'right':
$h = 'east';
break;
case 'center':
$h = 'center';
break;
default:
$h = 'west';
}
if (($v == 'center' && $h != 'center') ||
($v == 'center' && $h == 'center')) {
$gravity = $h;
} elseif ($h == 'center' && $v != 'center') {
$gravity = $v;
} else {
$gravity = $v . $h;
}
/* Determine font point size */
$point = Horde_Image::getFontSize($this->_params['fontsize']);
//@TODO:
throw new Horde_Image_Exception('Not Yet Implemented.');
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Imagick/Unsharpmask.php 0000664 0001750 0001750 00000005400 13160240461 022361 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Unsharp mask Image effect.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Imagick_Unsharpmask extends Horde_Image_Effect
{
/**
* Valid parameters:
* - radius: (float) Thickness of the sharpened edge. Should be greater
* than sigma (or 0, and imagick will attempt to auto choose).
* In general, radius should be roughly output dpi / 150. So
* for display purposes a radius of 0.5 is suggested.
* - amount: (float) Amount of the difference between original and the
* blur image that gets added back to the original. Can be
* thought of as the "strength" of the effect. Too high may
* cause blocking of shadows and highlights. Given a decimal
* value indicating percentage, e.g. 1.2 = 120%
* - threshold: (float) Determines how large the brightness delta between
* adjacent pixels needs to be to sharpen the edge. Larger
* values == less sharpening. Useful for preventing noisy
* images from being oversharpened.
* - channel: (integer) Which channel to apply the sharpening to.
*
* @var array
*/
protected $_params = array(
'radius' => 0.5,
'amount' => 1,
'threshold' => 0.05,
'channel' => Imagick::CHANNEL_ALL
);
/**
* Applies the effect.
*/
public function apply()
{
/* Calculate appropriate sigma:
* Determines how the sharpening is graduated away from the center
* pixel of the sharpened edge. In general, if radius < 1, then sigma =
* radius else sigma = sqrt(radius) */
$this->_params['sigma'] = ($this->_params['radius'] < 1)
? $this->_params['radius']
: sqrt($this->_params['radius']);
try {
$this->_image->imagick->unsharpMaskImage(
$this->_params['radius'],
$this->_params['sigma'],
$this->_params['amount'],
$this->_params['threshold'],
$this->_params['channel']
);
} catch (Imagick_Exception $e) {
throw new Horde_Image_Exception($e);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect/Border.php 0000664 0001750 0001750 00000003114 13160240461 017736 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Image border decorator for the Horde_Image package.
*
* @author Chuck Hagenbuch
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect_Border extends Horde_Image_Effect
{
/**
* Valid parameters for border effects:
* - bordercolor: Border color. Defaults to black.
* - borderwidth: Border thickness, defaults to 1 pixel.
* - preserve: Preserves the alpha transparency layer (if present)
*
* @var array
*/
protected $_params = array(
'bordercolor' => 'black',
'borderwidth' => 1,
'preserve' => true
);
/**
* Draws the border.
*
* This draws the configured border to the provided image. Beware, that
* every pixel inside the border clipping will be overwritten with the
* background color.
*/
public function apply()
{
$dimension = $this->_image->getDimensions();
$this->_image->rectangle(
0,
0,
$dimension['width'],
$dimension['height'],
$this->_params['bordercolor'],
'none'
);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Base.php 0000664 0001750 0001750 00000003311 13160240461 020325 0 ustar jan jan
* @category Horde
* @package Image
*/
/**
* TODO
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Base
{
/**
* Look up the data type.
*
* @param string The string representation of the hex type code.
*
* @return array An array containing the string type name in the 0 index
* and the integer size in the 1 index.
*/
protected function _lookupType($type)
{
switch($type) {
case '0001': $type = 'UBYTE'; $size = 1; break;
case '0002': $type = 'ASCII'; $size = 1; break;
case '0003': $type = 'USHORT'; $size = 2; break;
case '0004': $type = 'ULONG'; $size = 4; break;
case '0005': $type = 'URATIONAL'; $size = 8; break;
case '0006': $type = 'SBYTE'; $size = 1; break;
case '0007': $type = 'UNDEFINED'; $size = 1; break;
case '0008': $type = 'SSHORT'; $size = 2; break;
case '0009': $type = 'SLONG'; $size = 4; break;
case '000a': $type = 'SRATIONAL'; $size = 8; break;
case '000b': $type = 'FLOAT'; $size = 4; break;
case '000c': $type = 'DOUBLE'; $size = 8; break;
default: $type = 'error:' . $type; $size = 0; break;
}
return array($type, $size);
}
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Canon.php 0000664 0001750 0001750 00000073040 13160240461 020517 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Canon extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0001': $tag = 'Settings 1'; break;
case '0004': $tag = 'Settings 4'; break;
case '0006': $tag = 'ImageType'; break;
case '0007': $tag = 'FirmwareVersion'; break;
case '0008': $tag = 'ImageNumber'; break;
case '0009': $tag = 'OwnerName'; break;
case '000c': $tag = 'CameraSerialNumber'; break;
case '000f': $tag = 'CustomFunctions'; break;
default: $tag = sprintf(Horde_Image_Translation::t("Unknown: (%s)"), $tag); break;
}
return $tag;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data, $exif, &$result)
{
$place = 0;
switch ($type) {
case 'ASCII':
$result = $data = str_replace('\0', '', $data);
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0204') {
//DigitalZoom
$data = $data . 'x';
}
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
$result['RAWDATA'] = $data;
// TODO: split this code up
switch ($tag) {
case '0001':
//first chunk
$result['Bytes'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;
if ($result['Bytes'] != strlen($data) / 2) {
//Bad chunk
return $result;
}
$result['Macro'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//1
switch($result['Macro']) {
case 1: $result['Macro'] = Horde_Image_Translation::t("Macro"); break;
case 2: $result['Macro'] = Horde_Image_Translation::t("Normal"); break;
default: $result['Macro'] = Horde_Image_Translation::t("Unknown");
}
$result['SelfTimer'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//2
switch($result['SelfTimer']) {
case 0: $result['SelfTimer'] = Horde_Image_Translation::t("Off"); break;
default: $result['SelfTimer'] .= Horde_Image_Translation::t("/10s");
}
$result['Quality'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//3
switch($result['Quality']) {
case 2: $result['Quality'] = Horde_Image_Translation::t("Normal"); break;
case 3: $result['Quality'] = Horde_Image_Translation::t("Fine"); break;
case 5: $result['Quality'] = Horde_Image_Translation::t("Superfine"); break;
default: $result['Quality'] = Horde_Image_Translation::t("Unknown");
}
$result['Flash'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//4
switch($result['Flash']) {
case 0: $result['Flash'] = Horde_Image_Translation::t("Off"); break;
case 1: $result['Flash'] = Horde_Image_Translation::t("Auto"); break;
case 2: $result['Flash'] = Horde_Image_Translation::t("On"); break;
case 3: $result['Flash'] = Horde_Image_Translation::t("Red Eye Reduction"); break;
case 4: $result['Flash'] = Horde_Image_Translation::t("Slow Synchro"); break;
case 5: $result['Flash'] = Horde_Image_Translation::t("Auto + Red Eye Reduction"); break;
case 6: $result['Flash'] = Horde_Image_Translation::t("On + Red Eye Reduction"); break;
case 16: $result['Flash'] = Horde_Image_Translation::t("External Flash"); break;
default: $result['Flash'] = Horde_Image_Translation::t("Unknown");
}
$result['DriveMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//5
switch($result['DriveMode']) {
case 0: $result['DriveMode'] = Horde_Image_Translation::t("Single/Timer"); break;
case 1: $result['DriveMode'] = Horde_Image_Translation::t("Continuous"); break;
default: $result['DriveMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//6
$result['FocusMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//7
switch($result['FocusMode']) {
case 0: $result['FocusMode'] = Horde_Image_Translation::t("One-Shot"); break;
case 1: $result['FocusMode'] = Horde_Image_Translation::t("AI Servo"); break;
case 2: $result['FocusMode'] = Horde_Image_Translation::t("AI Focus"); break;
case 3: $result['FocusMode'] = Horde_Image_Translation::t("Manual Focus"); break;
case 4: $result['FocusMode'] = Horde_Image_Translation::t("Single"); break;
case 5: $result['FocusMode'] = Horde_Image_Translation::t("Continuous"); break;
case 6: $result['FocusMode'] = Horde_Image_Translation::t("Manual Focus"); break;
default: $result['FocusMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//8
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place,4 )));
$place+=4;//9
$result['ImageSize'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//10
switch($result['ImageSize']) {
case 0: $result['ImageSize'] = Horde_Image_Translation::t("Large"); break;
case 1: $result['ImageSize'] = Horde_Image_Translation::t("Medium"); break;
case 2: $result['ImageSize'] = Horde_Image_Translation::t("Small"); break;
default: $result['ImageSize'] = Horde_Image_Translation::t("Unknown");
}
$result['EasyShooting'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//11
switch($result['EasyShooting']) {
case 0: $result['EasyShooting'] = Horde_Image_Translation::t("Full Auto"); break;
case 1: $result['EasyShooting'] = Horde_Image_Translation::t("Manual"); break;
case 2: $result['EasyShooting'] = Horde_Image_Translation::t("Landscape"); break;
case 3: $result['EasyShooting'] = Horde_Image_Translation::t("Fast Shutter"); break;
case 4: $result['EasyShooting'] = Horde_Image_Translation::t("Slow Shutter"); break;
case 5: $result['EasyShooting'] = Horde_Image_Translation::t("Night"); break;
case 6: $result['EasyShooting'] = Horde_Image_Translation::t("Black & White"); break;
case 7: $result['EasyShooting'] = Horde_Image_Translation::t("Sepia"); break;
case 8: $result['EasyShooting'] = Horde_Image_Translation::t("Portrait"); break;
case 9: $result['EasyShooting'] = Horde_Image_Translation::t("Sport"); break;
case 10: $result['EasyShooting'] = Horde_Image_Translation::t("Macro/Close-Up"); break;
case 11: $result['EasyShooting'] = Horde_Image_Translation::t("Pan Focus"); break;
default: $result['EasyShooting'] = Horde_Image_Translation::t("Unknown");
}
$result['DigitalZoom'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//12
switch($result['DigitalZoom']) {
case 0:
case 65535: $result['DigitalZoom'] = Horde_Image_Translation::t("None"); break;
case 1: $result['DigitalZoom'] = Horde_Image_Translation::t("2x"); break;
case 2: $result['DigitalZoom'] = Horde_Image_Translation::t("4x"); break;
default: $result['DigitalZoom'] = Horde_Image_Translation::t("Unknown");
}
$result['Contrast'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//13
switch($result['Contrast']) {
case 0: $result['Contrast'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Contrast'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Contrast'] = Horde_Image_Translation::t("Low"); break;
default: $result['Contrast'] = Horde_Image_Translation::t("Unknown");
}
$result['Saturation'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//14
switch($result['Saturation']) {
case 0: $result['Saturation'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Saturation'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Saturation'] = Horde_Image_Translation::t("Low"); break;
default: $result['Saturation'] = Horde_Image_Translation::t("Unknown");
}
$result['Sharpness'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//15
switch($result['Sharpness']) {
case 0: $result['Sharpness'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Sharpness'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Sharpness'] = Horde_Image_Translation::t("Low"); break;
default: $result['Sharpness'] = Horde_Image_Translation::t("Unknown");
}
$result['ISO'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//16
switch($result['ISO']) {
case 32767:
case 0:
$result['ISO'] = isset($exif['SubIFD']['ISOSpeedRatings']) ?
$exif['SubIFD']['ISOSpeedRatings'] :
'Unknown';
break;
case 15:
$result['ISO'] = Horde_Image_Translation::t("Auto");
break;
case 16:
$result['ISO'] = 50;
break;
case 17:
$result['ISO'] = 100;
break;
case 18:
$result['ISO'] = 200;
break;
case 19:
$result['ISO'] = 400;
break;
default:
$result['ISO'] = Horde_Image_Translation::t("Unknown");
}
$result['MeteringMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//17
switch($result['MeteringMode']) {
case 3: $result['MeteringMode'] = Horde_Image_Translation::t("Evaluative"); break;
case 4: $result['MeteringMode'] = Horde_Image_Translation::t("Partial"); break;
case 5: $result['MeteringMode'] = Horde_Image_Translation::t("Center-weighted"); break;
default: $result['MeteringMode'] = Horde_Image_Translation::t("Unknown");
}
$result['FocusType'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//18
switch($result['FocusType']) {
case 0: $result['FocusType'] = Horde_Image_Translation::t("Manual"); break;
case 1: $result['FocusType'] = Horde_Image_Translation::t("Auto"); break;
case 3: $result['FocusType'] = Horde_Image_Translation::t("Close-up (Macro)"); break;
case 8: $result['FocusType'] = Horde_Image_Translation::t("Locked (Pan Mode)"); break;
default: $result['FocusType'] = Horde_Image_Translation::t("Unknown");
}
$result['AFPointSelected'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//19
switch($result['AFPointSelected']) {
case 12288: $result['AFPointSelected'] = Horde_Image_Translation::t("Manual Focus"); break;
case 12289: $result['AFPointSelected'] = Horde_Image_Translation::t("Auto Selected"); break;
case 12290: $result['AFPointSelected'] = Horde_Image_Translation::t("Right"); break;
case 12291: $result['AFPointSelected'] = Horde_Image_Translation::t("Center"); break;
case 12292: $result['AFPointSelected'] = Horde_Image_Translation::t("Left"); break;
default: $result['AFPointSelected'] = Horde_Image_Translation::t("Unknown");
}
$result['ExposureMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//20
switch($result['ExposureMode']) {
case 0: $result['ExposureMode'] = Horde_Image_Translation::t("EasyShoot"); break;
case 1: $result['ExposureMode'] = Horde_Image_Translation::t("Program"); break;
case 2: $result['ExposureMode'] = Horde_Image_Translation::t("Tv"); break;
case 3: $result['ExposureMode'] = Horde_Image_Translation::t("Av"); break;
case 4: $result['ExposureMode'] = Horde_Image_Translation::t("Manual"); break;
case 5: $result['ExposureMode'] = Horde_Image_Translation::t("Auto-DEP"); break;
default: $result['ExposureMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//21
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//22
$result['LongFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//23
$result['LongFocalLength'] .= 'focal units';
$result['ShortFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//24
$result['ShortFocalLength'] .= ' focal units';
$result['FocalUnits'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//25
$result['FocalUnits'] .= ' per mm';
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//26
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//27
$result['FlashActivity'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//28
switch($result['FlashActivity']) {
case 0: $result['FlashActivity'] = Horde_Image_Translation::t("Flash Did Not Fire"); break;
case 1: $result['FlashActivity'] = Horde_Image_Translation::t("Flash Fired"); break;
default: $result['FlashActivity'] = Horde_Image_Translation::t("Unknown");
}
$result['FlashDetails'] = str_pad(base_convert(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)), 16, 2), 16, '0', STR_PAD_LEFT);
$place += 4;//29
$flashDetails = array();
if (substr($result['FlashDetails'], 1, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("External E-TTL");
}
if (substr($result['FlashDetails'], 2, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("Internal Flash");
}
if (substr($result['FlashDetails'], 4, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("FP sync used");
}
if (substr($result['FlashDetails'], 8, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("2nd(rear)-curtain sync used");
}
if (substr($result['FlashDetails'], 12, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("1st curtain sync");
}
$result['FlashDetails'] = implode(',', $flashDetails);
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//30
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//31
$anotherFocusMode = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//32
if (strpos(Horde_String::upper($exif['IFD0']['Model']), 'G1') !== false) {
switch($anotherFocusMode) {
case 0: $result['FocusMode'] = Horde_Image_Translation::t("Single"); break;
case 1: $result['FocusMode'] = Horde_Image_Translation::t("Continuous"); break;
default: $result['FocusMode'] = Horde_Image_Translation::t("Unknown");
}
}
break;
case '0004':
//second chunk
$result['Bytes']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//0
if ($result['Bytes'] != strlen($data) / 2) {
return $result; //Bad chunk
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//1
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//2
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//3
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//4
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//5
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//6
$result['WhiteBalance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//7
switch($result['WhiteBalance']) {
case 0: $result['WhiteBalance'] = Horde_Image_Translation::t("Auto"); break;
case 1: $result['WhiteBalance'] = Horde_Image_Translation::t("Sunny"); break;
case 2: $result['WhiteBalance'] = Horde_Image_Translation::t("Cloudy"); break;
case 3: $result['WhiteBalance'] = Horde_Image_Translation::t("Tungsten"); break;
case 4: $result['WhiteBalance'] = Horde_Image_Translation::t("Fluorescent"); break;
case 5: $result['WhiteBalance'] = Horde_Image_Translation::t("Flash"); break;
case 6: $result['WhiteBalance'] = Horde_Image_Translation::t("Custom"); break;
default: $result['WhiteBalance'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//8
$result['SequenceNumber'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//9
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//10
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//11
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data ,$place, 4)));
$place += 4;//12
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//13
$result['AFPointUsed']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//14
$afPointUsed = array();
if ($result['AFPointUsed'] & 0x0001) {
$afPointUsed[] = Horde_Image_Translation::t("Right"); //bit 0
}
if ($result['AFPointUsed'] & 0x0002) {
$afPointUsed[] = Horde_Image_Translation::t("Center"); //bit 1
}
if ($result['AFPointUsed'] & 0x0004) {
$afPointUsed[] = Horde_Image_Translation::t("Left"); //bit 2
}
if ($result['AFPointUsed'] & 0x0800) {
$afPointUsed[] = 12; //bit 12
}
if ($result['AFPointUsed'] & 0x1000) {
$afPointUsed[] = 13; //bit 13
}
if ($result['AFPointUsed'] & 0x2000) {
$afPointUsed[] = 14; //bit 14
}
if ($result['AFPointUsed'] & 0x4000) {
$afPointUsed[] = 15; //bit 15
}
$result['AFPointUsed'] = implode(',', $afPointUsed);
$result['FlashBias'] = Horde_Image_Exif::intel2Moto(substr($data, $place, 4));
$place += 4;//15
switch($result['FlashBias']) {
case 'ffc0': $result['FlashBias'] = '-2 EV'; break;
case 'ffcc': $result['FlashBias'] = '-1.67 EV'; break;
case 'ffd0': $result['FlashBias'] = '-1.5 EV'; break;
case 'ffd4': $result['FlashBias'] = '-1.33 EV'; break;
case 'ffe0': $result['FlashBias'] = '-1 EV'; break;
case 'ffec': $result['FlashBias'] = '-0.67 EV'; break;
case 'fff0': $result['FlashBias'] = '-0.5 EV'; break;
case 'fff4': $result['FlashBias'] = '-0.33 EV'; break;
case '0000': $result['FlashBias'] = '0 EV'; break;
case '000c': $result['FlashBias'] = '0.33 EV'; break;
case '0010': $result['FlashBias'] = '0.5 EV'; break;
case '0014': $result['FlashBias'] = '0.67 EV'; break;
case '0020': $result['FlashBias'] = '1 EV'; break;
case '002c': $result['FlashBias'] = '1.33 EV'; break;
case '0030': $result['FlashBias'] = '1.5 EV'; break;
case '0034': $result['FlashBias'] = '1.67 EV'; break;
case '0040': $result['FlashBias'] = '2 EV'; break;
default: $result['FlashBias'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//16
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//17
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//18
$result['SubjectDistance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//19
$result['SubjectDistance'] .= '/100 m';
break;
case '0008':
//image number
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
$result = round($data / 10000) . '-' . $data % 10000;
break;
case '000c':
//camera serial number
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
$result = '#' . bin2hex(substr($data, 0, 16)) . substr($data, 16, 16);
break;
}
break;
default:
if ($type != 'UNDEFINED') {
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
}
break;
}
return $data;
}
/**
* Canon Special data section.
*
* @see http://www.burren.cx/david/canon.html
* @see http://www.burren.cx/david/canon.html
* @see http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$place = 0; //current place
if ($result['Endien'] == 'Intel') {
$intel = 1;
} else {
$intel = 0;
}
$model = $result['IFD0']['Model'];
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel == 1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
if ($bytesofdata <= 0) {
return; //if this value is 0 or less then we have read all the tags we can
}
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
$exiferFileSize = 0;
if ($seek->seek($globalOffset + hexdec($value), false) && $bytesofdata < $exiferFileSize) {
$data = $seek->substring(0, $bytesofdata);
} else {
$result['Errors'] = $result['Errors']++;
$data = '';
}
}
// Ensure the index exists.
$result['SubIFD']['MakerNote'][$tag_name] = '';
$formated_data = $this->_formatData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Fujifilm.php 0000664 0001750 0001750 00000031376 13160240461 021234 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Fujifilm extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch ($tag) {
case '0000': return 'Version';
case '1000': return 'Quality';
case '1001': return 'Sharpness';
case '1002': return 'WhiteBalance';
case '1003': return 'Color';
case '1004': return 'Tone';
case '1010': return 'FlashMode';
case '1011': return 'FlashStrength';
case '1020': return 'Macro';
case '1021': return 'FocusMode';
case '1030': return 'SlowSync';
case '1031': return 'PictureMode';
case '1032': return 'Unknown';
case '1100': return 'ContinuousTakingBracket';
case '1200': return 'Unknown';
case '1300': return 'BlurWarning';
case '1301': return 'FocusWarning';
case '1302': return 'AEWarning';
default: return 'unknown:' . $tag;
}
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '1011') {
//FlashStrength
$data = $data . ' EV';
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '1001':
//Sharpness
switch ($data) {
case 1: $data = Horde_Image_Translation::t("Soft"); break;
case 2: $data = Horde_Image_Translation::t("Soft"); break;
case 3: $data = Horde_Image_Translation::t("Normal"); break;
case 4: $data = Horde_Image_Translation::t("Hard"); break;
case 5: $data = Horde_Image_Translation::t("Hard"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '1002':
//WhiteBalance
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 256: $data = Horde_Image_Translation::t("Daylight"); break;
case 512: $data = Horde_Image_Translation::t("Cloudy"); break;
case 768: $data = Horde_Image_Translation::t("DaylightColor-fluorescence"); break;
case 769: $data = Horde_Image_Translation::t("DaywhiteColor-fluorescence"); break;
case 770: $data = Horde_Image_Translation::t("White-fluorescence"); break;
case 1024: $data = Horde_Image_Translation::t("Incandescense"); break;
case 3840: $data = Horde_Image_Translation::t("Custom"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '1003':
//Color
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Chroma Saturation Normal(STD)"); break;
case 256: $data = Horde_Image_Translation::t("Chroma Saturation High"); break;
case 512: $data = Horde_Image_Translation::t("Chroma Saturation Low(ORG)"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1004':
//Tone
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Contrast Normal(STD)"); break;
case 256: $data = Horde_Image_Translation::t("Contrast High(HARD)"); break;
case 512: $data = Horde_Image_Translation::t("Contrast Low(ORG)"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1010':
//FlashMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
case 2: $data = Horde_Image_Translation::t("Off"); break;
case 3: $data = Horde_Image_Translation::t("Red-Eye Reduction"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1020':
//Macro
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1021':
//FocusMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Manual"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1030':
//SlowSync
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1031':
//PictureMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Portrait"); break;
case 2: $data = Horde_Image_Translation::t("Landscape"); break;
case 4: $data = Horde_Image_Translation::t("Sports"); break;
case 5: $data = Horde_Image_Translation::t("Night"); break;
case 6: $data = Horde_Image_Translation::t("Program AE"); break;
case 256: $data = Horde_Image_Translation::t("Aperture Prority AE"); break;
case 512: $data = Horde_Image_Translation::t("Shutter Priority"); break;
case 768: $data = Horde_Image_Translation::t("Manual Exposure"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1100':
//ContinuousTakingBracket
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1300':
//BlurWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("No Warning"); break;
case 1: $data = Horde_Image_Translation::t("Warning"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1301':
//FocusWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto Focus Good"); break;
case 1: $data = Horde_Image_Translation::t("Out of Focus"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1302':
//AEWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("AE Good"); break;
case 1: $data = Horde_Image_Translation::t("Over Exposure"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @return unknown_type
*/
public function parse($block, &$result)
{
$intel = true;
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
$num = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Gps.php 0000664 0001750 0001750 00000026514 13160240461 020216 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Gps extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag
*
* @param unknown_type $tag
* @return string
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0000': return 'Version';
//north or south
case '0001': return 'LatitudeRef';
//dd mm.mm or dd mm ss
case '0002': return 'Latitude';
//east or west
case '0003': return 'LongitudeRef';
//dd mm.mm or dd mm ss
case '0004': return 'Longitude';
//sea level or below sea level
case '0005': return 'AltitudeRef';
//positive rational number
case '0006': return 'Altitude';
//three positive rational numbers
case '0007': return 'Time';
//text string up to 999 bytes long
case '0008': return 'Satellite';
//in progress or interop
case '0009': return 'ReceiveStatus';
//2D or 3D
case '000a': return 'MeasurementMode';
//positive rational number
case '000b': return 'MeasurementPrecision';
//KPH, MPH, knots
case '000c': return 'SpeedUnit';
//positive rational number
case '000d': return 'ReceiverSpeed';
//true or magnetic north
case '000e': return 'MovementDirectionRef';
//positive rational number
case '000f': return 'MovementDirection';
//true or magnetic north
case '0010': return 'ImageDirectionRef';
//positive rational number
case '0011': return 'ImageDirection';
//text string up to 999 bytes long
case '0012': return 'GeodeticSurveyData';
//north or south
case '0013': return 'DestLatitudeRef';
//three positive rational numbers
case '0014': return 'DestinationLatitude';
//east or west
case '0015': return 'DestLongitudeRef';
//three positive rational numbers
case '0016': return 'DestinationLongitude';
//true or magnetic north
case '0017': return 'DestBearingRef';
//positive rational number
case '0018': return 'DestinationBearing';
//km, miles, knots
case '0019': return 'DestDistanceRef';
//positive rational number
case '001a': return 'DestinationDistance';
case '001b': return 'ProcessingMethod';
case '001c': return 'AreaInformation';
//text string 10 bytes long
case '001d': return 'Datestamp';
//integer in range 0-65535
case '001e': return 'DifferentialCorrection';
default: return 'unknown: ' . $tag;
}
}
/**
* Formats a rational number
*/
protected function _rational($data, $intel)
{
if ($intel == 1) {
//intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
//motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
$bottom = hexdec(substr($data, 0, 8));
} else {
$bottom = hexdec(substr($data, 8, 8));
}
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
return $data;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
// Latitude Reference, Longitude Reference
if ($tag == '0001' || $tag == '0003') {
$data = ($data{1} == $data{2} && $data{1} == $data{3}) ? $data{0} : $data;
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 1) {
//intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
//motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
$bottom = hexdec(substr($data, 0, 8));
} else {
$bottom = hexdec(substr($data, 8, 8));
}
if ($type == 'SRATIONAL' && $top > 2147483647) {
// make the number signed instead of unsigned
$top = $top - 4294967296;
}
switch ($tag) {
case '0002':
case '0004':
//Latitude, Longitude
if ($intel == 1) {
$seconds = $this->_rational(substr($data, 0, 16), $intel);
$hour = $this->_rational(substr($data, 32, 16), $intel);
} else {
$hour = $this->_rational(substr($data, 0, 16), $intel);
$seconds = $this->_rational(substr($data, 32, 16), $intel);
}
$minutes = $this->_rational(substr($data, 16, 16), $intel);
$data = array($hour, $minutes, $seconds);
break;
case '0007':
//Time
$seconds = $this->_rational(substr($data, 0, 16), $intel);
$minutes = $this->_rational(substr($data, 16, 16), $intel);
$hour = $this->_rational(substr($data, 32, 16), $intel);
$data = $hour . ':' . $minutes . ':' . $seconds;
break;
default:
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0006') {
$data .= 'm';
}
break;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
break;
case 'UNDEFINED':
break;
case 'UBYTE':
$data = bin2hex($data);
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($data);
}
switch ($tag) {
case '0000':
// VersionID
$data = hexdec(substr($data, 0, 2))
. '.' . hexdec(substr($data, 2, 2))
. '.' . hexdec(substr($data, 4, 2))
. '.'. hexdec(substr($data, 6, 2));
break;
case '0005':
// Altitude Reference
if ($data == '00000000') {
$data = 'Above Sea Level';
} elseif ($data == '01000000') {
$data = 'Below Sea Level';
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
* GPS Special data section
*
* @see http://drewnoakes.com/code/exif/sampleOutput.html
* @see http://www.geosnapper.com
*/
public function parse($block, &$result, $offset, $seek, $globalOffset)
{
if ($result['Endien'] == 'Intel') {
$intel = 1;
} else {
$intel = 0;
}
//offsets are from TIFF header which is 12 bytes from the start of the
//file
if (!$seek->seek($globalOffset + $offset, false)) {
$result['Errors'] = $result['Errors']++;
return $result;
}
$num = bin2hex($seek->substring(0, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['GPS']['NumTags'] = $num;
$block = $seek->substring(0, $num * 12);
$place = 0;
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < $num; $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte datatype
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte number of elements
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel==1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value or pointer to value if larger than 4 bytes
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
if ($seek->seek($globalOffset + hexdec($value), false)) {
$data = $seek->substring(0, $bytesofdata);
} elseif ($v == -1) {
$result['Errors'] = $result['Errors']++;
}
}
$result['GPS' . $tag_name] = $this->_formatData($type, $tag, $intel, $data);
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Nikon.php 0000664 0001750 0001750 00000040474 13160240461 020544 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Nikon extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @param $model
* @return unknown_type
*/
protected function _lookupTag($tag, $model)
{
switch ($model) {
case 0:
switch($tag) {
case '0003': $tag = 'Quality'; break;
case '0004': $tag = 'ColorMode'; break;
case '0005': $tag = 'ImageAdjustment'; break;
case '0006': $tag = 'CCDSensitivity'; break;
case '0007': $tag = 'WhiteBalance'; break;
case '0008': $tag = 'Focus'; break;
case '0009': $tag = 'Unknown2'; break;
case '000a': $tag = 'DigitalZoom'; break;
case '000b': $tag = 'Converter'; break;
default: $tag = 'unknown: ' . $tag; break;
}
break;
case 1:
switch($tag) {
case '0002': $tag = 'ISOSetting'; break;
case '0003': $tag = 'ColorMode'; break;
case '0004': $tag = 'Quality'; break;
case '0005': $tag = 'Whitebalance'; break;
case '0006': $tag = 'ImageSharpening'; break;
case '0007': $tag = 'FocusMode'; break;
case '0008': $tag = 'FlashSetting'; break;
case '0009': $tag = 'FlashMode'; break;
case '000b': $tag = 'WhiteBalanceFine'; break;
case '000f': $tag = 'ISOSelection'; break;
case '0013': $tag = 'ISOSelection2'; break;
case '0080': $tag = 'ImageAdjustment'; break;
case '0081': $tag = 'ToneCompensation'; break;
case '0082': $tag = 'Adapter'; break;
case '0083': $tag = 'LensType'; break;
case '0084': $tag = 'LensInfo'; break;
case '0085': $tag = 'ManualFocusDistance'; break;
case '0086': $tag = 'DigitalZoom'; break;
case '0087': $tag = 'FlashUsed'; break;
case '0088': $tag = 'AFFocusPosition'; break;
case '008d': $tag = 'ColorMode'; break;
case '0090': $tag = 'LightType'; break;
case '0094': $tag = 'Saturation'; break;
case '0095': $tag = 'NoiseReduction'; break;
case '0010': $tag = 'DataDump'; break;
default: $tag = 'unknown: ' . $tag; break;
}
break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $model
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $model, $data)
{
switch ($type) {
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0085' && $model == 1) {
//ManualFocusDistance
$data = $data . ' m';
}
if ($tag == '0086' && $model == 1) {
//DigitalZoom
$data = $data . 'x';
}
if ($tag == '000a' && $model == 0) {
//DigitalZoom
$data = $data . 'x';
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
if ($model != 0) {
break;
}
switch ($tag) {
case '0003':
//Quality
switch ($data) {
case 1: $data = Horde_Image_Translation::t("VGA Basic"); break;
case 2: $data = Horde_Image_Translation::t("VGA Normal"); break;
case 3: $data = Horde_Image_Translation::t("VGA Fine"); break;
case 4: $data = Horde_Image_Translation::t("SXGA Basic"); break;
case 5: $data = Horde_Image_Translation::t("SXGA Normal"); break;
case 6: $data = Horde_Image_Translation::t("SXGA Fine"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0004':
//Color
switch ($data) {
case 1: $data = Horde_Image_Translation::t("Color"); break;
case 2: $data = Horde_Image_Translation::t("Monochrome"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0005':
//Image Adjustment
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Normal"); break;
case 1: $data = Horde_Image_Translation::t("Bright+"); break;
case 2: $data = Horde_Image_Translation::t("Bright-"); break;
case 3: $data = Horde_Image_Translation::t("Contrast+"); break;
case 4: $data = Horde_Image_Translation::t("Contrast-"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0006':
//CCD Sensitivity
switch ($data) {
case 0: $data = 'ISO-80'; break;
case 2: $data = 'ISO-160'; break;
case 4: $data = 'ISO-320'; break;
case 5: $data = 'ISO-100'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0007':
//White Balance
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Preset"); break;
case 2: $data = Horde_Image_Translation::t("Daylight"); break;
case 3: $data = Horde_Image_Translation::t("Incandescense"); break;
case 4: $data = Horde_Image_Translation::t("Flourescence"); break;
case 5: $data = Horde_Image_Translation::t("Cloudy"); break;
case 6: $data = Horde_Image_Translation::t("SpeedLight"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '000b':
//Converter
switch ($data) {
case 0: $data = Horde_Image_Translation::t("None"); break;
case 1: $data = Horde_Image_Translation::t("Fisheye"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
case 'UNDEFINED':
if ($model != 1) {
break;
}
switch ($tag) {
case '0001':
$data = $data/100;
break;
case '0088':
//AF Focus Position
$temp = Horde_Image_Translation::t("Center");
$data = bin2hex($data);
$data = str_replace('01', 'Top', $data);
$data = str_replace('02', 'Bottom', $data);
$data = str_replace('03', 'Left', $data);
$data = str_replace('04', 'Right', $data);
$data = str_replace('00', '', $data);
if (!strlen($data)) {
$data = $temp;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($model != 1) {
break;
}
switch ($tag) {
case '0083':
//Lens Type
$data = hexdec(substr($data, 0, 2));
switch ($data) {
case 0: $data = Horde_Image_Translation::t("AF non D"); break;
case 1: $data = Horde_Image_Translation::t("Manual"); break;
case 2: $data = 'AF-D or AF-S'; break;
case 6: $data = 'AF-D G'; break;
case 10: $data = 'AF-D VR'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0087':
//Flash type
$data = hexdec(substr($data,0,2));
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Did Not Fire"); break;
case 4: $data = Horde_Image_Translation::t("Unknown"); break;
case 7: $data = Horde_Image_Translation::t("External"); break;
case 9: $data = Horde_Image_Translation::t("On Camera"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @return unknown_type
*/
public function parse($block, &$result)
{
$intel = $result['Endien'] == 'Intel';
$model = $result['IFD0']['Model'];
//these 6 models start with "Nikon". Other models dont.
if ($model == "E700\0" ||
$model == "E800\0" ||
$model == "E900\0" ||
$model == "E900S\0" ||
$model == "E910\0" ||
$model == "E950\0") {
//current place
$place = 8;
$model = 0;
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag, $model);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
//if tag is 0002 then its the ASCII value which we know is at 140 so calc offset
//THIS HACK ONLY WORKS WITH EARLY NIKON MODELS
if ($tag == '0002') {
$offset = hexdec($value) - 140;
}
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $model, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
} else {
//current place
$place = 0;
$model = 1;
$nikon = substr($block, $place, 8);
$place += 8;
$endien = substr($block, $place, 4);
$place += 4;
//2 bytes of 0x002a
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
//Then 4 bytes of offset to IFD0 (usually 8 which includes all 8
//bytes of TIFF header)
$offset = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
if (hexdec($offset) > 8) {
$place += $offset - 8;
}
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag, $model);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) + hexdec($offset) + 2, $bytesofdata);
}
$formated_data = $this->_formatData($type, $tag, $intel, $model, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Olympus.php 0000664 0001750 0001750 00000017336 13160240461 021137 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Olympus extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0200': $tag = 'SpecialMode'; break;
case '0201': $tag = 'JpegQual'; break;
case '0202': $tag = 'Macro'; break;
case '0203': $tag = 'Unknown1'; break;
case '0204': $tag = 'DigiZoom'; break;
case '0205': $tag = 'Unknown2'; break;
case '0206': $tag = 'Unknown3'; break;
case '0207': $tag = 'SoftwareRelease'; break;
case '0208': $tag = 'PictInfo'; break;
case '0209': $tag = 'CameraID'; break;
case '0f00': $tag = 'DataDump'; break;
default: $tag = 'unknown: ' . $tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
switch ($tag) {
case '0204':
//DigitalZoom
$data .= 'x';
break;
case '0205':
//Unknown2
$data = $top . '/' . $bottom;
break;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0201':
//JPEGQuality
switch ($data) {
case 1: $data = 'SQ'; break;
case 2: $data = 'HQ'; break;
case 3: $data = 'SHQ'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0202':
//Macro
switch ($data) {
case 0: $data = 'Normal'; break;
case 1: $data = 'Macro'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @param $seek
* @param $globalOffset
* @return unknown_type
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$intel = $result['Endien']=='Intel';
$model = $result['IFD0']['Model'];
// New header for new DSLRs - Check for it because the number of bytes
// that count the IFD fields differ in each case. Fixed by Zenphoto
// 2/24/08
$new = false;
if (substr($block, 0, 8) == "OLYMPUS\x00") {
$new = true;
} elseif (substr($block, 0, 7) == "OLYMP\x00\x01" ||
substr($block, 0, 7) == "OLYMP\x00\x02") {
$new = false;
} else {
// Header does not match known Olympus headers.
// This is not a valid OLYMPUS Makernote.
return false;
}
// Offset of IFD entry after Olympus header.
$place = 8;
$offset = 8;
// Get number of tags (1 or 2 bytes, depending on New or Old makernote)
$countfieldbits = $new ? 1 : 2;
// New makernote repeats 1-byte value twice, so increment $place by 2
// in either case.
$num = bin2hex(substr($block, $place, $countfieldbits));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$ntags = hexdec($num);
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = $ntags;
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < $ntags; $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
// $v = fseek($seek, $globalOffset + hexdec($value));
$result['Errors'] = $result['Errors']++;
$data = '';
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Panasonic.php 0000664 0001750 0001750 00000046226 13160240461 021402 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Panasonic extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch ($tag) {
case '0001': $tag = 'Quality'; break;
case '0002': $tag = 'FirmwareVersion'; break;
case '0003': $tag = 'WhiteBalance'; break;
case '0007': $tag = 'FocusMode'; break;
case '000f': $tag = 'AFMode'; break;
case '001a': $tag = 'ImageStabilizer'; break;
case '001c': $tag = 'MacroMode'; break;
case '001f': $tag = 'ShootingMode'; break;
case '0020': $tag = 'Audio'; break;
case '0021': $tag = 'DataDump'; break;
case '0023': $tag = 'WhiteBalanceBias'; break;
case '0024': $tag = 'FlashBias'; break;
case '0025': $tag = 'SerialNumber'; break;
case '0028': $tag = 'ColourEffect'; break;
case '002a': $tag = 'BurstMode'; break;
case '002b': $tag = 'SequenceNumber'; break;
case '002c': $tag = 'Contrast'; break;
case '002d': $tag = 'NoiseReduction'; break;
case '002e': $tag = 'SelfTimer'; break;
case '0030': $tag = 'Rotation'; break;
case '0032': $tag = 'ColorMode'; break;
case '0036': $tag = 'TravelDay'; break;
default: $tag = 'unknown:' . $tag; break;
}
return $tag;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'UBYTE':
case 'SBYTE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
if ($tag == '000f') {
//AFMode
switch ($data) {
case 256:
$data = Horde_Image_Translation::t("9-area-focusing");
break;
case 16:
$data = Horde_Image_Translation::t("1-area-focusing");
break;
case 4096:
$data = Horde_Image_Translation::t("3-area-focusing (High speed)");
break;
case 4112:
$data = Horde_Image_Translation::t("1-area-focusing (High speed)");
break;
case 1:
$data = Horde_Image_Translation::t("Spot-focusing");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0001':
//Image Quality
switch ($data) {
case 2:
$data = Horde_Image_Translation::t("High");
break;
case 3:
$data = Horde_Image_Translation::t("Standard");
break;
case 6:
$data = Horde_Image_Translation::t("Very High");
break;
case 7:
$data = Horde_Image_Translation::t("RAW");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0003':
//White Balance
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Auto");
break;
case 2:
$data = Horde_Image_Translation::t("Daylight");
break;
case 3:
$data = Horde_Image_Translation::t("Cloudy");
break;
case 4:
$data = Horde_Image_Translation::t("Halogen");
break;
case 5:
$data = Horde_Image_Translation::t("Manual");
break;
case 8:
$data = Horde_Image_Translation::t("Flash");
break;
case 10:
$data = Horde_Image_Translation::t("Black and White");
break;
case 11:
$data = Horde_Image_Translation::t("Manual");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '0007':
//Focus Mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Auto");
break;
case 2:
$data = Horde_Image_Translation::t("Manual");
break;
case 4:
$data = Horde_Image_Translation::t("Auto, Focus button");
break;
case 5:
$data = Horde_Image_Translation::t("Auto, Continuous");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001a':
//Image Stabilizer
switch ($data) {
case 2:
$data = Horde_Image_Translation::t("Mode 1");
break;
case 3:
$data = Horde_Image_Translation::t("Off");
break;
case 4:
$data = Horde_Image_Translation::t("Mode 2");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001c':
//Macro mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("On");
break;
case 2:
$data = Horde_Image_Translation::t("Off");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001f':
//Shooting Mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Normal");
break;
case 2:
$data = Horde_Image_Translation::t("Portrait");
break;
case 3:
$data = Horde_Image_Translation::t("Scenery");
break;
case 4:
$data = Horde_Image_Translation::t("Sports");
break;
case 5:
$data = Horde_Image_Translation::t("Night Portrait");
break;
case 6:
$data = Horde_Image_Translation::t("Program");
break;
case 7:
$data = Horde_Image_Translation::t("Aperture Priority");
break;
case 8:
$data = Horde_Image_Translation::t("Shutter Priority");
break;
case 9:
$data = Horde_Image_Translation::t("Macro");
break;
case 11:
$data = Horde_Image_Translation::t("Manual");
break;
case 13:
$data = Horde_Image_Translation::t("Panning");
break;
case 14:
$data = Horde_Image_Translation::t("Simple");
break;
case 18:
$data = Horde_Image_Translation::t("Fireworks");
break;
case 19:
$data = Horde_Image_Translation::t("Party");
break;
case 20:
$data = Horde_Image_Translation::t("Snow");
break;
case 21:
$data = Horde_Image_Translation::t("Night Scenery");
break;
case 22:
$data = Horde_Image_Translation::t("Food");
break;
case 23:
$data = Horde_Image_Translation::t("Baby");
break;
case 27:
$data = Horde_Image_Translation::t("High Sensitivity");
break;
case 29:
$data = Horde_Image_Translation::t("Underwater");
break;
case 33:
$data = Horde_Image_Translation::t("Pet");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '0020':
//Audio
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Yes");
break;
case 2:
$data = Horde_Image_Translation::t("No");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0023':
//White Balance Bias
$data = $data . ' EV';
break;
case '0024':
//Flash Bias
$data = $data;
break;
case '0028':
//Colour Effect
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Off");
break;
case 2:
$data = Horde_Image_Translation::t("Warm");
break;
case 3:
$data = Horde_Image_Translation::t("Cool");
break;
case 4:
$data = Horde_Image_Translation::t("Black and White");
break;
case 5:
$data = Horde_Image_Translation::t("Sepia");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002a':
//Burst Mode
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Off");
break;
case 1:
$data = Horde_Image_Translation::t("Low/High Quality");
break;
case 2:
$data = Horde_Image_Translation::t("Infinite");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002c':
//Contrast
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Standard");
break;
case 1:
$data = Horde_Image_Translation::t("Low");
break;
case 2:
$data = Horde_Image_Translation::t("High");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002d':
//Noise Reduction
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Standard");
break;
case 1:
$data = Horde_Image_Translation::t("Low");
break;
case 2:
$data = Horde_Image_Translation::t("High");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002e':
//Self Timer
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Off");
break;
case 2:
$data = Horde_Image_Translation::t("10s");
break;
case 3:
$data = Horde_Image_Translation::t("2s");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0030':
//Rotation
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Horizontal (normal)");
break;
case 6:
$data = Horde_Image_Translation::t("Rotate 90 CW");
break;
case 8:
$data = Horde_Image_Translation::t("Rotate 270 CW");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0032':
//Color Mode
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Normal");
break;
case 1:
$data = Horde_Image_Translation::t("Natural");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0036':
//Travel Day
$data = $data;
break;
}
break;
case 'UNDEFINED':
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
* Panasonic Special data section
*/
public function parse($block, &$result)
{
$intel = true;
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
$num = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Parser/Sanyo.php 0000664 0001750 0001750 00000014612 13160240461 020552 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Parser_Sanyo extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0200': $tag = 'SpecialMode'; break;
case '0201': $tag = 'Quality'; break;
case '0202': $tag = 'Macro'; break;
case '0203': $tag = 'Unknown'; break;
case '0204': $tag = 'DigiZoom'; break;
case '0f00': $tag = 'DataDump'; break;
default: $tag = 'unknown:' . $tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0200':
//SpecialMode
$data = $data == 0 ? Horde_Image_Translation::t("Normal") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
case '0201':
//Quality
$data = $data == 2 ? Horde_Image_Translation::t("High") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
case '0202':
//Macro
$data = $data == 0 ? Horde_Image_Translation::t("Normal") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
}
return $data;
}
/**
*
* @param $block
* @param $result
* @param $seek
* @param $globalOffset
* @return unknown_type
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$intel = $result['Endien']=='Intel';
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
if ($seek->seek($globalOffset + hexdec($value))) {
$data = $seek->substring(0, $bytesofdata);
} else {
$result['Errors'] = $result['Errors']++;
}
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Base.php 0000664 0001750 0001750 00000012553 13160240461 017101 0 ustar jan jan
* @category Horde
* @package Image
*/
/**
* Base class for Horde_Image_Exif drivers.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
abstract class Horde_Image_Exif_Base
{
/**
* Instance parameters.
*
* @var array
*/
protected $_params;
/**
* Optional Logger
*/
protected $_logger;
/**
*
* @param array $params Parameter array:
* - logger: Horde_Log_Logger Logger instance.
*/
public function __construct($params = array())
{
if (!empty($params['logger'])) {
$this->_logger = $params['logger'];
unset($params['logger']);
}
$this->_params = $params;
}
/**
* Process the EXIF data.
*
* @param array $exif Array of EXIF data.
*
* @return array An array of processed EXIF data.
*/
protected function _processData($exif)
{
if (!$exif) {
return array();
}
$results = array();
$fields = Horde_Image_Exif::getFields($this);
foreach ($fields as $field => $data) {
$value = isset($exif[$field]) ? $exif[$field] : '';
// Don't store empty fields.
if ($value === '') {
continue;
}
/* Special handling of GPS data */
if ($data['type'] == 'gps') {
$value = $this->_parseGPSData($exif[$field]);
if (!empty($exif[$field . 'Ref']) &&
in_array($exif[$field . 'Ref'], array('S', 'South', 'W', 'West'))) {
$value = - abs($value);
}
}
/* Date fields are converted to a timestamp.*/
if ($data['type'] == 'date') {
@list($ymd, $hms) = explode(' ', $value, 2);
@list($year, $month, $day) = explode(':', $ymd, 3);
if (!empty($hms) && !empty($year) && !empty($month) && !empty($day)) {
$time = "$month/$day/$year $hms";
$value = strtotime($time);
}
}
if ($data['type'] == 'array' || is_array($value)) {
if (is_array($value)) {
$value = implode(',', $value);
}
}
$results[$field] = $value;
}
return $results;
}
/**
* Parse the Longitude and Latitude values into a standardized format
* regardless of the source format.
*
* @param mixed $data An array containing degrees, minutes, seconds
* in index 0, 1, 2 respectifully.
*
* @return double The location data in a decimal format.
*/
protected function _parseGPSData($data)
{
// According to EXIF standard, GPS data can be in the form of
// dd/1 mm/1 ss/1 or as a decimal reprentation.
if (!is_array($data)) {
// Assume a scalar is a decimal representation. Cast it to a float
// which will get rid of any stray ordinal indicators. (N, S,
// etc...)
return (double)$data;
}
if ($data[0] == 0) {
return 0;
}
if (strpos($data[0], '/') !== false) {
$degrees = explode('/', $data[0]);
if (count($degrees) > 1) {
$degrees = $degrees[0] / $degrees[1];
} else {
$degrees = $degrees[0];
}
} else {
$degrees = $data[0];
}
if (strpos($data[1], '/') !== false) {
$min = explode('/', $data[1]);
if (count($min) > 1) {
$min = $min[0] / $min[1];
} else {
$min = $min[0];
}
} else {
$min = $data[1];
}
if (strpos($data[2], '/') !== false) {
$sec = explode('/', $data[2]);
if (count($sec) > 1) {
$sec = $sec[0] / $sec[1];
} else {
$sec = $sec[0];
}
} else {
$sec = $data[2];
}
return self::_degToDecimal($degrees, $min, $sec);
}
/**
* Convert degrees representation to decimal representation.
*
* @param double $degrees The degrees latitude or longitude.
* @param double $minutes The minutes latitude or longitude.
* @param double $seconds the seconds latitude or longitude.
*
* @return double The decimal representation of the latitude or longitute.
*/
protected function _degToDecimal($degrees, $minutes, $seconds)
{
$degs = (double)($degrees + ($minutes / 60) + ($seconds / 3600));
return round($degs, 6);
}
protected function _logDebug($message)
{
if (!empty($this->_logger)) {
$this->_logger->debug($message);
}
}
protected function _logErr($message)
{
if (!empty($this->_logger)) {
$this->_logger->err($message);
}
}
abstract public function getData($image);
abstract public function supportedCategories();
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Bundled.php 0000664 0001750 0001750 00000102340 13160240461 017576 0 ustar jan jan
* @author Michael J. Rubinsky
* @category Horde
* @package Image
*/
/**
* Class for dealing with Exif data using a bundled PHP library based on the
* Exifer code written by and Copyright 2003 Jake Olefsky
*
* @see http://www.offsky.com/software/exif/index.php
* @author Jake Olefsky
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Bundled extends Horde_Image_Exif_Base
{
/**
* @param mixed $image Filename -or- an open PHP stream (@since 2.2.0).
*/
public function getData($image)
{
$raw = $this->_readData($image);
$exif = array();
foreach ($raw as $key => $value) {
if ($key == 'IFD0' || $key == 'SubIFD') {
foreach ($value as $subkey => $subvalue) {
$exif[$subkey] = $subvalue;
}
} else {
$exif[$key] = $value;
}
}
// Not really an EXIF property, but an attribute nonetheless...
// PHP's exif functions return it, so add it here to be consistent.
if (is_resource($image)) {
fseek($image, 0, SEEK_END);
$exif['FileSize'] = ftell($image);
} else {
$exif['FileSize'] = @filesize($image);
}
return $this->_processData($exif);
}
/**
*
* @param mixed $path Filename -or- an open PHP stream.
*
* @return array
*/
protected function _readData($path)
{
if (is_resource($path)) {
$in = new Horde_Stream_Existing(array('stream' => $path));
$in->rewind();
} else {
$in = new Horde_Stream_Existing(array('stream' => @fopen($path, 'rb')));
}
$globalOffset = 0;
$result = array('Errors' => 0);
// if the path was invalid, this error will catch it
if (!$in) {
$result['Errors'] = 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("The file could not be opened.");
return $result;
}
// First 2 bytes of JPEG are 0xFFD8
$data = bin2hex($in->substring(0, 2));
if ($data == 'ffd8') {
$result['ValidJpeg'] = 1;
} else {
$result['ValidJpeg'] = 0;
if (!is_resource($path)) {
$in->close();
}
return $result;
}
$result['ValidIPTCData'] = 0;
$result['ValidJFIFData'] = 0;
$result['ValidEXIFData'] = 0;
$result['ValidAPP2Data'] = 0;
$result['ValidCOMData'] = 0;
// Next 2 bytes are marker tag (0xFFE#)
$data = bin2hex($in->substring(0, 2));
$size = bin2hex($in->substring(0, 2));
// Loop through markers till you get to FFE1 (Exif marker)
while(!$in->eof() && $data != 'ffe1' && $data != 'ffc0' && $data != 'ffd9') {
switch ($data) {
case 'ffe0':
// JFIF Marker
$result['ValidJFIFData'] = 1;
$result['JFIF']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = $in->substring(0, hexdec($size) - 2);
$result['JFIF']['Data'] = $data;
}
$result['JFIF']['Identifier'] = substr($data, 0, 5);
$result['JFIF']['ExtensionCode'] = bin2hex(substr($data, 6, 1));
$globalOffset += hexdec($size) + 2;
break;
case 'ffed':
// IPTC Marker
$result['ValidIPTCData'] = 1;
$result['IPTC']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = $in->substring(0, hexdec($size) - 2);
$result['IPTC']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'ffe2':
// EXIF extension Marker
$result['ValidAPP2Data'] = 1;
$result['APP2']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = $in->substring(0, hexdec($size) - 2);
$result['APP2']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'fffe':
// COM extension Marker
$result['ValidCOMData'] = 1;
$result['COM']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = $in->substring(0, hexdec($size) - 2);
$result['COM']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'ffe1':
$result['ValidEXIFData'] = 1;
break;
}
$data = bin2hex($in->substring(0, 2));
$size = bin2hex($in->substring(0, 2));
}
if ($data != 'ffe1') {
if (!is_resource($path)) {
$in->close();
}
return $result;
}
$result['ValidEXIFData'] = 1;
// Size of APP1
$result['APP1Size'] = hexdec($size);
// Start of APP1 block starts with 'Exif' header (6 bytes)
$header = $in->substring(0, 6);
// Then theres a TIFF header with 2 bytes of endieness (II or MM)
$header = $in->substring(0, 2);
switch ($header) {
case 'II':
$intel = 1;
$result['Endien'] = 'Intel';
break;
case 'MM':
$intel = 0;
$result['Endien'] = 'Motorola';
break;
default:
// not sure what the default should be, but this seems reasonable
$intel = 1;
$result['Endien'] = 'Unknown';
break;
}
// 2 bytes of 0x002a
if (bin2hex($in->substring(0, 2)) != '002a') {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = 'Unexpected value.';
return $result;
}
// Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes
// of TIFF header)
$offset = bin2hex($in->substring(0, 4));
if ($intel == 1) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
// Check for extremely large values here
if (hexdec($offset) > 100000) {
$result['ValidEXIFData'] = 0;
if (!is_resource($path)) {
$in->close();
}
return $result;
}
if (hexdec($offset) > 8) {
$unknown = $in->substring(0, hexdec($offset) - 8);
}
// add 12 to the offset to account for TIFF header
$globalOffset += 12;
//===========================================================
// Start of IFD0
$num = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['IFD0NumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $intel, 'IFD0', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = 'Illegal size for IFD0';
}
// store offset to IFD1
$offset = bin2hex($in->substring(0, 4));
if ($intel == 1) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
$result['IFD1Offset'] = hexdec($offset);
// Check for SubIFD
if (!isset($result['IFD0']['ExifOffset']) ||
$result['IFD0']['ExifOffset'] == 0) {
if (!is_resource($path)) {
$in->close();
}
return $result;
}
// seek to SubIFD (Value of ExifOffset tag) above.
$ExifOffset = $result['IFD0']['ExifOffset'];
if (!$in->seek($globalOffset + $ExifOffset, false)) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find SubIFD");
}
//===========================================================
// Start of SubIFD
$num = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['SubIFDNumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $intel, 'SubIFD', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for SubIFD");
}
// Add the 35mm equivalent focal length:
// Now properly get this using the FocalLength35mmFilm tag
//$result['SubIFD']['FocalLength35mmEquiv'] = get35mmEquivFocalLength($result);
// Check for IFD1
if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) {
if (!is_resource($path)) {
$in->close();
}
return $result;
}
// seek to IFD1
if (!$in->seek($globalOffset + $result['IFD1Offset'], false)) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find IFD1");
}
//===========================================================
// Start of IFD1
$num = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['IFD1NumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $intel, 'IFD1', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for IFD1");
}
// include the thumbnail raw data...
if ($result['IFD1']['JpegIFOffset'] > 0 &&
$result['IFD1']['JpegIFByteCount'] > 0) {
$cpos = $in->pos();
if ($in->seek($globalOffset + $result['IFD1']['JpegIFOffset'], false)) {
$data = $in->substring(0, $result['IFD1']['JpegIFByteCount']);
} else {
$result['Errors'] = $result['Errors'] + 1;
}
$result['IFD1']['ThumbnailData'] = $data;
}
// Check for Interoperability IFD
if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) ||
$result['SubIFD']['ExifInteroperabilityOffset'] == 0) {
if (!is_resource($path)) {
$in->close();
}
return $result;
}
// Seek to InteroperabilityIFD
if (!$in->seek($globalOffset + $result['SubIFD']['ExifInteroperabilityOffset'], false)) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find InteroperabilityIFD");
}
//===========================================================
// Start of InteroperabilityIFD
$num = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['InteroperabilityIFDNumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $intel, 'InteroperabilityIFD', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for InteroperabilityIFD");
}
if (!is_resource($path)) {
$in->close();
}
return $result;
}
/**
*
* @param $result
* @param $in
* @param $intel
* @param $ifd_name
* @param $globalOffset
* @return unknown_type
*/
protected function _readEntry(&$result, $in, $intel, $ifd_name, $globalOffset)
{
// Still ok to read?
if ($in->eof()) {
$result['Errors'] = $result['Errors'] + 1;
return;
}
// 2 byte tag
$tag = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
// 2 byte datatype
$type = bin2hex($in->substring(0, 2));
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
list($type, $size) = $this->_lookupType($type);
// 4 byte number of elements
$count = bin2hex($in->substring(0, 4));
if ($intel == 1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
// 4 byte value or pointer to value if larger than 4 bytes
$value = $in->substring(0, 4);
// if datatype is 4 bytes or less, its the value
if ($bytesofdata <= 4) {
$data = $value;
} elseif ($bytesofdata < 100000) {
// otherwise its a pointer to the value, so lets go get it
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
// offsets are from TIFF header which is 12 bytes from the start of file
// @todo can we just use
// $in->getString($globalOffset + hexdec($value), $globalOffset + hexdec($value) + $bytesofdata);
// ?? Not clear from Horde_Stream docs about expected behavior if
// eof is reached.
$cpos = $in->pos();
if ($in->seek($globalOffset + hexdec($value), false)) {
$data = $in->substring(0, $bytesofdata);
$in->seek($cpos, false);
} else {
$result['Errors'] = $result['Errors'] + 1;
}
} else {
// bytesofdata was too big, so the exif had an error
$result['Errors'] = $result['Errors'] + 1;
return;
}
// if its a maker tag, we need to parse this specially
switch ($tag_name) {
case 'MakerNote':
$make = Horde_String::lower($result['IFD0']['Make']);
$parser = null;
if (strpos($make, 'nikon') !== false) {
$parser = new Horde_Image_Exif_Parser_Nikon();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'olympus') !== false) {
$parser = new Horde_Image_Exif_Parser_Olympus();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'canon') !== false) {
$parser = new Horde_Image_Exif_Parser_Canon();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'fujifilm') !== false) {
$parser = new Horde_Image_Exif_Parser_Fujifilm();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'sanyo') !== false) {
$parser = new Horde_Image_Exif_Parser_Sanyo();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'panasonic') !== false) {
$parser = new Horde_Image_Exif_Parser_Panasonic();
$result[$ifd_name]['KnownMaker'] = 1;
} else {
$result[$ifd_name]['KnownMaker'] = 0;
}
if ($parser) {
$cpos = $in->pos();
$parser->parse($data, $result, $in, $globalOffset);
$in->seek($cpos, false);
}
break;
case 'GPSInfoOffset':
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result[$ifd_name]['GPSInfo'] = $formated_data;
$parser = new Horde_Image_Exif_Parser_Gps();
$cpos = $in->pos();
$parser->parse($data, $result, $formated_data, $in, $globalOffset);
$in->seek($cpos, false);
break;
default:
// Format the data depending on the type and tag
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result[$ifd_name][$tag_name] = $formated_data;
}
}
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag)
{
// used by IFD0 'Camera Tags'
// text string up to 999 bytes long
case '000b': $tag = 'ACDComment'; break;
// integer -2147483648 to 2147483647
case '00fe': $tag = 'ImageType'; break;
// ?? Please send sample image with this tag
case '0106': $tag = 'PhotometricInterpret'; break;
// text string up to 999 bytes long
case '010e': $tag = 'ImageDescription'; break;
// text string up to 999 bytes long
case '010f': $tag = 'Make'; break;
// text string up to 999 bytes long
case '0110': $tag = 'Model'; break;
// integer values 1-9
case '0112': $tag = 'Orientation'; break;
// integer 0-65535
case '0115': $tag = 'SamplePerPixel'; break;
// positive rational number
case '011a': $tag = 'xResolution'; break;
// positive rational number
case '011b': $tag = 'yResolution'; break;
// integer values 1-2
case '011c': $tag = 'PlanarConfig'; break;
// integer values 1-3
case '0128': $tag = 'ResolutionUnit'; break;
// text string up to 999 bytes long
case '0131': $tag = 'Software'; break;
// YYYY:MM:DD HH:MM:SS
case '0132': $tag = 'DateTime'; break;
// text string up to 999 bytes long
case '013b': $tag = 'Artist'; break;
// text string
case '013c': $tag = 'HostComputer'; break;
// two positive rational numbers
case '013e': $tag = 'WhitePoint'; break;
// six positive rational numbers
case '013f': $tag = 'PrimaryChromaticities'; break;
// three positive rational numbers
case '0211': $tag = 'YCbCrCoefficients'; break;
// integer values 1-2
case '0213': $tag = 'YCbCrPositioning'; break;
// six positive rational numbers
case '0214': $tag = 'ReferenceBlackWhite'; break;
// text string up to 999 bytes long
case '8298': $tag = 'Copyright'; break;
// ??
case '8649': $tag = 'PhotoshopSettings'; break;
case '8825': $tag = 'GPSInfoOffset'; break;
// positive integer
case '8769': $tag = 'ExifOffset'; break;
// used by Exif SubIFD 'Image Tags'
// seconds or fraction of seconds 1/x
case '829a': $tag = 'ExposureTime'; break;
// positive rational number
case '829d': $tag = 'FNumber'; break;
// integer value 1-9
case '8822': $tag = 'ExposureProgram'; break;
// ??
case '8824': $tag = 'SpectralSensitivity'; break;
// integer 0-65535
case '8827': $tag = 'ISOSpeedRatings'; break;
// ??
case '9000': $tag = 'ExifVersion'; break;
// YYYY:MM:DD HH:MM:SS
case '9003': $tag = 'DateTimeOriginal'; break;
// YYYY:MM:DD HH:MM:SS
case '9004': $tag = 'DateTimedigitized'; break;
// ??
case '9101': $tag = 'ComponentsConfiguration'; break;
// positive rational number
case '9102': $tag = 'CompressedBitsPerPixel'; break;
// seconds or fraction of seconds 1/x
case '9201': $tag = 'ShutterSpeedValue'; break;
// positive rational number
case '9202': $tag = 'ApertureValue'; break;
// positive rational number
case '9203': $tag = 'BrightnessValue'; break;
// positive rational number (EV)
case '9204': $tag = 'ExposureBiasValue'; break;
// positive rational number
case '9205': $tag = 'MaxApertureValue'; break;
// positive rational number (meters)
case '9206': $tag = 'SubjectDistance'; break;
// integer 1-6 and 255
case '9207': $tag = 'MeteringMode'; break;
// integer 1-255
case '9208': $tag = 'LightSource'; break;
// integer 1-255
case '9209': $tag = 'Flash'; break;
// positive rational number (mm)
case '920a': $tag = 'FocalLength'; break;
// text string up to 999 bytes long
case '9213': $tag = 'ImageHistory'; break;
// a bunch of data
case '927c': $tag = 'MakerNote'; break;
// text string
case '9286': $tag = 'UserComment'; break;
// text string up to 999 bytes long
case '9290': $tag = 'SubsecTime'; break;
// text string up to 999 bytes long
case '9291': $tag = 'SubsecTimeOriginal'; break;
// text string up to 999 bytes long
case '9292': $tag = 'SubsecTimeDigitized'; break;
// ??
case 'a000': $tag = 'FlashPixVersion'; break;
// values 1 or 65535
case 'a001': $tag = 'ColorSpace'; break;
// ingeter 1-65535
case 'a002': $tag = 'ExifImageWidth'; break;
// ingeter 1-65535
case 'a003': $tag = 'ExifImageHeight'; break;
// text string 12 bytes long
case 'a004': $tag = 'RelatedSoundFile'; break;
// positive integer
case 'a005': $tag = 'ExifInteroperabilityOffset'; break;
// ??
case 'a20c': $tag = 'SpacialFreqResponse'; break;
// positive rational number
case 'a20b': $tag = 'FlashEnergy'; break;
// positive rational number
case 'a20e': $tag = 'FocalPlaneXResolution'; break;
// positive rational number
case 'a20f': $tag = 'FocalPlaneYResolution'; break;
// values 1-3
case 'a210': $tag = 'FocalPlaneResolutionUnit'; break;
// two integers 0-65535
case 'a214': $tag = 'SubjectLocation'; break;
// positive rational number
case 'a215': $tag = 'ExposureIndex'; break;
// values 1-8
case 'a217': $tag = 'SensingMethod'; break;
// integer
case 'a300': $tag = 'FileSource'; break;
// integer
case 'a301': $tag = 'SceneType'; break;
// undefined data type
case 'a302': $tag = 'CFAPattern'; break;
// values 0 or 1
case 'a401': $tag = 'CustomerRender'; break;
// values 0-2
case 'a402': $tag = 'ExposureMode'; break;
// values 0 or 1
case 'a403': $tag = 'WhiteBalance'; break;
// positive rational number
case 'a404': $tag = 'DigitalZoomRatio'; break;
case 'a405': $tag = 'FocalLengthIn35mmFilm';break;
// values 0-3
case 'a406': $tag = 'SceneCaptureMode'; break;
// values 0-4
case 'a407': $tag = 'GainControl'; break;
// values 0-2
case 'a408': $tag = 'Contrast'; break;
// values 0-2
case 'a409': $tag = 'Saturation'; break;
// values 0-2
case 'a40a': $tag = 'Sharpness'; break;
// used by Interoperability IFD
// text string 3 bytes long
case '0001': $tag = 'InteroperabilityIndex'; break;
// datatype undefined
case '0002': $tag = 'InteroperabilityVersion'; break;
// text string up to 999 bytes long
case '1000': $tag = 'RelatedImageFileFormat'; break;
// integer in range 0-65535
case '1001': $tag = 'RelatedImageWidth'; break;
// integer in range 0-65535
case '1002': $tag = 'RelatedImageLength'; break;
// used by IFD1 'Thumbnail'
// integer in range 0-65535
case '0100': $tag = 'ImageWidth'; break;
// integer in range 0-65535
case '0101': $tag = 'ImageLength'; break;
// integers in range 0-65535
case '0102': $tag = 'BitsPerSample'; break;
// values 1 or 6
case '0103': $tag = 'Compression'; break;
// values 0-4
case '0106': $tag = 'PhotometricInterpretation'; break;
// text string up to 999 bytes long
case '010e': $tag = 'ThumbnailDescription'; break;
// text string up to 999 bytes long
case '010f': $tag = 'ThumbnailMake'; break;
// text string up to 999 bytes long
case '0110': $tag = 'ThumbnailModel'; break;
// ??
case '0111': $tag = 'StripOffsets'; break;
// integer 1-9
case '0112': $tag = 'ThumbnailOrientation'; break;
// ??
case '0115': $tag = 'SamplesPerPixel'; break;
// ??
case '0116': $tag = 'RowsPerStrip'; break;
// ??
case '0117': $tag = 'StripByteCounts'; break;
// positive rational number
case '011a': $tag = 'ThumbnailXResolution'; break;
// positive rational number
case '011b': $tag = 'ThumbnailYResolution'; break;
// values 1 or 2
case '011c': $tag = 'PlanarConfiguration'; break;
// values 1-3
case '0128': $tag = 'ThumbnailResolutionUnit'; break;
case '0201': $tag = 'JpegIFOffset'; break;
case '0202': $tag = 'JpegIFByteCount'; break;
case '0212': $tag = 'YCbCrSubSampling'; break;
// misc
case '00ff': $tag = 'SubfileType'; break;
case '012d': $tag = 'TransferFunction'; break;
case '013d': $tag = 'Predictor'; break;
case '0142': $tag = 'TileWidth'; break;
case '0143': $tag = 'TileLength'; break;
case '0144': $tag = 'TileOffsets'; break;
case '0145': $tag = 'TileByteCounts'; break;
case '014a': $tag = 'SubIFDs'; break;
case '015b': $tag = 'JPEGTables'; break;
case '828d': $tag = 'CFARepeatPatternDim'; break;
case '828e': $tag = 'CFAPattern'; break;
case '828f': $tag = 'BatteryLevel'; break;
case '83bb': $tag = 'IPTC/NAA'; break;
case '8773': $tag = 'InterColorProfile'; break;
case '8828': $tag = 'OECF'; break;
case '8829': $tag = 'Interlace'; break;
case '882a': $tag = 'TimeZoneOffset'; break;
case '882b': $tag = 'SelfTimerMode'; break;
case '920b': $tag = 'FlashEnergy'; break;
case '920c': $tag = 'SpatialFrequencyResponse'; break;
case '920d': $tag = 'Noise'; break;
case '9211': $tag = 'ImageNumber'; break;
case '9212': $tag = 'SecurityClassification'; break;
case '9214': $tag = 'SubjectLocation'; break;
case '9215': $tag = 'ExposureIndex'; break;
case '9216': $tag = 'TIFF/EPStandardID'; break;
case 'a20b': $tag = 'FlashEnergy'; break;
default: $tag = 'unknown:'.$tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
// Search for a null byte and stop there.
if (($pos = strpos($data, chr(0))) !== false) {
$data = substr($data, 0, $pos);
}
// Format certain kinds of strings nicely (Camera make etc.)
if ($tag == '010f') {
$data = Horde_String::ucwords(Horde_String::lower(trim($data)));
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 1) {
// intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
// motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
// intel stores them bottom-top
$bottom = hexdec(substr($data, 0, 8));
} else {
// motorola stores them top-bottom
$bottom = hexdec(substr($data, 8, 8));
}
if ($type == 'SRATIONAL' && $top > 2147483647) {
// this makes the number signed instead of unsigned
$top = $top - 4294967296;
}
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
// Exposure Time
if ($tag == '829a') {
if ($bottom != 0) {
$data = $top . '/' . $bottom;
} else {
$data = 0;
}
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) {
$data = substr($data, 0, 4);
}
$data = hexdec($data);
if ($type == 'SSHORT' && $data > 32767) {
// this makes the number signed instead of unsigned
$data = $data - 65536;
}
if ($type == 'SLONG' && $data > 2147483647) {
// this makes the number signed instead of unsigned
$data = $data - 4294967296;
}
break;
case 'UNDEFINED':
// ExifVersion,FlashPixVersion,InteroperabilityVersion
if ($tag == '9000' || $tag == 'a000' || $tag == '0002') {
$data = sprintf(Horde_Image_Translation::t("version %d"), $data / 100);
}
break;
default:
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
* Look up the data type.
*
* @param string The string representation of the hex type code.
*
* @return array An array containing the string type name in the 0 index
* and the integer size in the 1 index.
*/
protected function _lookupType($type)
{
switch ($type) {
case '0001': $type = 'UBYTE'; $size = 1; break;
case '0002': $type = 'ASCII'; $size = 1; break;
case '0003': $type = 'USHORT'; $size = 2; break;
case '0004': $type = 'ULONG'; $size = 4; break;
case '0005': $type = 'URATIONAL'; $size = 8; break;
case '0006': $type = 'SBYTE'; $size = 1; break;
case '0007': $type = 'UNDEFINED'; $size = 1; break;
case '0008': $type = 'SSHORT'; $size = 2; break;
case '0009': $type = 'SLONG'; $size = 4; break;
case '000a': $type = 'SRATIONAL'; $size = 8; break;
case '000b': $type = 'FLOAT'; $size = 4; break;
case '000c': $type = 'DOUBLE'; $size = 8; break;
default: $type = 'error:'.$type; $size = 0; break;
}
return array($type, $size);
}
public function supportedCategories()
{
return array('EXIF');
}
}
Horde_Image-2.5.2/lib/Horde/Image/Exif/Exiftool.php 0000664 0001750 0001750 00000005520 13160240461 020014 0 ustar jan jan
* @category Horde
* @package Image
*/
/**
* Exiftool driver for reading/writing image meta data
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Exiftool extends Horde_Image_Exif_Base
{
/**
* Path to exiftool binary
*
* @var string
*/
protected $_exiftool;
public function __construct($params)
{
parent::__construct($params);
if (empty($this->_params['exiftool'])) {
throw new InvalidArgumentException('Missing required exiftool path');
}
$this->_exiftool = $this->_params['exiftool'];
}
/**
* Get the image's EXIF data.
*
* @param string $image The path to an image.
*
* @return array The exif data.
*/
public function getData($image)
{
// Request the full stream of meta data in JSON format.
// -j option outputs in JSON, appending '#' to the -TAG prevents
// screen formatting.
$categories = Horde_Image_Exif::getCategories();
$tags = '';
foreach (array('EXIF', 'IPTC', 'XMP') as $category) {
foreach ($categories[$category] as $field => $value) {
$tags .= ' -' . $field . '#';
}
}
foreach ($categories['COMPOSITE'] as $field => $value) {
$tags .= ' -' . $field;
}
$command = '-j' . $tags . ' ' . $image;
$this->_logDebug('Command executed by Exiftool: ' . $command);
$results = json_decode($this->_execute($command));
$this->_logDebug('Results of Exiftool command: ' . print_r($results, true));
if (is_array($results)) {
return $this->_processData((array)array_pop($results));
}
throw new Horde_Image_Exception('Unknown error running exiftool command');
}
public function supportedCategories()
{
return array('EXIF', 'IPTC', 'XMP', 'COMPOSITE');
}
/**
* Executes a exiftool command.
*
* @param string $command The command to run
*
* @return mixed The result of the command.
*/
protected function _execute($command)
{
$output = array();
$retval = null;
exec($this->_exiftool . ' ' . escapeshellcmd($command), $output, $retval);
if ($retval) {
$this->_logErr(sprintf("Error running command: %s", $command . "\n" . implode("\n", $output)));
}
if (is_array($output)) {
$output = implode('', $output);
}
return $output;
}
} Horde_Image-2.5.2/lib/Horde/Image/Exif/Php.php 0000664 0001750 0001750 00000001431 13160240461 016747 0 ustar jan jan
* @category Horde
* @package Image
*/
/**
* Exif driver for Horde_Image utilizing PHP's compiled-in exif functions
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @package Image
*/
class Horde_Image_Exif_Php extends Horde_Image_Exif_Base
{
public function getData($image)
{
return $this->_processData(@exif_read_data($image, 0, false));
}
public function supportedCategories()
{
return array('EXIF');
}
}
Horde_Image-2.5.2/lib/Horde/Image/Base.php 0000664 0001750 0001750 00000035332 13160240461 016206 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class defines the Horde_Image API, and also provides some utility
* functions, such as generating highlights of a color.
*
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
abstract class Horde_Image_Base extends EmptyIterator
{
/**
* Background color.
*
* @var string
*/
protected $_background = 'white';
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array();
/**
* The current image data.
*
* @var Horde_Stream
*/
protected $_data;
/**
* Logger.
*/
protected $_logger;
/**
* The current width of the image data.
*
* @var integer
*/
protected $_width = 0;
/**
* The current height of the image data.
*
* @var integer
*/
protected $_height = 0;
/**
* A directory for temporary files.
*
* @var string
*/
protected $_tmpdir;
/**
* Array containing available Effects
*
* @var array
*/
protected $_loadedEffects = array();
/**
* What kind of images should ImageMagick generate? Defaults to 'png'.
*
* @var string
*/
protected $_type = 'png';
/**
* Cache the context
*
* @param array
*/
protected $_context;
/**
* Constructor.
*
* @param array $params The image object parameters. Values include:
* - background: (string) The background color.
* DEFAULT: white.
* - data: (string) The image binary data.
* - height: (integer) The desired image height.
* - type: (string) The output image type (png, jpeg
* etc.). DEFAULT: png.
* - width: (integer) The desired image width.
* @param array $context The object context - configuration, injected
* objects:
* - logger: (Horde_Log_Logger) A logger.
* - tmpdir: [REQUIRED] (string) Temporary directory.
*
* @throws InvalidArgumentException
*/
protected function __construct($params, $context = array())
{
$this->_params = $params;
$this->_context = $context;
if (empty($context['tmpdir'])) {
throw new InvalidArgumentException(
'A path to a temporary directory is required.'
);
}
$this->_tmpdir = $context['tmpdir'];
$this->_logger = !empty($context['logger'])
? $context['logger']
: new Horde_Support_Stub();
if (isset($params['width'])) {
$this->_width = (integer)$params['width'];
}
if (isset($params['height'])) {
$this->_height = (integer)$params['height'];
}
if (!empty($params['type'])) {
// We only want the extension, not the full mimetype.
if (strpos($params['type'], 'image/') !== false) {
$params['type'] = substr($params['type'], 6);
}
$this->_type = $params['type'];
}
if (!empty($params['background'])) {
$this->_background = $params['background'];
}
}
/**
* Catch-all method so that we don't error out when calling an unsupported
* manipulation method.
*/
public function __call($method, $args)
{
}
/**
* Returns the capabilities.
*
* @return array A list of backend capabilities.
*/
public function getCapabilities()
{
return $this->_capabilities;
}
/**
* Checks the existence of a particular capability.
*
* @param string $capability The capability to check for.
*
* @return boolean True if the backend has this capability.
*/
public function hasCapability($capability)
{
return in_array($capability, $this->_capabilities);
}
/**
* Sends HTTP headers for the image.
*/
public function headers()
{
header('Content-type: ' . $this->getContentType());
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'image/' . $this->_type;
}
/**
* Returns the image type.
*
* @return string The type of this image (png, jpg, etc.).
*/
public function getType()
{
return $this->_type;
}
/**
* Sets the output image type.
*
* @param string $type An image type (png, jpg, etc.)
*
* @return string The previous image type.
*/
public function setType($type)
{
// We only want the extension, not the full mimetype.
if (strpos($type, 'image/') !== false) {
$type = substr($type, 6);
}
$old = $this->_type;
$this->_type = $type;
return $old;
}
/**
* Draws a shaped point at the specified (x,y) point.
*
* Useful for scatter diagrams, debug points, etc. Draws squares, circles,
* diamonds, and triangles.
*
* @param integer $x The x coordinate of the point to brush.
* @param integer $y The y coordinate of the point to brush.
* @param string $color The color to brush the point with.
* @param string $shape What brush to use? Defaults to a square.
*/
public function brush($x, $y, $color = 'black', $shape = 'square')
{
switch ($shape) {
case 'triangle':
$verts[0] = array('x' => $x + 3, 'y' => $y + 3);
$verts[1] = array('x' => $x, 'y' => $y - 3);
$verts[2] = array('x' => $x - 3, 'y' => $y + 3);
$this->polygon($verts, $color, $color);
break;
case 'circle':
$this->circle($x, $y, 3, $color, $color);
break;
case 'diamond':
$verts[0] = array('x' => $x - 3, 'y' => $y);
$verts[1] = array('x' => $x, 'y' => $y + 3);
$verts[2] = array('x' => $x + 3, 'y' => $y);
$verts[3] = array('x' => $x, 'y' => $y - 3);
$this->polygon($verts, $color, $color);
break;
case 'square':
default:
$this->rectangle($x - 2, $y - 2, 4, 4, $color, $color);
break;
}
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
if ($this->_data) {
$this->_data->close();
}
$this->_data = null;
$this->_width = null;
$this->_height = null;
$this->_background = 'white';
}
/**
* Returns the height and width of the current image data.
*
* @return array An hash with 'width' containing the width,
* 'height' containing the height of the image.
*/
public function getDimensions()
{
// Check if we know it already
if ($this->_width == 0 && $this->_height == 0) {
$tmp = $this->toFile();
$details = @getimagesize($tmp);
list($this->_width, $this->_height) = $details;
unlink($tmp);
}
return array('width' => $this->_width, 'height' => $this->_height);
}
/**
* Loads the image data from a string.
*
* @param mixed $image_data The data to use for the image as a string,
* Horde_Stream, or stream resource.
*/
public function loadString($image_data)
{
$this->reset();
$this->_data = new Horde_Stream_Temp();
$this->_data->add($image_data, true);
}
/**
* Loads the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from.
*
* @throws Horde_Image_Exception
*/
public function loadFile($filename)
{
$this->reset();
if (!file_exists($filename)) {
throw new Horde_Image_Exception(
sprintf("The image file, %s, does not exist.", $filename)
);
}
$fp = fopen($filename, 'r');
$this->_data = new Horde_Stream_Temp();
$this->_data->add($fp, true);
if (!$this->_data->length()) {
throw new Horde_Image_Exception(
sprintf("Could not load the image file %s", $filename)
);
}
}
/**
* Saves image data to file.
*
* If $data is false-ish, saves current image data after performing pending
* operations on the data. If $data contains raw image data, saves that
* data to file without regard for the current image data.
*
* @param mixed String or stream resource of binary image data.
*
* @return string Path to temporary file.
* @throws Horde_Image_Exception
*/
public function toFile($data = null)
{
if (empty($data)) {
if ($data = $this->raw(false, array('stream' => true))) {
return $this->toFile($data);
}
throw new Horde_Image_Exception('Unable to copy to file.');
}
$tmp = Horde_Util::getTempFile('img', false, $this->_tmpdir);
$fp = fopen($tmp, 'wb');
if (is_resource($data)) {
rewind($data);
while (!feof($data)) {
fwrite($fp, fread($data, 8192));
}
} elseif ($data) {
fwrite($fp, $data);
}
fclose($fp);
return $tmp;
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$data = $this->raw(true, array('stream' => true));
$output = fopen('php://output', 'w');
while (!feof($data)) {
fwrite($output, fread($data, 8192));
}
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert If true, the image data will be returned in the
* target format, independently from any image
* operations.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return string The raw image data.
*/
public function raw($convert = false, $options = array())
{
if (empty($options['stream'])) {
return $this->_data->__toString();
}
return $this->_data->stream;
}
/**
* Attempts to apply requested effect to this image.
*
* @param string $type The type of effect to apply.
* @param array $params Any parameters for the effect.
*/
public function addEffect($type, $params)
{
$class = str_replace('Horde_Image_', '', get_class($this));
$params['logger'] = $this->_logger;
$effect = Horde_Image_Effect::factory($type, $class, $params);
$effect->setImageObject($this);
$effect->apply();
}
/**
* Returns a list of available effects for this driver.
*/
public function getLoadedEffects()
{
if (!count($this->_loadedEffects)) {
$class = str_replace('Horde_Image_', '', get_class($this));
$this->_loadedEffects = array();
// First, load the driver-agnostic Effects.
$path = __DIR__ . '/Effect/';
if (is_dir($path)) {
if ($handle = opendir($path)) {
while (($file = readdir($handle)) !== false) {
if (substr($file, -4, 4) == '.php') {
$this->_loadedEffects[] = substr(
$file, 0, strlen($file) - 4
);
}
}
}
}
// Driver specific effects.
$path = $path . $class;
if (is_dir($path)) {
if ($handle = opendir($path)) {
while (($file = readdir($handle)) !== false) {
if (substr($file, -4, 4) == '.php') {
$this->_loadedEffects[] = substr(
$file, 0, strlen($file) - 4
);
}
}
}
}
}
return $this->_loadedEffects;
}
/**
* Applies any effects in the effect queue.
*/
public function applyEffects()
{
$this->raw();
}
/**
* Returns the current temporary directory.
*
* @return string The current temporary directory.
*/
public function getTmpDir()
{
return $this->_tmpdir;
}
/**
* Utility function to zero out cached geometry information.
*
* Shouldn't really be called from client code, but is needed since effects
* may need to clear these.
*/
public function clearGeometry()
{
$this->_height = 0;
$this->_width = 0;
}
/**
* Logs a message at debug level.
*
* @param string $message The log message.
*/
protected function _logDebug($message)
{
$this->_logger->debug($message);
}
/**
* Logs a message at error level.
*
* @param string $message The log message.
*/
protected function _logErr($message)
{
$this->_logger->err($message);
}
/**
* Returns a specific image from the pages of images.
*
* @param integer $index The index to return.
*
* @return Horde_Image_Base The requested image
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
$class = get_class($this);
return new $class(array('data' => $this->raw()), $this->_context);
}
/**
* Returns the number of image pages available in the image object.
*
* @return integer The number of images.
*/
public function getImagePageCount()
{
return 1;
}
}
Horde_Image-2.5.2/lib/Horde/Image/Effect.php 0000664 0001750 0001750 00000006330 13160240461 016524 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* The Horde_Image_Effect parent class defines a general API for ways to apply
* effects to Horde_Image objects.
*
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Effect
{
/**
* Effect parameters.
*
* @var array
*/
protected $_params = array();
/**
* The bound Horde_Image object
*
* @var Horde_Image
*/
protected $_image = null;
/**
* Logger.
*/
protected $_logger;
/**
* Effect constructor.
*
* @param array $params Any parameters for the effect. Parameters are
* documented in each subclass.
*/
public function __construct($params = array())
{
$this->_logger = new Horde_Support_Stub();
foreach ($params as $key => $val) {
$this->_params[$key] = $val;
}
}
/**
* Bind this effect to a Horde_Image object.
*
* @param Horde_Image $image The Horde_Image object
*/
public function setImageObject($image)
{
$this->_image = $image;
}
/**
* Attaches a logger.
*
* @param Horde_Log_Logger $logger A logger.
*/
public function setLogger($logger)
{
$this->_logger = $logger;
}
/**
* Effect factory.
*
* @param string $type An effect name.
* @param string $driver An image driver name.
* @param array $params Any effect parameters.
*
* @return Horde_Image_Effect An effect instance.
*/
public static function factory($type, $driver, $params)
{
if (is_array($type)) {
list($app, $type) = $type;
}
// First check for a driver specific effect, if we can't find one,
// assume there is a vanilla effect object around.
$class = 'Horde_Image_Effect_' . $driver . '_' . $type;
$vclass = 'Horde_Image_Effect_' . $type;
if (class_exists($class)) {
$effect = new $class($params);
} elseif (class_exists($vclass)) {
$effect = new $vclass($params);
} else {
$message = sprintf(
'Horde_Image Effect "%s" for "%s" driver not found.',
$type,
$driver
);
if (!empty($params['logger'])) {
$params['logger']->err($message);
}
throw new Horde_Image_Exception($message);
}
if (!empty($params['logger'])) {
$effect->setLogger($params['logger']);
}
return $effect;
}
} Horde_Image-2.5.2/lib/Horde/Image/Exception.php 0000664 0001750 0001750 00000001244 13160240461 017265 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Exception class for Horde_Image
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2009-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Exception extends Horde_Exception_Wrapped {
}
Horde_Image-2.5.2/lib/Horde/Image/Exif.php 0000664 0001750 0001750 00000064413 13160240461 016231 0 ustar jan jan
* @author Michael J. Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* General class for fetching and parsing EXIF information from images.
*
* Works equally well with either the built in php exif functions (if PHP
* compiled with exif support), the Exiftool package (more complete but
* slower), or the bundled exif library.
*
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2003-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Exif
{
protected static $_titleFields = array(
'IPTC' => array('ObjectName'),
'XMP' => array('Title'),
'EXIF' => array(),
'COMPOSITE' => array()
);
protected static $_descriptionFields = array(
'IPTC' => array('Caption-Abstract'),
'XMP' => array('Description'),
'EXIF' => array('ImageDescription'),
'COMPOSITE' => array()
);
/**
* Factory method for instantiating a Horde_Image_Exif object.
*
* @param string $driver
* @param array $params
*
* @return Horde_Image_Exif
*/
public static function factory($driver = null, $params = array())
{
if (empty($driver) && function_exists('exif_read_data')) {
$driver = 'Php';
} elseif (empty($driver)) {
$driver = 'Bundled';
} else {
$driver = basename($driver);
}
$class = 'Horde_Image_Exif_' . $driver;
return new $class($params);
}
/**
* Converts from Intel to Motorola endien.
*
* Just reverses the bytes (assumes hex is passed in)
*
* @param string $intel
*
* @return string
*/
public static function intel2Moto($intel)
{
$len = strlen($intel);
$moto = '';
for ($i = 0; $i <= $len; $i += 2) {
$moto .= substr($intel, $len - $i, 2);
}
return $moto;
}
/**
* Obtains an array of supported meta data fields.
*
* @TODO: This should probably be extended by the subclass?
*
* @return array
*/
public static function getCategories()
{
return array(
'IPTC' => array(
'Keywords' => array('description' => Horde_Image_Translation::t("Image keywords"), 'type' => 'array'),
'ObjectName' => array('description' => Horde_Image_Translation::t("Image Title"), 'type' => 'text'),
'By-line' => array('description' => Horde_Image_Translation::t("By"), 'type' => 'text'),
'CopyrightNotice' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
'Caption-Abstract' => array('description' => Horde_Image_Translation::t("Caption"), 'type' => 'text'),
),
'XMP' => array(
'Creator' => array('description' => Horde_Image_Translation::t("Image Creator"), 'type' => 'text'),
'Rights' => array('description' => Horde_Image_Translation::t("Rights"), 'type' => 'text'),
'UsageTerms' => array('description' => Horde_Image_Translation::t("Usage Terms"), 'type' => 'text'),
'Title' => array('description' => Horde_Image_Translation::t("Title"), 'type' => 'text'),
'Description' => array('description' => Horde_Image_Translation::t("Description"), 'type' => 'text'),
),
'EXIF' => array(
'DateTime' => array('description' => Horde_Image_Translation::t("Date Photo Modified"), 'type' => 'date'),
'DateTimeOriginal' => array('description' => Horde_Image_Translation::t("Date Photo Taken"), 'type' => 'date'),
'DateTimeDigitized' => array('description' => Horde_Image_Translation::t("Date Photo Digitized"), 'type' => 'date'),
'GPSLatitude' => array('description' => Horde_Image_Translation::t("Latitude"), 'type' => 'gps'),
'GPSLongitude' => array('description' => Horde_Image_Translation::t("Longitude"), 'type' => 'gps'),
'Make' => array('description' => Horde_Image_Translation::t("Camera Make"), 'type' => 'text'),
'Model' => array('description' => Horde_Image_Translation::t("Camera Model"), 'type' => 'text'),
'Software' => array('description' => Horde_Image_Translation::t("Software Version"), 'type' => 'text'),
'ImageType' => array('description' => Horde_Image_Translation::t("Photo Type"), 'type' => 'text'),
'ImageDescription' => array('description' => Horde_Image_Translation::t("Photo Description"), 'type' => 'text'),
'FileSize' => array('description' => Horde_Image_Translation::t("File Size"), 'type' => 'number'),
'ExifImageWidth' => array('description' => Horde_Image_Translation::t("Width"), 'type' => 'number'),
'ExifImageLength' => array('description' => Horde_Image_Translation::t("Height"), 'type' => 'number'),
'XResolution' => array('description' => Horde_Image_Translation::t("X Resolution"), 'type' => 'number'),
'YResolution' => array('description' => Horde_Image_Translation::t("Y Resolution"), 'type' => 'number'),
'ResolutionUnit' => array('description' => Horde_Image_Translation::t("Resolution Unit"), 'type' => 'text'),
'ShutterSpeedValue' => array('description' => Horde_Image_Translation::t("Shutter Speed"), 'type' => 'number'),
'ExposureTime' => array('description' => Horde_Image_Translation::t("Exposure"), 'type' => 'number'),
'FocalLength' => array('description' => Horde_Image_Translation::t("Focal Length"), 'type' => 'number'),
'FocalLengthIn35mmFilm' => array('description' => Horde_Image_Translation::t("Focal Length (35mm equiv)"), 'type' => 'number'),
'ApertureValue' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'number'),
'FNumber' => array('description' => Horde_Image_Translation::t("F-Number"), 'type' => 'number'),
'ISOSpeedRatings' => array('description' => Horde_Image_Translation::t("ISO Setting"), 'type' => 'number'),
'ExposureBiasValue' => array('description' => Horde_Image_Translation::t("Exposure Bias"), 'type' => 'number'),
'ExposureMode' => array('description' => Horde_Image_Translation::t("Exposure Mode"), 'type' => 'number'),
'ExposureProgram' => array('description' => Horde_Image_Translation::t("Exposure Program"), 'type' => 'number'),
'MeteringMode' => array('description' => Horde_Image_Translation::t("Metering Mode"), 'type' => 'number'),
'Flash' => array('description' => Horde_Image_Translation::t("Flash Setting"), 'type' => 'number'),
'UserComment' => array('description' => Horde_Image_Translation::t("User Comment"), 'type' => 'text'),
'ColorSpace' => array('description' => Horde_Image_Translation::t("Color Space"), 'type' => 'number'),
'SensingMethod' => array('description' => Horde_Image_Translation::t("Sensing Method"), 'type' => 'number'),
'WhiteBalance' => array('description' => Horde_Image_Translation::t("White Balance"), 'type' => 'number'),
'Orientation' => array('description' => Horde_Image_Translation::t("Camera Orientation"), 'type' => 'number'),
'Copyright' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
'Artist' => array('description' => Horde_Image_Translation::t("Artist"), 'type' => 'text'),
'LightSource' => array('description' => Horde_Image_Translation::t("Light source"), 'type' => 'number'),
'ImageStabalization' => array('description' => Horde_Image_Translation::t("Image Stabilization"), 'type' => 'text'),
'SceneCaptureType' => array('description' => Horde_Image_Translation::t("Scene Type"), 'type' => 'number'),
),
'COMPOSITE' => array(
'LensID' => array('description' => Horde_Image_Translation::t("Lens Id"), 'type' => 'text'),
'Lens' => array('description' => 'Lens', 'type' => 'text'),
'Aperture' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'text'),
'DOF' => array('description' => Horde_Image_Translation::t("Depth of Field"), 'type' => 'text'),
'FOV' => array('description' => Horde_Image_Translation::t("Field of View"), 'type' => 'text')
)
);
}
/**
* Returns a list of metadata fields that can by used for image titles.
*
* @param mixed $driver A Horde_Image_Exif_Base instance or a string
* specifying the driver in use.
*
* @return array An array of metadata field name hashes.
* @since 2.1.0
*/
public static function getTitleFields($driver = null)
{
if (!is_null($driver) && is_array($driver)) {
$driver = self::factory($driver[0], $driver[1]);
}
if ($driver instanceof Horde_Image_Exif_Base) {
$supported = $driver->supportedCategories();
} else {
$supported = array('XMP', 'IPTC', 'EXIF');
}
$fields = array();
foreach ($supported as $category) {
$fields = array_merge($fields, self::$_titleFields[$category]);
}
$return = array();
$all = self::getFields($driver, true);
foreach ($fields as $field) {
$return[$field] = $all[$field];
}
return $return;
}
/**
* Returns a list of metadata fields that can by used for image
* descriptions.
*
* @param mixed $driver A Horde_Image_Exif_Base instance or a string
* specifying the driver in use.
*
* @return array An array of metadata field hashes.
* @since 2.1.0
*/
public static function getDescriptionFields($driver = null)
{
$map = self::getCategories();
if (!is_null($driver) && is_array($driver)) {
$driver = self::factory($driver[0], $driver[1]);
}
if ($driver instanceof Horde_Image_Exif_Base) {
$supported = $driver->supportedCategories();
} else {
$supported = array('XMP', 'IPTC', 'EXIF');
}
$fields = array();
foreach ($supported as $category) {
$fields = array_merge($fields, self::$_descriptionFields[$category]);
}
$return = array();
$all = self::getFields($driver, true);
foreach ($fields as $field) {
$return[$field] = $all[$field];
}
return $return;
}
/**
* Returns a flattened array of supported metadata fields.
*
* @param mixed $driver A Horde_Image_Exif_Base instance or a
* string specifying the driver in use.
* @param boolean $description_only Only return the field descriptions.
*
* @return array
*/
public static function getFields($driver = null, $description_only = false)
{
if (!is_null($driver) && is_array($driver)) {
$driver = self::factory($driver[0], $driver[1]);
}
if ($driver instanceof Horde_Image_Exif_Base) {
$supported = $driver->supportedCategories();
} else {
$supported = array('XMP', 'IPTC', 'EXIF' );
}
$categories = self::getCategories();
$flattened = array();
foreach ($supported as $category) {
$flattened = array_merge($flattened, $categories[$category]);
}
if ($description_only) {
foreach ($flattened as $key => $data) {
$return[$key] = $data['description'];
}
return $return;
}
return $flattened;
}
/**
* More human friendly exposure formatting.
*/
protected static function _formatExposure($data)
{
if ($data > 0) {
if ($data > 1) {
return sprintf(
Horde_Image_Translation::t("%d sec"),
round($data, 2)
);
} else {
$n = $d = 0;
self::_convertToFraction($data, $n, $d);
if ($n <> 1) {
return sprintf(
Horde_Image_Translation::t("%4f sec"), $n / $d
);
}
return sprintf(
Horde_Image_Translation::t("%s / %s sec"), $n, $d
);
}
} else {
return Horde_Image_Translation::t("Bulb");
}
}
/**
* Converts a floating point number into a fraction.
*
* Many thanks to Matthieu Froment for this code.
*
* (Ported from the Exifer library).
*/
protected static function _convertToFraction($v, &$n, &$d)
{
$MaxTerms = 15; // Limit to prevent infinite loop
$MinDivisor = 0.000001; // Limit to prevent divide by zero
$MaxError = 0.00000001; // How close is enough
// Initialize fraction being converted
$f = $v;
// Initialize fractions with 1/0, 0/1
$n_un = 1;
$d_un = 0;
$n_deux = 0;
$d_deux = 1;
for ($i = 0; $i < $MaxTerms; $i++) {
$a = floor($f); // Get next term
$f = $f - $a; // Get new divisor
$n = $n_un * $a + $n_deux; // Calculate new fraction
$d = $d_un * $a + $d_deux;
$n_deux = $n_un; // Save last two fractions
$d_deux = $d_un;
$n_un = $n;
$d_un = $d;
// Quit if dividing by zero
if ($f < $MinDivisor) {
break;
}
if (abs($v - $n / $d) < $MaxError) {
break;
}
// reciprocal
$f = 1 / $f;
}
}
/**
* Converts an exif field into human-readable form.
*
* Some of these cases are ported from the Exifer library, others were
* changed from their implementation where the EXIF standard dictated
* different behaviour.
*
* @param string $field The name of the field to translate.
* @param string $data The data value to translate.
*
* @return string The converted data.
*/
public static function getHumanReadable($field, $data)
{
switch ($field) {
case 'ExposureMode':
switch ($data) {
case 0: return Horde_Image_Translation::t("Auto exposure");
case 1: return Horde_Image_Translation::t("Manual exposure");
case 2: return Horde_Image_Translation::t("Auto bracket");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ExposureProgram':
switch ($data) {
case 1: return Horde_Image_Translation::t("Manual");
case 2: return Horde_Image_Translation::t("Normal Program");
case 3: return Horde_Image_Translation::t("Aperture Priority");
case 4: return Horde_Image_Translation::t("Shutter Priority");
case 5: return Horde_Image_Translation::t("Creative");
case 6: return Horde_Image_Translation::t("Action");
case 7: return Horde_Image_Translation::t("Portrait");
case 8: return Horde_Image_Translation::t("Landscape");
default: return Horde_Image_Translation::t("Unknown");
}
case 'XResolution':
case 'YResolution':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
return sprintf(Horde_Image_Translation::t("%d dots per unit"), $n);
}
return sprintf(Horde_Image_Translation::t("%d per unit"), $data);
case 'ResolutionUnit':
switch ($data) {
case 1: return Horde_Image_Translation::t("Pixels");
case 2: return Horde_Image_Translation::t("Inch");
case 3: return Horde_Image_Translation::t("Centimeter");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ExifImageWidth':
case 'ExifImageLength':
return sprintf(Horde_Image_Translation::t("%d pixels"), $data);
case 'Orientation':
switch ($data) {
case 1:
return sprintf(Horde_Image_Translation::t("Normal (O deg)"));
case 2:
return sprintf(Horde_Image_Translation::t("Mirrored"));
case 3:
return sprintf(Horde_Image_Translation::t("Upsidedown"));
case 4:
return sprintf(Horde_Image_Translation::t("Upsidedown Mirrored"));
case 5:
return sprintf(Horde_Image_Translation::t("90 deg CW Mirrored"));
case 6:
return sprintf(Horde_Image_Translation::t("90 deg CCW"));
case 7:
return sprintf(Horde_Image_Translation::t("90 deg CCW Mirrored"));
case 8:
return sprintf(Horde_Image_Translation::t("90 deg CW"));
}
break;
case 'ExposureTime':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
}
return self::_formatExposure($data);
case 'ShutterSpeedValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
}
$data = exp($data * log(2));
if ($data > 0) {
$data = 1 / $data;
}
return self::_formatExposure($data);
case 'ApertureValue':
case 'MaxApertureValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
$data = exp(($data * log(2)) / 2);
// Precision is 1 digit.
$data = round($data, 1);
}
return 'f/' . $data;
case 'FocalLength':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
return sprintf(Horde_Image_Translation::t("%d mm"), round($n / $d));
}
return sprintf(Horde_Image_Translation::t("%d mm"), $data);
case 'FNumber':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d != 0) {
return 'f/' . round($n / $d, 1);
}
}
return 'f/' . $data;
case 'ExposureBiasValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($n == 0) {
return '0 EV';
}
}
return $data . ' EV';
case 'MeteringMode':
switch ($data) {
case 0: return Horde_Image_Translation::t("Unknown");
case 1: return Horde_Image_Translation::t("Average");
case 2: return Horde_Image_Translation::t("Center Weighted Average");
case 3: return Horde_Image_Translation::t("Spot");
case 4: return Horde_Image_Translation::t("Multi-Spot");
case 5: return Horde_Image_Translation::t("Multi-Segment");
case 6: return Horde_Image_Translation::t("Partial");
case 255: return Horde_Image_Translation::t("Other");
default: return sprintf(Horde_Image_Translation::t("Unknown: %s"), $data);
}
break;
case 'LightSource':
switch ($data) {
case 1: return Horde_Image_Translation::t("Daylight");
case 2: return Horde_Image_Translation::t("Fluorescent");
case 3: return Horde_Image_Translation::t("Tungsten");
case 4: return Horde_Image_Translation::t("Flash");
case 9: return Horde_Image_Translation::t("Fine weather");
case 10: return Horde_Image_Translation::t("Cloudy weather");
case 11: return Horde_Image_Translation::t("Shade");
case 12: return Horde_Image_Translation::t("Daylight fluorescent");
case 13: return Horde_Image_Translation::t("Day white fluorescent");
case 14: return Horde_Image_Translation::t("Cool white fluorescent");
case 15: return Horde_Image_Translation::t("White fluorescent");
case 17: return Horde_Image_Translation::t("Standard light A");
case 18: return Horde_Image_Translation::t("Standard light B");
case 19: return Horde_Image_Translation::t("Standard light C");
case 20: return 'D55';
case 21: return 'D65';
case 22: return 'D75';
case 23: return 'D50';
case 24: return Horde_Image_Translation::t("ISO studio tungsten");
case 255: return Horde_Image_Translation::t("other light source");
default: return Horde_Image_Translation::t("Unknown");
}
case 'WhiteBalance':
switch ($data) {
case 0: return Horde_Image_Translation::t("Auto");
case 1: return Horde_Image_Translation::t("Manual");
default: Horde_Image_Translation::t("Unknown");
}
break;
case 'FocalLengthIn35mmFilm':
return $data . ' mm';
case 'Flash':
switch ($data) {
case 0: return Horde_Image_Translation::t("No Flash");
case 1: return Horde_Image_Translation::t("Flash");
case 5: return Horde_Image_Translation::t("Flash, strobe return light not detected");
case 7: return Horde_Image_Translation::t("Flash, strobe return light detected");
case 9: return Horde_Image_Translation::t("Compulsory Flash");
case 13: return Horde_Image_Translation::t("Compulsory Flash, Return light not detected");
case 15: return Horde_Image_Translation::t("Compulsory Flash, Return light detected");
case 16: return Horde_Image_Translation::t("No Flash");
case 24: return Horde_Image_Translation::t("No Flash");
case 25: return Horde_Image_Translation::t("Flash, Auto-Mode");
case 29: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light not detected");
case 31: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light detected");
case 32: return Horde_Image_Translation::t("No Flash");
case 65: return Horde_Image_Translation::t("Red Eye");
case 69: return Horde_Image_Translation::t("Red Eye, Return light not detected");
case 71: return Horde_Image_Translation::t("Red Eye, Return light detected");
case 73: return Horde_Image_Translation::t("Red Eye, Compulsory Flash");
case 77: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light not detected");
case 79: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light detected");
case 89: return Horde_Image_Translation::t("Red Eye, Auto-Mode");
case 93: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light not detected");
case 95: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light detected");
}
break;
case 'FileSize':
if ($data <= 0) {
return '0 Bytes';
}
$s = array('B', 'kB', 'MB', 'GB');
$e = floor(log($data, 1024));
return round($data/pow(1024, $e), 2) . ' ' . $s[$e];
case 'SensingMethod':
switch ($data) {
case 1: return Horde_Image_Translation::t("Not defined");
case 2: return Horde_Image_Translation::t("One Chip Color Area Sensor");
case 3: return Horde_Image_Translation::t("Two Chip Color Area Sensor");
case 4: return Horde_Image_Translation::t("Three Chip Color Area Sensor");
case 5: return Horde_Image_Translation::t("Color Sequential Area Sensor");
case 7: return Horde_Image_Translation::t("Trilinear Sensor");
case 8: return Horde_Image_Translation::t("Color Sequential Linear Sensor");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ColorSpace':
switch ($data) {
case 1: return Horde_Image_Translation::t("sRGB");
default: return Horde_Image_Translation::t("Uncalibrated");
}
case 'SceneCaptureType':
switch ($data) {
case 0: return Horde_Image_Translation::t("Standard");
case 1: return Horde_Image_Translation::t("Landscape");
case 2: return Horde_Image_Translation::t("Portrait");
case 3: return Horde_Image_Translation::t("Night Scene");
default: return Horde_Image_Translation::t("Unknown");
}
case 'DateTime':
case 'DateTimeOriginal':
case 'DateTimeDigitized':
return date('m/d/Y H:i:s O', $data);
case 'UserComment':
//@TODO: the first 8 bytes of this field contain the charset used
// to encode the comment. Either ASCII, JIS, UNICODE, or
// UNDEFINED. Should probably either convert to a known charset
// here and let the calling code deal with it, or allow this
// method to take an optional charset to convert to (would
// introduce a dependency on Horde_String to do the conversion).
$data = trim(substr($data, 7)) ;
default:
return !empty($data) ? $data : '---';
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Gd.php 0000664 0001750 0001750 00000104342 13160240461 015664 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image API for the PHP GD extension.
*
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*
* @property-read resource $_im The underlaying image resource.
*/
class Horde_Image_Gd extends Horde_Image_Base
{
/**
* Allocated color resources.
*
* @var int[]
*/
protected $_colors = array();
/**
* Capabilites of this driver.
*
* @var string[]
*/
protected $_capabilities = array(
'canvas',
'circle',
'crop',
'dashedLine',
'flip',
'grayscale',
'line',
'mirror',
'polygon',
'polyline',
'rectangle',
'resize',
'rotate',
'roundedRectangle',
'sepia',
'text',
'yellowize',
);
/**
* GD image resource for the current image data.
*
* @var resource
*/
protected $_im;
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif (!empty($params['data'])) {
$this->loadString($params['data']);
} elseif (!empty($params['width'])) {
$this->_im = $this->create($this->_width, $this->_height);
$this->call(
'imageFill',
array(
$this->_im,
0, 0,
$this->_allocateColor($this->_background)
)
);
}
}
/**
*/
public function __get($property)
{
switch ($property) {
case '_im':
return $this->_im;
}
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$this->call('image' . $this->_type, array($this->_im));
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert Ignored for Gd driver.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return string The raw image data.
*/
public function raw($convert = false, $options = array())
{
if (!is_resource($this->_im)) {
return '';
}
ob_start();
call_user_func('image' . $this->_type, $this->_im);
if (empty($options['stream'])) {
return ob_get_clean();
}
$s = new Horde_Stream_Temp();
$s->add(ob_get_clean(), true);
return $s->stream;
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
parent::reset();
if (is_resource($this->_im)) {
$this->call('imageDestroy', array($this->_im));
}
}
/**
* Returns the height and width of the current image data.
*
* @return array A hash with 'width' containing the width, 'height'
* containing the height of the image.
*/
public function getDimensions()
{
if (is_resource($this->_im) &&
$this->_width == 0 &&
$this->_height == 0) {
$this->_width = $this->call('imageSX', array($this->_im));
$this->_height = $this->call('imageSY', array($this->_im));
}
return array(
'width' => $this->_width,
'height' => $this->_height
);
}
/**
* Creates a color that can be accessed in this object.
*
* When a color is set, the integer resource of it is returned.
*
* @param string $name The name of the color.
* @param integer $alpha Alpha transparency (0 - 127).
*
* @return integer The resource of the color that can be passed to GD.
*/
private function _allocateColor($name, $alpha = 0)
{
if (empty($this->_colors[$name])) {
if ($name == 'none') {
$this->_colors[$name] = $this->call(
'imageColorAllocateAlpha',
array($this->_im, 0, 0, 0, 127)
);
} else {
list($r, $g, $b) = Horde_Image::getRGB($name);
$this->_colors[$name] = $this->call(
'imageColorAllocateAlpha',
array($this->_im, $r, $g, $b, $alpha)
);
}
}
return $this->_colors[$name];
}
/**
* Returns the numeric font size a from textual description.
*
* @param string $font A textual size description.
*
* @return integer The font size supported by GD.
*/
private function _getFont($font)
{
switch ($font) {
case 'tiny':
return 1;
case 'medium':
return 3;
case 'large':
return 4;
case 'giant':
return 5;
case 'small':
default:
return 2;
}
}
/**
* Loads the image data from a string.
*
* @param string $image_data The data to use for the image.
*/
public function loadString($image_data)
{
$this->_im = $this->call('imageCreateFromString', array($image_data));
}
/**
* Loads the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from.
*/
public function loadFile($filename)
{
$info = $this->call('getimagesize', array($filename));
if (is_array($info)) {
switch ($info[2]) {
case IMAGETYPE_GIF:
if (function_exists('imagecreatefromgif')) {
$this->_im = $this->call(
'imagecreatefromgif',
array($filename)
);
}
break;
case IMAGETYPE_JPEG:
$this->_im = $this->call(
'imagecreatefromjpeg',
array($filename)
);
break;
case IMAGETYPE_PNG:
$this->_im = $this->call(
'imagecreatefrompng',
array($filename)
);
break;
case IMAGETYPE_WBMP:
if (function_exists('imagecreatefromgwbmp')) {
$this->_im = $this->call(
'imagecreatefromgwbmp',
array($filename)
);
}
break;
case IMAGETYPE_XBM:
$this->_im = $this->call(
'imagecreatefromxbm',
array($filename)
);
break;
}
}
if (is_resource($this->_im)) {
return;
}
parent::loadFile($filename);
$this->_im = $this->call(
'imageCreateFromString',
array($this->_data->__toString())
);
}
/**
* Resizes the current image.
*
* @param integer $width The new width.
* @param integer $height The new height.
* @param boolean $ratio Maintain original aspect ratio.
* @param boolean $keepProfile Keep the image meta data (unused).
*/
public function resize($width, $height, $ratio = true, $keepProfile = false)
{
/* Abort if we're asked to divide by zero, truncate the image
* completely in either direction, or there is no image data. */
if (!$width || !$height || !is_resource($this->_im)) {
throw new Horde_Image_Exception('Unable to resize image.');
}
if ($ratio) {
if ($width / $height > $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im))) {
$width = $height
* $this->call('imageSX', array($this->_im))
/ $this->call('imageSY', array($this->_im));
} else {
$height = $width
* $this->call('imageSY', array($this->_im))
/ $this->call('imageSX', array($this->_im));
}
}
$im = $this->_im;
$this->_im = $this->create($width, $height);
/* Reset geometry since it will change. */
$this->_width = 0;
$this->_height = 0;
try {
$this->call(
'imageCopyResampled',
array(
$this->_im, $im,
0, 0, 0, 0,
$width, $height,
$this->call('imageSX', array($im)),
$this->call('imageSY', array($im))
)
);
} catch (Horde_Image_Exception $e) {
$this->call(
'imageCopyResized',
array(
$this->_im, $im,
0, 0, 0, 0,
$width, $height,
$this->call('imageSX', array($im)),
$this->call('imageSY', array($im))
)
);
}
}
/**
* Crops the current image.
*
* @param integer $x1 x for the top left corner.
* @param integer $y1 y for the top left corner.
* @param integer $x2 x for the bottom right corner.
* @param integer $y2 y for the bottom right corner.
*/
public function crop($x1, $y1, $x2, $y2)
{
$im = $this->_im;
$this->_im = $this->create($x2 - $x1, $y2 - $y1);
$this->_width = 0;
$this->_height = 0;
$this->call(
'imageCopy',
array($this->_im, $im, 0, 0, $x1, $y1, $x2 - $x1, $y2 - $y1)
);
}
/**
* Rotates the current image.
*
* @param integer $angle The angle to rotate the image by, in the
* clockwise direction.
* @param string $background The background color to fill any triangles.
*/
public function rotate($angle, $background = 'white')
{
$background = $this->_allocateColor($background);
$this->_width = 0;
$this->_height = 0;
switch ($angle) {
case '90':
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$xymax = max($x, $y);
$im = $this->create($xymax, $xymax);
$im = $this->call('imageRotate', array($im, 270, $background));
$this->_im = $im;
$im = $this->create($y, $x);
if ($x < $y) {
$this->call(
'imageCopy',
array($im, $this->_im, 0, 0, 0, 0, $xymax, $xymax)
);
} elseif ($x > $y) {
$this->call(
'imageCopy',
array(
$im, $this->_im,
0, 0, $xymax - $y, $xymax - $x,
$xymax, $xymax
)
);
}
$this->_im = $im;
break;
default:
$this->_im = $this->call(
'imageRotate',
array($this->_im, 360 - $angle, $background)
);
break;
}
}
/**
* Flips the current image.
*/
public function flip()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curY = 0; $curY < $y; $curY++) {
$this->call(
'imageCopy',
array($im, $this->_im, 0, $y - ($curY + 1), 0, $curY, $x, 1)
);
}
$this->_im = $im;
}
/**
* Mirrors the current image.
*/
public function mirror()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curX = 0; $curX < $x; $curX++) {
$this->call(
'imageCopy',
array($im, $this->_im, $x - ($curX + 1), 0, $curX, 0, 1, $y)
);
}
$this->_im = $im;
}
/**
* Converts the current image to grayscale.
*/
public function grayscale()
{
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = min(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(
255,
abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
$this->call('imageColorSet', array($this->_im, $x, $new, $new, $new));
}
}
/**
* Applies a sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
$tintR = 80;
$tintG = 43;
$tintB = -23;
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(
255,
abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness
);
$r = min(255, $new + $tintR);
$g = min(255, $new + $tintG);
$b = min(255, $new + $tintB);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Applies a yellow filter.
*
* Adds a layer of yellow that can be transparent or solid. If $intensityY
* is 255 the image will be 0% transparent (solid).
*
* @param integer $intensityY How strong should the yellow (red and green)
* be? (0-255)
* @param integer $intensityB How weak should the blue be? (>= 2, in the
* positive limit it will be make BLUE 0)
*/
public function yellowize($intensityY = 50, $intensityB = 3)
{
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$r = min($src['red'] + $intensityY, 255);
$g = min($src['green'] + $intensityY, 255);
$b = max(($r + $g) / max($intensityB, 2), 0);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = '', $color = 'black', $direction = 0,
$fontsize = 'small'
)
{
$c = $this->_allocateColor($color);
$f = $this->_getFont($fontsize);
switch ($direction) {
case -90:
case 270:
$this->call(
'imageStringUp',
array($this->_im, $f, $x, $y, $string, $c)
);
break;
case 0:
default:
$this->call(
'imageString',
array($this->_im, $f, $x, $y, $string, $c)
);
break;
}
}
/**
* Draws a circle.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = null)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$result = $this->call(
'imageEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
} else {
if ($fill !== $color) {
$fillColor = $this->_allocateColor($fill);
$this->call(
'imageFilledEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $fillColor)
);
$this->call(
'imageEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
} else {
$this->call(
'imageFilledEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
}
}
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = 'none')
{
$vertices = array();
foreach ($verts as $vert) {
$vertices[] = $vert['x'];
$vertices[] = $vert['y'];
}
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledPolygon',
array($this->_im, $vertices, count($verts), $f)
);
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call(
'imagePolygon',
array($this->_im, $vertices, count($verts), $c)
);
}
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle(
$x, $y, $width, $height, $color = 'black', $fill = 'none'
)
{
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledRectangle',
array($this->_im, $x, $y, $x + $width, $y + $height, $f)
);
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call(
'imageRectangle',
array($this->_im, $x, $y, $x + $width, $y + $height, $c)
);
}
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rounded rectangle with.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color = 'black', $fill = 'none'
)
{
if ($round <= 0) {
// Optimize out any calls with no corner rounding.
$this->rectangle($x, $y, $width, $height, $color, $fill);
return;
}
$c = $this->_allocateColor($color);
// Set corner points to avoid lots of redundant math.
$xul = $x + $round;
$yul = $y + $round;
$xur = $x + $width - $round;
$yur = $y + $round;
$xlr = $x + $width - $round;
$ylr = $y + $height - $round;
$xll = $x + $round;
$yll = $y + $height - $round;
$r = $round * 2;
// Calculate the upper left arc.
$pul = Horde_Image::arcPoints($round, 180, 270);
// Calculate the upper right arc.
$pur = Horde_Image::arcPoints($round, 270, 360);
// Calculate the lower right arc.
$plr = Horde_Image::arcPoints($round, 0, 90);
// Calculate the lower left arc.
$pll = Horde_Image::arcPoints($round, 90, 180);
// Draw the corners - upper left, upper right, lower right, lower left.
$this->call(
'imageArc',
array($this->_im, $xul, $yul, $r, $r, 180, 270, $c)
);
$this->call(
'imageArc',
array($this->_im, $xur, $yur, $r, $r, 270, 360, $c)
);
$this->call(
'imageArc',
array($this->_im, $xlr, $ylr, $r, $r, 0, 90, $c)
);
$this->call(
'imageArc',
array($this->_im, $xll, $yll, $r, $r, 90, 180, $c)
);
// Draw the connecting sides - top, right, bottom, left.
$this->call(
'imageLine',
array($this->_im, $xul, $y, $xur, $y, $c)
);
$this->call(
'imageLine',
array($this->_im, $x + $width, $yur, $x + $width, $ylr, $c)
);
$this->call(
'imageLine',
array($this->_im, $xlr, $y + $height, $xll, $y + $height, $c)
);
$this->call(
'imageLine',
array($this->_im, $x, $yll, $x, $yul, $c)
);
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFillToBorder',
array(
$this->_im,
$x + ($width / 2), $y + ($height / 2),
$c, $f
)
);
}
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$c = $this->_allocateColor($color);
// Don't need to do anything special for single-width lines.
if ($width == 1) {
$this->call('imageLine', array($this->_im, $x1, $y1, $x2, $y2, $c));
} elseif ($x1 == $x2) {
// For vertical lines, we can just draw a vertical rectangle.
$left = $x1 - floor(($width - 1) / 2);
$right = $x1 + floor($width / 2);
$this->call(
'imageFilledRectangle',
array($this->_im, $left, $y1, $right, $y2, $c)
);
} elseif ($y1 == $y2) {
// For horizontal lines, we can just draw a horizontal filled
// rectangle.
$top = $y1 - floor($width / 2);
$bottom = $y1 + floor(($width - 1) / 2);
$this->call(
'imageFilledRectangle',
array($this->_im, $x1, $top, $x2, $bottom, $c)
);
} else {
// Angled lines.
// Make sure that the end points of the line are perpendicular to
// the line itself.
$a = atan2($y1 - $y2, $x2 - $x1);
$dx = (sin($a) * $width / 2);
$dy = (cos($a) * $width / 2);
$verts = array(
$x2 + $dx, $y2 + $dy,
$x2 - $dx, $y2 - $dy,
$x1 - $dx, $y1 - $dy,
$x1 + $dx, $y1 + $dy
);
$this->call(
'imageFilledPolygon',
array($this->_im, $verts, count($verts) / 2, $c)
);
}
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x co-ordinate of the start.
* @param integer $y0 The y co-ordinate of the start.
* @param integer $x1 The x co-ordinate of the end.
* @param integer $y1 The y co-ordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line.
* @param integer $dash_space The length of a space in the dashed line.
*/
public function dashedLine(
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
$c = $this->_allocateColor($color);
$w = $this->_allocateColor('white');
// Set up the style array according to the $dash_* parameters.
$style = array();
for ($i = 0; $i < $dash_length; $i++) {
$style[] = $c;
}
for ($i = 0; $i < $dash_space; $i++) {
$style[] = $w;
}
$this->call('imageSetStyle', array($this->_im, $style));
$this->call('imageSetThickness', array($this->_im, $width));
$this->call(
'imageLine',
array($this->_im, $x0, $y0, $x1, $y1, IMG_COLOR_STYLED)
);
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
$first = true;
foreach ($verts as $vert) {
if (!$first) {
$this->line(
$lastX, $lastY,
$vert['x'], $vert['y'],
$color, $width
);
} else {
$first = false;
}
$lastX = $vert['x'];
$lastY = $vert['y'];
}
}
/**
* Draws an arc.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc (defaults to none).
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = null
)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$this->call(
'imageArc',
array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c)
);
} else {
if ($fill !== $color) {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$f, IMG_ARC_PIE
)
);
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$c, IMG_ARC_EDGED | IMG_ARC_NOFILL
)
);
} else {
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$c, IMG_ARC_PIE
)
);
}
}
}
/**
* Creates an image of the given size.
*
* If possible the function returns a true color image.
*
* @param integer $width The image width.
* @param integer $height The image height.
*
* @return resource The image handler.
* @throws Horde_Image_Exception
*/
public function create($width, $height)
{
$result = $this->call('imageCreateTrueColor', array($width, $height));
if (!is_resource($result)) {
throw new Horde_Image_Exception('Could not create image.');
}
$this->call('imagesavealpha', array($result, true));
$this->call('imagealphablending', array($result, false));
if (function_exists('imageantialias')) {
$this->call('imageantialias', array($result, true));
}
return $result;
}
/**
* Wraps a call to a function of the gd extension.
*
* @param string $function The name of the function to wrap.
* @param array $params An array with all parameters for that function.
*
* @return mixed The result of the function call.
* @throws Horde_Image_Exception
*/
public function call($function, $params = null)
{
unset($php_errormsg);
$track = ini_set('track_errors', 1);
$result = @call_user_func_array($function, $params);
if ($track !== false) {
ini_set('track_errors', $track);
}
if (!empty($php_errormsg)) {
$error_msg = $php_errormsg;
throw new Horde_Image_Exception($error_msg);
}
return $result;
}
/**
* Applies the specified mask to the image.
*
* @param resource $mask The gd image resource representing the mask
*/
public function applyMask($mask)
{
$imgX = round($this->call('imageSX', array($this->_im)));
$imgY = round($this->call('imageSY', array($this->_im)));
$mask_resized = $this->create($imgX, $imgY);
$this->call(
'imageCopyResampled',
array(
$mask_resized, $mask,
0, 0, 0, 0,
$imgX, $imgY,
$this->call('imageSX', array($mask)),
$this->call('imageSY', array($mask))
)
);
$mask_blendtemp = $this->create($imgX, $imgY);
$mbtX = $this->call('imageSX', array($mask_blendtemp));
$mbtY = $this->call('imageSY', array($mask_blendtemp));
$color_background = $this->call(
'imageColorAllocate',
array($mask_blendtemp, 0, 0, 0)
);
$this->call(
'imageFilledRectangle',
array($mask_blendtemp, 0, 0, $mbtX, $mbtY, $color_background)
);
$this->call('imageAlphaBlending', array($mask_blendtemp, false));
$this->call('imageSaveAlpha', array($mask_blendtemp, true));
for ($x = 0; $x < $imgX; $x++) {
for ($y = 0; $y < $imgY; $y++) {
$realPixel = $this->call(
'imageColorsForIndex',
array(
$this->_im,
$this->call('imageColorAt', array($this->_im, $x, $y))
)
);
$maskPixel = Horde_Image::grayscalePixel(
$this->call(
'imageColorsForIndex',
array(
$mask_resized,
$this->call(
'imageColorAt',
array($mask_resized, $x, $y)
)
)
)
);
$maskAlpha = 127
- floor($maskPixel['red'] / 2)
* (1 - ($realPixel['alpha'] / 127));
$newcolor = $this->call(
'imageColorAllocateAlpha',
array(
$mask_blendtemp,
$realPixel['red'],
$realPixel['green'],
$realPixel['blue'],
intval($maskAlpha)
)
);
$this->call(
'imageSetPixel',
array($mask_blendtemp, $x, $y, $newcolor)
);
}
}
$this->call('imageAlphaBlending', array($this->_im, false));
$this->call('imageSaveAlpha', array($this->_im, true));
$this->call(
'imageCopy',
array($this->_im, $mask_blendtemp, 0, 0, 0, 0, $mbtX, $mbtY)
);
$this->call('imageDestroy', array($mask_blendtemp));
$this->call('imageDestroy', array($mask_resized));
}
}
Horde_Image-2.5.2/lib/Horde/Image/Im.php 0000664 0001750 0001750 00000063206 13160240461 015702 0 ustar jan jan
* @author Mike Cochrane
* @author Michael J. Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* ImageMagick driver for the Horde_Image API.
*
* @author Chuck Hagenbuch
* @author Mike Cochrane
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*
* @property-read Imagick $imagick The underlaying Imagick object.
*/
class Horde_Image_Im extends Horde_Image_Base
{
/**
* Capabilites of this driver.
*
* @var string[]
*/
protected $_capabilities = array(
'arc',
'canvas',
'circle',
'crop',
'dashedLine',
'flip',
'grayscale',
'line',
'mirror',
'multipage',
'pdf',
'polygon',
'polyline',
'rectangle',
'resize',
'rotate',
'roundedRectangle',
'sepia',
'text',
);
/**
* Operations to be performed before the source filename is specified on
* the command line.
*
* @var array
*/
protected $_operations = array();
/**
* Operations to be added after the source filename is specified on the
* command line.
*
* @var array
*/
protected $_postSrcOperations = array();
/**
* An array of temporary filenames that need to be unlinked at the end of
* processing.
*
* Use addFileToClean() from client code (effects) to add files to this
* array.
*
* @var array
*/
protected $_toClean = array();
/**
* Path to the convert binary.
*
* @var string
*/
protected $_convert = '';
/**
* Path to the identify binary.
*
* @var string
*/
protected $_identify;
/**
* Cache for the number of image pages.
*
* @var integer
*/
private $_pages;
/**
* The current page for the iterator.
*
* @var integer
*/
private $_currentPage = 0;
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (empty($context['convert'])) {
throw new InvalidArgumentException(
'A path to the convert binary is required.'
);
}
$this->_convert = $context['convert'];
if (!empty($context['identify'])) {
$this->_identify = $context['identify'];
}
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif (!empty($params['data'])) {
$this->loadString($params['data']);
} else {
$cmd = sprintf(
'-size %dx%d xc:%s -strip %s:__FILEOUT__',
$this->_width,
$this->_height,
escapeshellarg($this->_background),
$this->_type
);
$this->executeConvertCmd($cmd);
}
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert If true, the image data will be returned in the
* target format, independently from any image
* operations.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return mixed The raw image data either as a string or stream resource.
*/
public function raw($convert = false, $options = array())
{
if (!empty($options['stream'])) {
return $this->_raw($convert)->stream;
}
return $this->_raw($convert)->__toString();
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert If true, the image data will be returned
* in the target format, independently from
* any image operations.
* @param array $options An array of options:
* - index: (integer) An image index.
* - preserve_data (boolean) If true, return the converted image but
* preserve the internal image data.
*
* @return Horde_Stream The data, in a Horde_Stream object.
*/
private function _raw($convert = false, $options = array())
{
$options = array_merge(
array('index' => 0, 'preserve_data' => false),
$options
);
if (empty($this->_data) ||
// If there are no operations, and we already have data, don't
// bother writing out files, just return the current data.
(!$convert &&
!count($this->_operations) &&
!count($this->_postSrcOperations))) {
return $this->_data;
}
$tmpin = $this->toFile($this->_data);
$tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
$command = $this->_convert . ' ' . implode(' ', $this->_operations)
. ' "' . $tmpin . '"\'[' . (integer)$options['index'] . ']\' '
. implode(' ', $this->_postSrcOperations)
. ' -strip ' . $this->_type . ':"' . $tmpout . '" 2>&1';
$this->_logDebug(sprintf("convert command executed by Horde_Image_im::raw(): %s", $command));
exec($command, $output, $retval);
if ($retval) {
$error = sprintf("Error running command: %s", $command . "\n" . implode("\n", $output));
$this->_logErr($error);
throw new Horde_Image_Exception($error);
}
/* Empty the operations queue */
$this->_operations = array();
$this->_postSrcOperations = array();
/* Load the result */
$fp = fopen($tmpout, 'r');
$return = new Horde_Stream_Temp();
$return->add($fp, true);
if (empty($options['preserve_data'])) {
if ($this->_data) {
$this->_data->close();
}
$this->_data = $return;
}
@unlink($tmpin);
@unlink($tmpout);
return $return;
}
/**
* Resets the image data.
*/
public function reset()
{
parent::reset();
$this->_operations = array();
$this->_postSrcOperations = array();
$this->clearGeometry();
}
/**
* Resizes the current image.
*
* @param integer $width The new width.
* @param integer $height The new height.
* @param boolean $ratio Maintain original aspect ratio.
* @param boolean $keepProfile Keep the image meta data.
*/
public function resize($width, $height, $ratio = true, $keepProfile = false)
{
$resWidth = $width * 2;
$resHeight = $height * 2;
$this->_operations[] = "-size {$resWidth}x{$resHeight}";
if ($ratio) {
$this->_postSrcOperations[] =
($keepProfile ? '-resize' : '-thumbnail')
. sprintf(' %dx%d', $width, $height);
} else {
$this->_postSrcOperations[] =
($keepProfile ? '-resize' : '-thumbnail')
. sprintf(' %dx%d', $width, $height);
}
// Refresh the data
$this->raw(false, array('stream' => true));
// Reset the width and height instance variables since after resize we
// don't know the *exact* dimensions yet (especially if we maintained
// aspect ratio.
$this->clearGeometry();
}
/**
* Crops the current image.
*
* @param integer $x1 x for the top left corner.
* @param integer $y1 y for the top left corner.
* @param integer $x2 x for the bottom right corner.
* @param integer $y2 y for the bottom right corner.
*/
public function crop($x1, $y1, $x2, $y2)
{
$line = ($x2 - $x1) . 'x' . ($y2 - $y1) . '+' . (integer)$x1 . '+' . (integer)$y1;
$this->_operations[] = '-crop ' . $line . ' +repage';
// Reset width/height since these might change
$this->raw(false, array('stream' => true));
$this->clearGeometry();
}
/**
* Rotates the current image.
*
* @param integer $angle The angle to rotate the image by,
* in the clockwise direction.
* @param integer $background The background color to fill any triangles.
*/
public function rotate($angle, $background = 'white')
{
$this->raw(false, array('stream' => true));
$this->_operations[] = sprintf(
'-background %s -rotate %d',
escapeshellarg($this->_background),
(integer)$angle
);
$this->raw(false, array('stream' => true));
// Reset width/height since these might have changed
$this->clearGeometry();
}
/**
* Flips the current image.
*/
public function flip()
{
$this->_operations[] = '-flip';
}
/**
* Mirrors the current image.
*/
public function mirror()
{
$this->_operations[] = '-flop';
}
/**
* Converts the current image to grayscale.
*/
public function grayscale()
{
$this->_postSrcOperations[] = '-colorspace GRAY';
}
/**
* Applies a sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
$this->_operations[] = '-sepia-tone ' . (integer)$threshold . '%';
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @TODO: Need to differentiate between the stroke (border) and the fill
* color, but this is a BC break, since we were just not providing a
* border.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = '', $color = 'black', $direction = 0,
$fontsize = 'small'
)
{
$string = addslashes('"' . $string . '"');
$fontsize = Horde_Image::getFontSize($fontsize);
$command = 'text ' . (integer)$x . ',' . (integer)$y . ' ' . $string;
$this->_postSrcOperations[] = '-fill ' . escapeshellarg($color)
. (!empty($font) ? ' -font ' . escapeshellarg($font) : '')
. sprintf(
' -pointsize %d -gravity northwest -draw "%s" -fill none',
$fontsize, $command
);
}
/**
* Draws a circle.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = 'none')
{
$xMax = $x + $r;
$this->_postSrcOperations[] = sprintf(
'-stroke %s -fill %s -draw "circle %d,%d %d,%d" -stroke none -fill none',
escapeshellarg($color), escapeshellarg($fill), $x, $y, $xMax, $y
);
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = 'none')
{
$command = '';
foreach ($verts as $vert) {
$command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
}
$this->_postSrcOperations[] = sprintf(
'-stroke %s -fill %s -draw "polygon $command" -stroke none -fill none',
escapeshellarg($color), escapeshellarg($fill)
);
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle($x, $y, $width, $height, $color, $fill = 'none')
{
$xMax = $x + $width;
$yMax = $y + $height;
$this->_postSrcOperations[] = sprintf(
'-stroke %s -fill %s -draw "rectangle %d,%d %d,%d" -stroke none -fill none',
escapeshellarg($color), escapeshellarg($fill), $x, $y, $xMax, $yMax
);
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rounded rectangle with.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color, $fill
)
{
$x1 = $x + $width;
$y1 = $y + $height;
$this->_postSrcOperations[] = sprintf(
'-stroke %s -fill %s -draw "roundRectangle %d,%d %d,%d %d,%d" -stroke none -fill none',
escapeshellarg($color), escapeshellarg($fill), $x, $y, $x1, $y1, $round, $round
);
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x0, $y0, $x1, $y1, $color = 'black', $width = 1)
{
$this->_operations[] = sprintf(
'-stroke %s -strokewidth %d -draw "line %d,%d %d,%d"',
escapeshellarg($color), $width, $x0, $y0, $x1, $y1
);
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x co-ordinate of the start.
* @param integer $y0 The y co-ordinate of the start.
* @param integer $x1 The x co-ordinate of the end.
* @param integer $y1 The y co-ordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line
* @param integer $dash_space The length of a space in the dashed line
*/
public function dashedLine(
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
$this->_operations[] = sprintf(
'-stroke %s -strokewidth %d -draw "line %d,%d %d,%d"',
escapeshellarg($color), $width, $x0, $y0, $x1, $y1
);
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
$command = '';
foreach ($verts as $vert) {
$command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
}
$this->_operations[] = sprintf(
'-stroke %s -strokewidth %d -fill none -draw "polyline $command" -strokewidth 1 -stroke none -fill none',
escapeshellarg($color), $width
);
}
/**
* Draws an arc.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc (defaults to none).
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = 'none'
)
{
// Split up arcs greater than 180 degrees into two pieces.
$this->_postSrcOperations[] = sprintf(
'-stroke %s -fill %s',
escapeshellarg($color), escapeshellarg($fill)
);
$mid = round(($start + $end) / 2);
$x = round($x);
$y = round($y);
$r = round($r);
if ($mid > 90) {
$this->_postSrcOperations[] = sprintf(
'-draw "ellipse %d,%d %d,%d %d,%d"',
$x, $y, $r, $r, $start, $mid
);
$this->_postSrcOperations[] = sprintf(
'-draw "ellipse %d,%d %d,%d %d,%d"',
$x, $y, $r, $r, $mid, $end
);
} else {
$this->_postSrcOperations[] = sprintf(
'-draw "ellipse %d,%d %d,%d %d,%d"',
$x, $y, $r, $r, $start, $end
);
}
// If filled, draw the outline.
if (!empty($fill)) {
list($x1, $y1) = Horde_Image::circlePoint($start, $r * 2);
list($x2, $y2) = Horde_Image::circlePoint($mid, $r * 2);
list($x3, $y3) = Horde_Image::circlePoint($end, $r * 2);
$verts = array(
array('x' => $x + round($x3), 'y' => $y + round($y3)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x1), 'y' => $y + round($y1))
);
if ($mid > 90) {
$verts1 = array(
array('x' => $x + round($x2), 'y' => $y + round($y2)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x1), 'y' => $y + round($y1))
);
$verts2 = array(
array('x' => $x + round($x3), 'y' => $y + round($y3)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x2), 'y' => $y + round($y2))
);
$this->polygon($verts1, $fill, $fill);
$this->polygon($verts2, $fill, $fill);
} else {
$this->polygon($verts, $fill, $fill);
}
$this->polyline($verts, $color);
$this->_postSrcOperations[] = '-stroke none -fill none';
}
}
/**
* Applies any effects in the effect queue.
*/
public function applyEffects()
{
$this->raw(false, array('stream' => true));
foreach ($this->_toClean as $tempfile) {
@unlink($tempfile);
}
}
/**
* Method to execute a raw command directly in convert.
*
* Useful for executing more involved operations that may require multiple
* convert commands piped into each other as could be needed by
* Im based Horde_Image_Effect objects.
*
* The input and output files are quoted and substituted for __FILEIN__ and
* __FILEOUT__ respectfully. In order to support piped convert commands,
* the path to the convert command is substitued for __CONVERT__ (but the
* initial convert command is added automatically).
*
* @param string $cmd The command string, with substitutable tokens
* @param array $values Any values that should be substituted for tokens.
*/
public function executeConvertCmd($cmd, $values = array())
{
// First, get a temporary file for the input
if (strpos($cmd, '__FILEIN__') !== false) {
$tmpin = $this->toFile($this->_data);
} else {
$tmpin = '';
}
// Now an output file
$tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
// Substitue them in the cmd string
$cmd = str_replace(
array('__FILEIN__', '__FILEOUT__', '__CONVERT__'),
array('"' . $tmpin . '"', '"' . $tmpout . '"', $this->_convert),
$cmd
);
//TODO: See what else needs to be replaced.
$cmd = $this->_convert . ' ' . $cmd . ' 2>&1';
// Log it
$this->_logDebug(sprintf("convert command executed by Horde_Image_im::executeConvertCmd(): %s", $cmd));
exec($cmd, $output, $retval);
if ($retval) {
$this->_logErr(sprintf("Error running command: %s", $cmd . "\n" . implode("\n", $output)));
}
$fp = fopen($tmpout, 'r');
if ($this->_data) {
$this->_data->close();
}
$this->_data = new Horde_Stream_Temp();
$this->_data->add($fp, true);
@unlink($tmpin);
@unlink($tmpout);
}
/**
* Returns the version of the convert command available.
*
* This needs to be publicly visable since it's used by various effects.
*
* @return string A version string suitable for using in version_compare().
*/
public function getIMVersion()
{
static $version = null;
if (!is_array($version)) {
$commandline = $this->_convert . ' --version';
exec($commandline, $output, $retval);
if (preg_match('/([0-9])\.([0-9])\.([0-9])/', $output[0], $matches)) {
$version = $matches;
return $matches;
} else {
return false;
}
}
return $version;
}
public function addPostSrcOperation($operation)
{
$this->_postSrcOperations[] = $operation;
}
public function addOperation($operation)
{
$this->_operations[] = $operation;
}
public function addFileToClean($filename)
{
$this->_toClean[] = $filename;
}
public function getConvertPath()
{
return $this->_convert;
}
/**
* Reset the imagick iterator to the first image in the set.
*
* @return void
*/
public function rewind()
{
$this->_logDebug('Horde_Image_Im#rewind');
$this->_currentPage = 0;
}
/**
* Return the current image from the internal iterator.
*
* @return Horde_Image_Imagick
*/
public function current()
{
$this->_logDebug('Horde_Image_Im#current');
return $this->getImageAtIndex($this->_currentPage);
}
/**
* Get the index of the internal iterator.
*
* @return integer
*/
public function key()
{
$this->_logDebug('Horde_Image_Im#key');
return $this->_currentPage;
}
/**
* Advance the iterator
*
* @return Horde_Image_Im
*/
public function next()
{
$this->_logDebug('Horde_Image_Im#next');
$this->_currentPage++;
if ($this->valid()) {
return $this->getImageAtIndex($this->_currentPage);
}
}
/**
* Deterimines if the current iterator item is valid.
*
* @return boolean
*/
public function valid()
{
return $this->_currentPage < $this->getImagePageCount();
}
/**
* Request a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Base
*/
public function getImageAtIndex($index)
{
$this->_logDebug('Horde_Image_Im#getImageAtIndex: ' . $index);
if ($index >= $this->getImagePageCount()) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
$rawImage = $this->_raw(true, array('index' => $index, 'preserve_data' => true));
$image = new Horde_Image_Im(array('data' => $rawImage), $this->_context);
return $image;
}
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
public function getImagePageCount()
{
if (is_null($this->_pages)) {
$pages = $this->_getImagePages();
$this->_pages = array_pop($pages);
}
$this->_logDebug('Horde_Image_Im#getImagePageCount: ' . $this->_pages);
return $this->_pages;
}
private function _getImagePages()
{
$this->_logDebug('Horde_Image_Im#_getImagePages');
$filename = $this->toFile();
$cmd = $this->_identify . ' -format "%n" ' . $filename;
exec($cmd, $output, $retval);
if ($retval) {
$this->_logErr(sprintf("Error running command: %s", $cmd . "\n" . implode("\n", $output)));
}
unlink($filename);
return $output;
}
}
Horde_Image-2.5.2/lib/Horde/Image/Imagick.php 0000664 0001750 0001750 00000065120 13160240461 016676 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Imagick driver for the Horde_Image API.
*
* @author Michael J. Rubinsky
* @category Horde
* @copyright 2007-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*
* @property-read Imagick $imagick The underlaying Imagick object.
*/
class Horde_Image_Imagick extends Horde_Image_Base
{
/**
* The underlaying Imagick object.
*
* @var Imagick
*/
protected $_imagick;
/**
* Flag for iterator, since calling nextImage on Imagick would result in a
* fatal error if there are no more images.
*
* @var boolean
*/
private $_noMoreImages = false;
/**
* Capabilites of this driver.
*
* @var string[]
*/
protected $_capabilities = array(
'canvas',
'circle',
'crop',
'dashedLine',
'flip',
'grayscale',
'line',
'mirror',
'multipage',
'pdf',
'polygon',
'polyline',
'rectangle',
'resize',
'rotate',
'roundedRectangle',
'sepia',
'text',
);
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
if (!Horde_Util::loadExtension('imagick')) {
throw new Horde_Image_Exception(
'Required PECL Imagick extension not found.'
);
}
parent::__construct($params, $context);
ini_set('imagick.locale_fix', 1);
$this->_imagick = new Imagick();
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif(!empty($params['data'])) {
$this->loadString($params['data']);
} else {
$this->_width = max(array($this->_width, 1));
$this->_height = max(array($this->_height, 1));
try {
$color = new ImagickPixel($this->_background);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->newImage(
$this->_width, $this->_height, $color
);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
try {
$this->_imagick->setImageFormat($this->_type);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Loads the image data from a string.
*
* @param string $image_data The data to use for the image.
*
* @throws Horde_Image_Exception
*/
public function loadString($image_data)
{
parent::loadString($image_data);
$this->_imagick->clear();
try {
$this->_data->rewind();
$this->_imagick->readImageFile($this->_data->stream);
$this->_imagick->setImageFormat($this->_type);
$this->_imagick->setIteratorIndex(0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->_data->close();
unset($this->_data);
}
/**
* Loads the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from.
*
* @throws Horde_Image_Exception
*/
public function loadFile($filename)
{
$this->reset();
try {
$this->_imagick->readImage($filename);
$this->_imagick->setImageFormat($this->_type);
$this->_imagick->setIteratorIndex(0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
// // Parent function loads image data into $this->_data
// parent::loadFile($filename);
// $this->loadString($this->_data);
}
/**
* Sets the output image type.
*
* @param string $type An image type (png, jpg, etc.)
*
* @return string The previous image type.
*/
public function setType($type)
{
$old = parent::setType($type);
try {
$this->_imagick->setImageFormat($this->_type);
} catch (ImagickException $e) {
// Don't care about an empty wand here.
}
return $old;
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert Ignored for Imagick driver.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return mixed The raw image data as a string or stream resource.
*/
public function raw($convert = false, $options = array())
{
try {
$this->_imagick->stripImage();
if (empty($options['stream'])) {
return $this->_imagick->getImageBlob();
}
$s = new Horde_Stream_Temp();
$s->add($this->_imagick->getImageBlob(), true);
return $s->stream;
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
parent::reset();
$this->_imagick->clear();
$this->_noMoreImages = false;
}
/**
* Returns the height and width of the current image data.
*
* @return array An hash with 'width' containing the width,
* 'height' containing the height of the image.
*/
public function getDimensions()
{
if ($this->_height == 0 && $this->_width == 0) {
try {
$size = $this->_imagick->getImageGeometry();
} catch (ImagickException $e) {
return array('width' => 0, 'height' => 0);
}
$this->_height = $size['height'];
$this->_width = $size['width'];
}
return array('width' => $this->_width, 'height' => $this->_height);
}
/**
* Resizes the current image.
*
* @param integer $width The new width.
* @param integer $height The new height.
* @param boolean $ratio Maintain original aspect ratio.
* @param boolean $keepProfile Keep the image meta data.
*/
public function resize($width, $height, $ratio = true, $keepProfile = false)
{
try {
if ($keepProfile) {
$this->_imagick->resizeImage($width, $height, $ratio);
} else {
$this->_imagick->thumbnailImage($width, $height, $ratio);
}
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* Crops the current image.
*
* @param integer $x1 x for the top left corner.
* @param integer $y1 y for the top left corner.
* @param integer $x2 x for the bottom right corner.
* @param integer $y2 y for the bottom right corner.
*/
public function crop($x1, $y1, $x2, $y2)
{
try {
$result = $this->_imagick->cropImage($x2 - $x1, $y2 - $y1, $x1, $y1);
$this->_imagick->setImagePage(0, 0, 0, 0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* Rotates the current image.
*
* @param integer $angle The angle to rotate the image by, in the
* clockwise direction.
* @param string $background The background color to fill any triangles.
*/
public function rotate($angle, $background = 'white')
{
try {
$this->_imagick->rotateImage($background, $angle);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* Flips the current image.
*/
public function flip()
{
try {
$this->_imagick->flipImage();
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Mirrors the current image.
*/
public function mirror()
{
try {
$this->_imagick->flopImage();
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Converts the current image to grayscale.
*/
public function grayscale()
{
try {
$this->_imagick->setImageType(Imagick::IMGTYPE_GRAYSCALE);
} catch (ImageException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Applies a sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
try {
$this->_imagick->sepiaToneImage($threshold);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @TODO: Need to differentiate between the stroke (border) and the fill
* color, but this is a BC break, since we were just not providing a
* border.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = '', $color = 'black', $direction = 0,
$fontsize = 'small'
)
{
$fontsize = Horde_Image::getFontSize($fontsize);
try {
$pixel = new ImagickPixel($color);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$draw = new ImagickDraw();
$draw->setFillColor($pixel);
if (!empty($font)) {
$draw->setFont($font);
}
$draw->setFontSize($fontsize);
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->annotateImage($draw, $x, $y, $direction, $string);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a circle.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = 'none')
{
try {
$draw = new ImagickDraw();
$draw->setFillColor(new ImagickPixel($fill));
$draw->setStrokeColor(new ImagickPixel($color));
$draw->circle($x, $y, $r + $x, $y);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = 'none')
{
try {
$draw = new ImagickDraw();
$draw->setFillColor(new ImagickPixel($fill));
$draw->setStrokeColor(new ImagickPixel($color));
$draw->polygon($verts);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle($x, $y, $width, $height, $color, $fill = 'none')
{
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setFillColor(new ImagickPixel($fill));
$draw->rectangle($x, $y, $x + $width, $y + $height);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rounded rectangle with.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color, $fill
)
{
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setFillColor(new ImagickPixel($fill));
$draw->roundRectangle($x, $y, $x + $width, $y + $height, $round, $round);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x0, $y0, $x1, $y1, $color = 'black', $width = 1)
{
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->line($x0, $y0, $x1, $y1);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x co-ordinate of the start.
* @param integer $y0 The y co-ordinate of the start.
* @param integer $x1 The x co-ordinate of the end.
* @param integer $y1 The y co-ordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line.
* @param integer $dash_space The length of a space in the dashed line.
*/
public function dashedLine(
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->setStrokeDashArray(array($dash_length, $dash_space));
$draw->line($x0, $y0, $x1, $y1);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImageException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->setFillColor(new ImagickPixel('none'));
$draw->polyline($verts);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draws an arc.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc (defaults to none).
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = 'none'
)
{
$points = Horde_Image::arcPoints($r, $start, $end);
$points['x1'] += $x;
$points['x2'] += $x;
$points['x3'] += $x;
$points['y1'] += $y;
$points['y2'] += $y;
$points['y3'] += $y;
try {
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setFillColor(new ImagickPixel($fill));
$draw->arc($x - $r, $y - $r, $x + $r, $y + $r, $start, $end);
} catch (ImagickDrawException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
}
// If filled, draw the outline.
if (!empty($fill)) {
$mid = round(($start + $end) / 2);
list($x1, $y1) = Horde_Image::circlePoint($start, $r * 2);
list($x2, $y2) = Horde_Image::circlePoint($mid, $r * 2);
list($x3, $y3) = Horde_Image::circlePoint($end, $r * 2);
$verts = array(
array('x' => $x + round($x3), 'y' => $y + round($y3)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x1), 'y' => $y + round($y1))
);
if ($mid > 90) {
$verts1 = array(
array('x' => $x + round($x2), 'y' => $y + round($y2)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x1), 'y' => $y + round($y1))
);
$verts2 = array(
array('x' => $x + round($x3), 'y' => $y + round($y3)),
array('x' => $x, 'y' => $y),
array('x' => $x + round($x2), 'y' => $y + round($y2))
);
$this->polygon($verts1, $fill, $fill);
$this->polygon($verts2, $fill, $fill);
} else {
$this->polygon($verts, $fill, $fill);
}
$this->polyline($verts, $color);
}
try {
$this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Applies any effects in the effect queue.
*/
public function applyEffects()
{
// noop for this driver.
}
/**
*/
public function __get($property)
{
switch ($property) {
case 'imagick':
return $this->_imagick;
}
}
/**
* Utility function to wrap Imagick::borderImage.
*
* Use when you don't want to replace all pixels in the clipping area with
* the border color i.e. you want to "frame" the existing image. Preserves
* transparency etc.
*
* @param Imagick &$image The Imagick object to border.
* @param string $color The border color.
* @param integer $width The image width including the border.
* @param integer $height The image height including the border.
*
* @todo Make non-static for H6.
*/
public static function frameImage(&$image, $color, $width, $height)
{
// Need to jump through these hoops in order to preserve any
// transparency.
try {
// @todo Use clone or $this->_cloneImagickObject().
$border = $image->clone();
$border->borderImage(new ImagickPixel($color), $width, $height);
$border->compositeImage($image, Imagick::COMPOSITE_COPY, $width, $height);
$image->clear();
$image->addImage($border);
} catch (ImagickPixelException $e) {
throw new Horde_Image_Exception($e);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$border->destroy();
}
/**
* Resets the Imagick iterator to the first image in the set.
*/
public function rewind()
{
$this->_logDebug('Horde_Image_Imagick#rewind');
$this->_imagick->setFirstIterator();
$this->_noMoreImages = false;
}
/**
* Returns the current image from the internal iterator.
*
* @return Horde_Image_Imagick
*/
public function current()
{
$this->_logDebug('Horde_Image_Imagick#current');
$params = array('data' => $this->raw(false, array('stream' => true)));
$image = new Horde_Image_Imagick($params, $this->_context);
return $image;
}
/**
* Returns the index of the internal iterator.
*
* @return integer
*/
public function key()
{
$this->_logDebug('Horde_Image_Imagick#key: ' . $this->_imagick->getIteratorIndex());
return $this->_imagick->getIteratorIndex();
}
/**
* Advances the iterator.
*
* @return Horde_Image_Imagick
*/
public function next()
{
if ($this->_imagick->hasNextImage()) {
$this->_imagick->nextImage();
return $this->current();
} else {
$this->_noMoreImages = true;
return false;
}
}
/**
* Deterimines if the current iterator item is valid.
*
* @return boolean
*/
public function valid()
{
$this->_logDebug('Horde_Image_Imagick#valid:' . print_r(!$this->_noMoreImages, true));
return !$this->_noMoreImages;
}
/**
* Returns a specific image from the pages of images.
*
* @param integer $index The index to return.
*
* @return Horde_Image_Imagick The requested image
*/
public function getImageAtIndex($index)
{
if ($index >= $this->_imagick->getNumberImages()) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
$currentIndex = $this->_imagick->getIteratorIndex();
$this->_imagick->setIteratorIndex($index);
$image = $this->current();
$this->_imagick->setIteratorIndex($currentIndex);
return $image;
}
/**
* Returns the number of image pages available in the image object.
*
* @return integer The number of images.
*/
public function getImagePageCount()
{
return $this->_imagick->getNumberImages();
}
/**
* Wrapper around cloning the imagick resource object.
*
* @param Imagick $imagick A imagick resource object to clone. If empty
* will clone the imagick object associated with
* this Horde_Imagice_Imagick object.
*
* @todo Remove in H6 when we can increase version dependency of Imagick.
*
* @return Imagick
* @since 2.4.0
*/
public function cloneImagickObject($imagick = null)
{
if (version_compare(phpversion('imagick'), '3.1.0') >= 0) {
return empty($imagick)
? clone $this->_imagick
: clone $imagick;
}
return empty($imagick)
? $this->_imagick->clone()
: $imagick->clone();
}
}
Horde_Image-2.5.2/lib/Horde/Image/Null.php 0000664 0001750 0001750 00000002126 13160240461 016241 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* It's a fallback to still be able to use API even if no image manipulation
* service is available.
*
* @author Jan Schneider
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Null extends Horde_Image_Base
{
/**
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif (!empty($params['data'])) {
$this->loadString($params['data']);
} else {
$this->_data = new Horde_Stream_Temp();
}
}
}
Horde_Image-2.5.2/lib/Horde/Image/Png.php 0000664 0001750 0001750 00000022113 13160240461 016051 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image API for PNG images.
*
* It mainly provides some utility functions, such as the ability to make
* pixels or solid images for now.
*
* @author Mike Cochrane
* @category Horde
* @copyright 2003-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Png extends Horde_Image_Base
{
/**
* The array of pixel data.
*
* @var array
*/
protected $_img = array();
/**
* Color depth (only 8 and 16 implemented).
*
* @var integer
*/
protected $_colorDepth = 8;
/**
* Color type (only 2 (true color) implemented).
*
* @var integer
*/
protected $_colorType = 2;
/**
* Compression method (0 is the only current valid value).
*
* @var integer
*/
protected $_compressionMethod = 0;
/**
* Filter method (0 is the only current valid value).
*
* @var integer
*/
protected $_filterMethod = 0;
/**
* Interlace method (only 0 (no interlace) implemented).
*
* @var integer
*/
protected $_interlaceMethod = 0;
/**
* PNG image constructor.
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['width'])) {
$this->rectangle(
0, 0, $params['width'], $params['height'],
$this->_background, $this->_background
);
}
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'image/png';
}
/**
* Returns the raw data for this image.
*
* @return string The raw image data.
*/
public function raw()
{
return $this->_header()
. $this->_IHDR()
/* Say what created the image file. */
. $this->_tEXt('Software', 'Horde_Image_Png')
/* Set the last modified date/time. */
. $this->_tIME()
. $this->_IDAT()
. $this->_IEND();
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
parent::reset();
$this->_img = array();
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle(
$x, $y, $width, $height, $color = 'black', $fill = 'none'
)
{
list($r, $g, $b) = Horde_Image::getRGB($color);
if ($fill != 'none') {
list($fR, $fG, $fB) = Horde_Image::getRGB($fill);
}
$x2 = $x + $width;
$y2 = $y + $height;
for ($h = $y; $h <= $y2; $h++) {
for ($w = $x; $w <= $x2; $w++) {
// See if we're on an edge.
if ($w == $x || $h == $y || $w == $x2 || $h == $y2) {
$this->_img[$h][$w] = array('r' => $r, 'g' => $g, 'b' => $b);
} elseif ($fill != 'none') {
$this->_img[$h][$w] = array('r' => $fR, 'g' => $fG, 'b' => $fB);
}
}
}
}
/**
* Creates the PNG file header.
*/
protected function _header()
{
return pack('CCCCCCCC', 137, 80, 78, 71, 13, 10, 26, 10);
}
/**
* Creates the IHDR block.
*/
protected function _IHDR()
{
$data = pack(
'a4NNCCCCC',
'IHDR',
$this->_width,
$this->_height,
$this->_colorDepth,
$this->_colorType,
$this->_compressionMethod,
$this->_filterMethod,
$this->_interlaceMethod
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the IEND block.
*/
protected function _IEND()
{
$data = 'IEND';
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the IDAT block.
*/
protected function _IDAT()
{
$data = '';
$prevscanline = null;
$filter = 0;
for ($i = 0; $i < $this->_height; $i++) {
$scanline = array();
$data .= chr($filter);
for ($j = 0; $j < $this->_width; $j++) {
if ($this->_colorDepth == 8) {
$scanline[$j] = pack(
'CCC',
$this->_img[$i][$j]['r'],
$this->_img[$i][$j]['g'],
$this->_img[$i][$j]['b']
);
} elseif ($this->_colorDepth == 16) {
$scanline[$j] = pack(
'nnn',
$this->_img[$i][$j]['r'] << 8,
$this->_img[$i][$j]['g'] << 8,
$this->_img[$i][$j]['b'] << 8
);
}
if ($filter == 0) {
/* No Filter. */
$data .= $scanline[$j];
} elseif ($filter == 2) {
/* Up Filter. */
$pixel = $scanline[$j] - $prevscanline[$j];
if ($this->_colorDepth == 8) {
$data .= pack(
'CCC',
$pixel >> 16,
($pixel >> 8) & 0xFF,
$pixel & 0xFF
);
} elseif ($this->_colorDepth == 16) {
$data .= pack(
'nnn',
($pixel >> 32),
($pixel >> 16) & 0xFFFF,
$pixel & 0xFFFF
);
}
}
}
$prevscanline = $scanline;
}
$compressed = gzdeflate($data, 9);
$data = 'IDAT'
. pack(
'CCa' . strlen($compressed) . 'a4',
0x78,
0x01,
$compressed,
$this->_Adler32($data)
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the tEXt block.
*/
protected function _tEXt($keyword, $text)
{
$data = 'tEXt' . $keyword . "\0" . $text;
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the tIME block.
*
* @param integer $date A timestamp.
*/
protected function _tIME($date = null)
{
if (is_null($date)) {
$date = time();
}
$data = 'tIME'
. pack(
'nCCCCC',
intval(date('Y', $date)),
intval(date('m', $date)),
intval(date('j', $date)),
intval(date('G', $date)),
intval(date('i', $date)),
intval(date('s', $date))
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Calculates an Adler32 checksum for a string.
*/
protected function _Adler32($input)
{
$s1 = 1;
$s2 = 0;
$iMax = strlen($input);
for ($i = 0; $i < $iMax; $i++) {
$s1 = ($s1 + ord($input[$i])) % 0xFFF1;
$s2 = ($s2 + $s1) % 0xFFF1;
}
return pack('N', (($s2 << 16) | $s1));
}
/**
* Requests a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Png
* @throws Horde_Image_Exception
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
return clone($this);
}
/**
* Returns the number of image pages available in the image object.
*
* @return integer The number of images.
*/
public function getImagePageCount()
{
return 1;
}
}
Horde_Image-2.5.2/lib/Horde/Image/Rgb.php 0000664 0001750 0001750 00000015346 13160240461 016051 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Horde_Image_Rgb contains mappings of HTML color names to RGB values.
*
* @author Jan Schneider
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Rgb
{
public static $colors = array(
'aliceblue' => array(240, 248, 255),
'antiquewhite' => array(250, 235, 215),
'aqua' => array(0, 255, 255),
'aquamarine' => array(127, 255, 212),
'azure' => array(240, 255, 255),
'beige' => array(245, 245, 220),
'bisque' => array(255, 228, 196),
'black' => array(0, 0, 0),
'blanchedalmond' => array(255, 235, 205),
'blue' => array(0, 0, 255),
'blueviolet' => array(138, 43, 226),
'brown' => array(165, 42, 42),
'burlywood' => array(222, 184, 135),
'cadetblue' => array(95, 158, 160),
'chartreuse' => array(127, 255, 0),
'chocolate' => array(210, 105, 30),
'coral' => array(255, 127, 80),
'cornflowerblue' => array(100, 149, 237),
'cornsilk' => array(255, 248, 220),
'crimson' => array(220, 20, 60),
'cyan' => array(0, 255, 255),
'darkblue' => array(0, 0, 139),
'darkcyan' => array(0, 139, 139),
'darkgoldenrod' => array(184, 134, 11),
'darkgray' => array(169, 169, 169),
'darkgreen' => array(0, 100, 0),
'darkkhaki' => array(189, 183, 107),
'darkmagenta' => array(139, 0, 139),
'darkolivegreen' => array(85, 107, 47),
'darkorange' => array(255, 140, 0),
'darkorchid' => array(153, 50, 204),
'darkred' => array(139, 0, 0),
'darksalmon' => array(233, 150, 122),
'darkseagreen' => array(143, 188, 143),
'darkslateblue' => array(72, 61, 139),
'darkslategray' => array(47, 79, 79),
'darkturquoise' => array(0, 206, 209),
'darkviolet' => array(148, 0, 211),
'deeppink' => array(255, 20, 147),
'deepskyblue' => array(0, 191, 255),
'dimgray' => array(105, 105, 105),
'dodgerblue' => array(30, 144, 255),
'firebrick' => array(178, 34, 34),
'floralwhite' => array(255, 250, 240),
'forestgreen' => array(34, 139, 34),
'fuchsia' => array(255, 0, 255),
'gainsboro' => array(220, 220, 220),
'ghostwhite' => array(248, 248, 255),
'gold' => array(255, 215, 0),
'goldenrod' => array(218, 165, 32),
'gray' => array(128, 128, 128),
'green' => array(0, 128, 0),
'greenyellow' => array(173, 255, 47),
'honeydew' => array(240, 255, 240),
'hotpink' => array(255, 105, 180),
'indianred ' => array(205, 92, 92),
'indigo ' => array(75, 0, 130),
'ivory' => array(255, 255, 240),
'khaki' => array(240, 230, 140),
'lavender' => array(230, 230, 250),
'lavenderblush' => array(255, 240, 245),
'lawngreen' => array(124, 252, 0),
'lemonchiffon' => array(255, 250, 205),
'lightblue' => array(173, 216, 230),
'lightcoral' => array(240, 128, 128),
'lightcyan' => array(224, 255, 255),
'lightgoldenrodyellow' => array(250, 250, 210),
'lightgray' => array(211, 211, 211),
'lightgreen' => array(144, 238, 144),
'lightpink' => array(255, 182, 193),
'lightsalmon' => array(255, 160, 122),
'lightseagreen' => array(32, 178, 170),
'lightskyblue' => array(135, 206, 250),
'lightslategray' => array(119, 136, 153),
'lightsteelblue' => array(176, 196, 222),
'lightyellow' => array(255, 255, 224),
'lime' => array(0, 255, 0),
'limegreen' => array(50, 205, 50),
'linen' => array(250, 240, 230),
'magenta' => array(255, 0, 255),
'maroon' => array(128, 0, 0),
'mediumaquamarine' => array(102, 205, 170),
'mediumblue' => array(0, 0, 205),
'mediumorchid' => array(186, 85, 211),
'mediumpurple' => array(147, 112, 219),
'mediumseagreen' => array(60, 179, 113),
'mediumslateblue' => array(123, 104, 238),
'mediumspringgreen' => array(0, 250, 154),
'mediumturquoise' => array(72, 209, 204),
'mediumvioletred' => array(199, 21, 133),
'midnightblue' => array(25, 25, 112),
'mintcream' => array(245, 255, 250),
'mistyrose' => array(255, 228, 225),
'moccasin' => array(255, 228, 181),
'navajowhite' => array(255, 222, 173),
'navy' => array(0, 0, 128),
'oldlace' => array(253, 245, 230),
'olive' => array(128, 128, 0),
'olivedrab' => array(107, 142, 35),
'orange' => array(255, 165, 0),
'orangered' => array(255, 69, 0),
'orchid' => array(218, 112, 214),
'palegoldenrod' => array(238, 232, 170),
'palegreen' => array(152, 251, 152),
'paleturquoise' => array(175, 238, 238),
'palevioletred' => array(219, 112, 147),
'papayawhip' => array(255, 239, 213),
'peachpuff' => array(255, 218, 185),
'peru' => array(205, 133, 63),
'pink' => array(255, 192, 203),
'plum' => array(221, 160, 221),
'powderblue' => array(176, 224, 230),
'purple' => array(128, 0, 128),
'rebeccapurple' => array(102, 51, 153),
'red' => array(255, 0, 0),
'rosybrown' => array(188, 143, 143),
'royalblue' => array(65, 105, 225),
'saddlebrown' => array(139, 69, 19),
'salmon' => array(250, 128, 114),
'sandybrown' => array(244, 164, 96),
'seagreen' => array(46, 139, 87),
'seashell' => array(255, 245, 238),
'sienna' => array(160, 82, 45),
'silver' => array(192, 192, 192),
'skyblue' => array(135, 206, 235),
'slateblue' => array(106, 90, 205),
'slategray' => array(112, 128, 144),
'snow' => array(255, 250, 250),
'springgreen' => array(0, 255, 127),
'steelblue' => array(70, 130, 180),
'tan' => array(210, 180, 140),
'teal' => array(0, 128, 128),
'thistle' => array(216, 191, 216),
'tomato' => array(255, 99, 71),
'turquoise' => array(64, 224, 208),
'violet' => array(238, 130, 238),
'wheat' => array(245, 222, 179),
'white' => array(255, 255, 255),
'whitesmoke' => array(245, 245, 245),
'yellow' => array(255, 255, 0),
'yellowgreen' => array(154, 205, 50),
);
}
Horde_Image-2.5.2/lib/Horde/Image/Svg.php 0000664 0001750 0001750 00000033112 13160240461 016065 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image:: API for SVG.
*
* @author Chuck Hagenbuch
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*
* Requires PEAR XML_SVG
*/
class Horde_Image_Svg extends Horde_Image_Base
{
/**
* SVG document handle.
*
* @var XML_SVG
*/
protected $_svg;
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array(
'canvas',
'circle',
'dashedLine',
'line',
'polygon',
'polyline',
'rectangle',
'roundedRectangle',
'text',
);
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
$this->_svg = new XML_SVG_Document(array(
'width' => $this->_width,
'height' => $this->_height,
));
if ($this->_background != 'none') {
$this->rectangle(
0, 0,
$this->_width, $this->_height,
$this->_background, $this->_background
);
}
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'image/svg+xml';
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$this->_svg->printElement();
}
/**
* Return raw image data.
*
* @param boolean $convert Unused in SVG driver.
* @param array $options Array of options:
* NONE USED in SVG driver.
*
* @return string The raw image data.
*/
public function raw($convert = false, $options = array())
{
return $this->_svg->bufferObject();
}
private function _createSymbol($s, $id)
{
$s->setParam('id', $id);
$defs = new XML_SVG_Defs();
$defs->addChild($s);
$this->_svg->addChild($defs);
}
private function _createDropShadow($id = 'dropShadow')
{
$defs = new XML_SVG_Defs();
$filter = new XML_SVG_Filter(array('id' => $id));
$filter->addPrimitive(
'GaussianBlur',
array(
'in' => 'SourceAlpha',
'stdDeviation' => 2,
'result' => 'blur'
)
);
$filter->addPrimitive(
'Offset',
array(
'in' => 'blur',
'dx' => 4,
'dy' => 4,
'result' => 'offsetBlur'
)
);
$merge = new XML_SVG_FilterPrimitive('Merge');
$merge->addMergeNode('offsetBlur');
$merge->addMergeNode('SourceGraphic');
$filter->addChild($merge);
$defs->addChild($filter);
$this->_svg->addChild($defs);
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0
)
{
$height = 12;
$style = 'font-family:' . $font . ';font-height:' . $height
. 'px;fill:' . Horde_Image::getHexColor($color) . ';text-anchor:start;';
$transform = 'rotate(' . $direction . ',' . $x . ',' . $y . ')';
$this->_svg->addChild(new XML_SVG_Text(array(
'text' => $string,
'x' => (int)$x,
'y' => (int)$y + $height,
'transform' => $transform,
'style' => $style
)));
}
/**
* Draws a circle.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = null)
{
if (!empty($fill)) {
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Circle(array(
'cx' => $x,
'cy' => $y,
'r' => $r,
'style' => $style
)));
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = null)
{
if (!empty($fill)) {
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
$points = '';
foreach ($verts as $v) {
$points .= $v['x'] . ',' . $v['y'] . ' ';
}
$points = trim($points);
$this->_svg->addChild(new XML_SVG_Polygon(array(
'points' => $points,
'style' => $style
)));
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle($x, $y, $width, $height, $color, $fill = null)
{
if (!empty($fill) && $fill != 'none') {
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Rect(array(
'x' => $x,
'y' => $y,
'width' => $width,
'height' => $height,
'style' => $style
)));
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rounded rectangle with.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color, $fill
)
{
if (!empty($fill)) {
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Rect(
array('x' => $x,
'y' => $y,
'rx' => $round,
'ry' => $round,
'width' => $width,
'height' => $height,
'style' => $style
)));
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$style = 'stroke:' . Horde_Image::getHexColor($color)
. '; stroke-width:' . (int)$width;
$this->_svg->addChild(new XML_SVG_Line(array(
'x1' => $x1,
'y1' => $y1,
'x2' => $x2,
'y2' => $y2,
'style' => $style
)));
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line
* @param integer $dash_space The length of a space in the dashed line
*/
public function dashedLine(
$x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
$style = 'stroke:' . Horde_Image::getHexColor($color)
. '; stroke-width:' . (int)$width
. '; stroke-dasharray:' . $dash_length . ',' . $dash_space . ';';
$this->_svg->addChild(new XML_SVG_Line(array(
'x1' => $x1,
'y1' => $y1,
'x2' => $x2,
'y2' => $y2,
'style' => $style
)));
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
$style = 'stroke:' . Horde_Image::getHexColor($color)
. '; stroke-width:' . $width . ';fill:none;';
// Calculate the path entry.
$path = '';
$first = true;
foreach ($verts as $vert) {
if ($first) {
$first = false;
$path .= 'M ' . $vert['x'] . ',' . $vert['y'];
} else {
$path .= ' L ' . $vert['x'] . ',' . $vert['y'];
}
}
$this->_svg->addChild(new XML_SVG_Path(array(
'd' => $path,
'style' => $style
)));
}
/**
* Draws an arc.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc (defaults to none).
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = null
)
{
if (!empty($fill)) {
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
$mid = round(($start + $end) / 2);
// Calculate the path entry.
$path = '';
// If filled, draw the outline.
if (!empty($fill)) {
// Start at the center of the ellipse the arc is on.
$path .= "M $x,$y ";
// Draw out to ellipse edge.
list($arcX, $arcY) = Horde_Image::circlePoint($start, $r * 2);
$path .= 'L ' . round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
}
// Draw arcs.
list($arcX, $arcY) = Horde_Image::circlePoint($mid, $r * 2);
$path .= "A $r,$r 0 0 1 " .
round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
list($arcX, $arcY) = Horde_Image::circlePoint($end, $r * 2);
$path .= "A $r,$r 0 0 1 " .
round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
// If filled, close the outline.
if (!empty($fill)) {
$path .= 'Z';
}
$path = trim($path);
$this->_svg->addChild(new XML_SVG_Path(array(
'd' => $path,
'style' => $style
)));
}
} Horde_Image-2.5.2/lib/Horde/Image/Swf.php 0000664 0001750 0001750 00000043274 13160240461 016077 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image API for SWF, using the PHP Ming
* extension.
*
* @author Chuck Hagenbuch
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Swf extends Horde_Image_Base
{
/**
* Capabilites of this driver.
*
* @var string[]
*/
protected $_capabilities = array(
'canvas',
'circle',
'dashedLine',
'line',
'polygon',
'polyline',
'rectangle',
'roundedRectangle',
'text',
);
/**
* SWF root movie.
*
* @var resource
*/
protected $_movie;
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
$this->_movie = new SWFMovie();
$this->_movie->setDimension($this->_width, $this->_height);
$color = Horde_Image::getRGB($this->_background);
$this->_movie->setBackground($color[0], $color[1], $color[2]);
$this->_movie->setRate(30);
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'application/x-shockwave-flash';
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$this->_movie->output();
}
/**
* Returns the raw data for this image.
*
* @return string The raw image data.
*/
public function raw()
{
ob_start();
$this->_movie->output();
$data = ob_get_clean();
return $data;
}
/**
* Creates a color that can be accessed in this object.
*
* When a color is set, the rgba values are returned in an array.
*
* @param string $name The name of the color.
*
* @return array The red, green, blue, alpha values of the color.
*/
public function allocateColor($name)
{
list($r, $g, $b) = Horde_Image::getRGB($name);
return array('red' => $r, 'green' => $g, 'blue' => $b, 'alpha' => 255);
}
/**
* Translates font names.
*
* @param string $font A font name.
*
* @return string An SWF font name.
*/
public function getFont($font)
{
switch ($font) {
case 'sans-serif':
return '_sans';
case 'serif':
return '_serif';
case 'monospace':
return '_typewriter';
default:
return $font;
}
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0
)
{
$color = $this->allocateColor($color);
$text = new SWFTextField(SWFTEXTFIELD_NOEDIT);
$text->setColor(
$color['red'], $color['green'], $color['blue'], $color['alpha']
);
$text->setFont(new SWFBrowserFont($this->getFont($font)));
$text->addString($string);
$t = $this->_movie->add($text);
$t->moveTo($x, $y);
$t->rotate($direction);
}
/**
* Draws a circle.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = 'none')
{
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(
1, $color['red'], $color['green'], $color['blue'], $color['alpha']
);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $s->addFill(
$fillColor['red'],
$fillColor['green'],
$fillColor['blue'],
$fillColor['alpha']
);
$s->setRightFill($f);
}
$a = $r * 0.414213562; // = tan(22.5 deg)
$b = $r * 0.707106781; // = sqrt(2)/2 = sin(45 deg)
$s->movePenTo($x + $r, $y);
$s->drawCurveTo($x + $r, $y - $a, $x + $b, $y - $b);
$s->drawCurveTo($x + $a, $y - $r, $x, $y - $r);
$s->drawCurveTo($x - $a, $y - $r, $x - $b, $y - $b);
$s->drawCurveTo($x - $r, $y - $a, $x - $r, $y);
$s->drawCurveTo($x - $r, $y + $a, $x - $b, $y + $b);
$s->drawCurveTo($x - $a, $y + $r, $x, $y + $r);
$s->drawCurveTo($x + $a, $y + $r, $x + $b, $y + $b);
$s->drawCurveTo($x + $r, $y + $a, $x + $r, $y);
$this->_movie->add($s);
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = 'none')
{
$color = $this->allocateColor($color);
if (!is_array($color) || !is_array($verts) || (sizeof($verts) <= 2)) {
return;
}
$shape = new SWFShape();
$shape->setLine(
1, $color['red'], $color['green'], $color['blue'], $color['alpha']
);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $shape->addFill(
$fillColor['red'],
$fillColor['green'],
$fillColor['blue'],
$fillColor['alpha']
);
$shape->setRightFill($f);
}
$first_done = false;
foreach ($verts as $value) {
if (!$first_done) {
$shape->movePenTo($value['x'], $value['y']);
$first_done = true;
$first_x = $value['x'];
$first_y = $value['y'];
}
$shape->drawLineTo($value['x'], $value['y']);
}
$shape->drawLineTo($first_x, $first_y);
$this->_movie->add($shape);
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle($x, $y, $width, $height, $color, $fill = 'none')
{
$verts[0] = array('x' => $x, 'y' => $y);
$verts[1] = array('x' => $x + $width, 'y' => $y);
$verts[2] = array('x' => $x + $width, 'y' => $y + $height);
$verts[3] = array('x' => $x, 'y' => $y + $height);
$this->polygon($verts, $color, $fill);
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color = 'black', $fill = 'none'
)
{
if ($round <= 0) {
// Optimize out any calls with no corner rounding.
return $this->rectangle($x, $y, $width, $height, $color, $fill);
}
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(
1, $color['red'], $color['green'], $color['blue'], $color['alpha']
);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $s->addFill(
$fillColor['red'],
$fillColor['green'],
$fillColor['blue'],
$fillColor['alpha']
);
$s->setRightFill($f);
}
// Set corner points to avoid lots of redundant math.
$x1 = $x + $round;
$y1 = $y + $round;
$x2 = $x + $width - $round;
$y2 = $y + $round;
$x3 = $x + $width - $round;
$y3 = $y + $height - $round;
$x4 = $x + $round;
$y4 = $y + $height - $round;
// Draw the upper left corner.
$s->movePenTo($x1, $y2);
$s->drawArc($round, 270, 360);
// Connect the top left and right curves.
$s->movePenTo($x1, $y);
$s->drawLineTo($x2, $y);
// Draw the upper right corner.
$s->movePenTo($x2, $y2);
$s->drawArc($round, 0, 90);
// Connect the top right and lower right curves.
$s->movePenTo($x + $width, $y2);
$s->drawLineTo($x + $width, $y3);
// Draw the lower right corner.
$s->movePenTo($x3, $y3);
$s->drawArc($round, 90, 180);
// Connect the bottom right and bottom left curves.
$s->movePenTo($x3, $y + $height);
$s->drawLineTo($x4, $y + $height);
// Draw the lower left corner.
$s->movePenTo($x4, $y4);
$s->drawArc($round, 180, 270);
// Connect the bottom left and top left curves.
$s->movePenTo($x, $y4);
$s->drawLineTo($x, $y1);
$this->_movie->add($s);
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$color = $this->allocateColor($color);
if (!is_array($color)) {
return;
}
$shape = new SWFShape();
$shape->setLine(
$width,
$color['red'],
$color['green'],
$color['blue'],
$color['alpha']
);
$shape->movePenTo($x1, $y1);
$shape->drawLineTo($x2, $y2);
$this->_movie->add($shape);
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x co-ordinate of the start.
* @param integer $y0 The y co-ordinate of the start.
* @param integer $x1 The x co-ordinate of the end.
* @param integer $y1 The y co-ordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line.
* @param integer $dash_space The length of a space in the dashed line.
*/
public function dashedLine(
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
// Get the length of the line in pixels.
$line_length = max(
ceil(sqrt(pow(($x1 - $x0), 2) + pow(($y1 - $y0), 2))),
2
);
$cosTheta = ($x1 - $x0) / $line_length;
$sinTheta = ($y1 - $y0) / $line_length;
$lastx = $x0;
$lasty = $y0;
// Draw the dashed line.
for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
$x = ($dash_length * $cosTheta) + $lastx;
$y = ($dash_length * $sinTheta) + $lasty;
$this->line($lastx, $lasty, $x, $y, $color);
$lastx = $x + ($dash_space * $cosTheta);
$lasty = $y + ($dash_space * $sinTheta);
}
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
$color = $this->allocateColor($color);
$shape = new SWFShape();
$shape->setLine(
$width,
$color['red'],
$color['green'],
$color['blue'],
$color['alpha']
);
$first_done = false;
foreach ($verts as $value) {
if (!$first_done) {
$shape->movePenTo($value['x'], $value['y']);
$first_done = true;
}
$shape->drawLineTo($value['x'], $value['y']);
}
$this->_movie->add($shape);
}
/**
* Draws an arc.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc.
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = 'none'
)
{
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(
1, $color['red'], $color['green'], $color['blue'], $color['alpha']
);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$s->setRightFill(
$fillColor['red'],
$fillColor['green'],
$fillColor['blue'],
$fillColor['alpha']
);
}
$pts = Horde_Image::arcPoints($r, $start, $end);
$s->movePenTo($x, $y);
$s->drawArc($r, $start + 90, $end + 90);
$s->movePenTo($x, $y);
$s->drawLineTo(round($pts['x1']) + $x, round($pts['y1']) + $y);
$s->movePenTo($x, $y);
$s->drawLineTo(round($pts['x2']) + $x, round($pts['y2']) + $y);
$this->_movie->add($s);
}
/**
* Draws a rectangle filled with a gradient.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The outline color of the rectangle.
* @param string $fill1 The name of the start color for the gradient.
* @param string $fill2 The name of the end color for the gradient.
*/
public function gradientRectangle(
$x, $y, $width, $height, $color = 'black',
$fill1 = 'black', $fill2 = 'white'
)
{
$s = new SWFShape();
if ($color != 'none') {
$color = $this->allocateColor($color);
$s->setLine(
1,
$color['red'],
$color['green'],
$color['blue'],
$color['alpha']
);
}
$fill1 = $this->allocateColor($fill1);
$fill2 = $this->allocateColor($fill2);
$gradient = new SWFGradient();
$gradient->addEntry(
0.0,
$fill1['red'],
$fill1['green'],
$fill1['blue'],
$fill1['alpha']
);
$gradient->addEntry(
1.0,
$fill2['red'],
$fill2['green'],
$fill2['blue'],
$fill2['alpha']
);
$f = $s->addFill($gradient, SWFFILL_LINEAR_GRADIENT);
$f->scaleTo($width / $this->_width);
$f->moveTo($x, $y);
$s->setRightFill($f);
$verts[0] = array('x' => $x, 'y' => $y);
$verts[1] = array('x' => $x + $width, 'y' => $y);
$verts[2] = array('x' => $x + $width, 'y' => $y + $height);
$verts[3] = array('x' => $x, 'y' => $y + $height);
$first_done = false;
foreach ($verts as $vert) {
if (!$first_done) {
$s->movePenTo($vert['x'], $vert['y']);
$first_done = true;
$first_x = $vert['x'];
$first_y = $vert['y'];
}
$s->drawLineTo($vert['x'], $vert['y']);
}
$s->drawLineTo($first_x, $first_y);
$this->_movie->add($s);
}
}
Horde_Image-2.5.2/lib/Horde/Image/Translation.php 0000664 0001750 0001750 00000002033 13160240461 017622 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* Horde_Image_Translation is the translation wrapper class for Horde_Image.
* Imagick driver for the Horde_Image API.
*
* @author Jan Schneider
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Translation extends Horde_Translation_Autodetect
{
/**
* The translation domain
*
* @var string
*/
protected static $_domain = 'Horde_Image';
/**
* The absolute PEAR path to the translations for the default gettext
* handler.
*
* @var string
*/
protected static $_pearDirectory = '@data_dir@';
}
Horde_Image-2.5.2/lib/Horde/Image.php 0000664 0001750 0001750 00000020733 13160240461 015333 0 ustar jan jan
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class provides some utility functions, such as generating highlights
* of a color.
*
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @author Jan Schneider
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image
{
/**
* Calculates a lighter (or darker) version of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
* @param integer $factor The brightness difference between -0xff and
* +0xff. Plus values raise the brightness,
* negative values reduce it.
*
* @return string A modified HTML color.
*/
public static function modifyColor($color, $factor = 0x11)
{
list($r, $g, $b) = self::getColor($color);
$r = min(max($r + $factor, 0), 255);
$g = min(max($g + $factor, 0), 255);
$b = min(max($b + $factor, 0), 255);
return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT)
. str_pad(dechex($g), 2, '0', STR_PAD_LEFT)
. str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
}
/**
* Calculates a more intense version of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
* @param integer $factor The intensity difference between -0xff and
* +0xff. Plus values raise the intensity,
* negative values reduce it.
*
* @return string A more intense HTML color.
*/
public static function moreIntenseColor($color, $factor = 0x11)
{
list($r, $g, $b) = self::getColor($color);
if ($r >= $g && $r >= $b) {
$g = $g / $r;
$b = $b / $r;
$r += $factor;
$g = floor($g * $r);
$b = floor($b * $r);
} elseif ($g >= $r && $g >= $b) {
$r = $r / $g;
$b = $b / $g;
$g += $factor;
$r = floor($r * $g);
$b = floor($b * $g);
} else {
$r = $r / $b;
$g = $g / $b;
$b += $factor;
$r = floor($r * $b);
$g = floor($g * $b);
}
$r = min(max($r, 0), 255);
$g = min(max($g, 0), 255);
$b = min(max($b, 0), 255);
return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT)
. str_pad(dechex($g), 2, '0', STR_PAD_LEFT)
. str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
}
/**
* Returns the brightness of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
*
* @return integer The brightness on a scale of 0 to 255.
*/
public static function brightness($color)
{
list($r, $g, $b) = self::getColor($color);
return round((($r * 299) + ($g * 587) + ($b * 114)) / 1000);
}
/**
* Calculates the grayscale value of a color.
*
* @param integer $r A red value.
* @param integer $g A green value.
* @param integer $b A blue value.
*
* @return integer The grayscale value of the color.
*/
public static function grayscaleValue($r, $g, $b)
{
return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
}
/**
* Turns an RGB value into grayscale.
*
* @param integer[] $originalPixel A hash with 'red', 'green', and 'blue'
* values.
*
* @return integer[] A hash with 'red', 'green', and 'blue' values for the
* corresponding gray color.
*/
public static function grayscalePixel($originalPixel)
{
$gray = Horde_Image::grayscaleValue(
$originalPixel['red'],
$originalPixel['green'],
$originalPixel['blue']
);
return array('red' => $gray, 'green' => $gray, 'blue' => $gray);
}
/**
* Normalizes an HTML color.
*
* @param string $color An HTML color, e.g.: #ffffcc or #ffc.
*
* @return integer[] Array with three elements: red, green, and blue.
*/
public static function getColor($color)
{
if ($color[0] == '#') {
$color = substr($color, 1);
}
if (strlen($color) == 3) {
$color = str_repeat($color[0], 2) .
str_repeat($color[1], 2) .
str_repeat($color[2], 2);
}
return array(
hexdec(substr($color, 0, 2)),
hexdec(substr($color, 2, 2)),
hexdec(substr($color, 4, 2))
);
}
/**
* Returns the RGB values for an HTML color name.
*
* @param string $colorname A color name.
*
* @return array An array of RGB values.
*/
public static function getRGB($colorname)
{
return isset(Horde_Image_Rgb::$colors[$colorname]) ?
Horde_Image_Rgb::$colors[$colorname] :
array(0, 0, 0);
}
/**
* Returns the hexadecimal representation of an HTML color name.
*
* @param string $colorname A color name.
*
* @return string The hex representation of the color.
*/
public static function getHexColor($colorname)
{
list($r, $g, $b) = self::getRGB($colorname);
return '#' . str_pad(dechex(min($r, 255)), 2, '0', STR_PAD_LEFT)
. str_pad(dechex(min($g, 255)), 2, '0', STR_PAD_LEFT)
. str_pad(dechex(min($b, 255)), 2, '0', STR_PAD_LEFT);
}
/**
* Returns an x,y pair on circle, assuming center is 0,0.
*
* @param float $degrees The degrees of arc to get the point for.
* @param integer $diameter The diameter of the circle.
*
* @return array (x coordinate, y coordinate) of the point.
*/
public static function circlePoint($degrees, $diameter)
{
// Avoid problems with floats.
$degrees += 0.0001;
return array(cos(deg2rad($degrees)) * ($diameter / 2),
sin(deg2rad($degrees)) * ($diameter / 2));
}
/**
* Returns point coordinates at the limits of an arc.
*
* @param integer $r The radius of the arc.
* @param integer $start The starting angle.
* @param integer $end The ending angle.
*
* @return array The start point (x1,y1), end point (x2,y2), and anchor
* point (x3,y3).
*/
public static function arcPoints($r, $start, $end)
{
// Start point.
$pts['x1'] = $r * cos(deg2rad($start));
$pts['y1'] = $r * sin(deg2rad($start));
// End point.
$pts['x2'] = $r * cos(deg2rad($end));
$pts['y2'] = $r * sin(deg2rad($end));
// Anchor point.
$pts['x3'] = $pts['y3'] = 0;
// Shift to positive.
if ($pts['x1'] < 0) {
$pts['x2'] += abs($pts['x1']);
$pts['x3'] += abs($pts['x1']);
$pts['x1'] = 0;
}
if ($pts['x2'] < 0) {
$pts['x1'] += abs($pts['x2']);
$pts['x3'] += abs($pts['x2']);
$pts['x2'] = 0;
}
if ($pts['x3'] < 0) {
$pts['x1'] += abs($pts['x3']);
$pts['x2'] += abs($pts['x3']);
$pts['x3'] = 0;
}
if ($pts['y1'] < 0) {
$pts['y2'] += abs($pts['y1']);
$pts['y3'] += abs($pts['y1']);
$pts['y1'] = 0;
}
if ($pts['y2'] < 0) {
$pts['y1'] += abs($pts['y2']);
$pts['y3'] += abs($pts['y2']);
$pts['y2'] = 0;
}
if ($pts['y3'] < 0) {
$pts['y1'] += abs($pts['y3']);
$pts['y2'] += abs($pts['y3']);
$pts['y3'] = 0;
}
return $pts;
}
/**
* Returns the point size for an HTML font size name.
*/
public static function getFontSize($fontsize)
{
switch ($fontsize) {
case 'medium':
return 18;
case 'large':
return 24;
case 'giant':
return 30;
default:
return 12;
}
}
}
Horde_Image-2.5.2/locale/ar/LC_MESSAGES/Horde_Image.mo 0000664 0001750 0001750 00000000732 13160240461 020114 0 ustar jan jan <