FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,22 @@
name = File<strike>Field</strike> Paths
description = Adds improved Token based file sorting and renaming functionalities.
dependencies[] = token
package = Fields
core = 7.x
files[] = filefield_paths.install
files[] = filefield_paths.module
files[] = modules/file.inc
files[] = modules/filefield_paths.inc
files[] = modules/image.inc
files[] = modules/token.inc
;files[] = tests/filefield_paths.test
; Information added by drupal.org packaging script on 2011-11-14
version = "7.x-1.x-dev"
core = "7.x"
project = "filefield_paths"
datestamp = "1321229825"

View File

@@ -0,0 +1,194 @@
<?php
/**
* @file
* Install, update and uninstall functions for the FileField Paths module.
*/
/**
* Implements hook_schema().
*/
function filefield_paths_schema() {
$schema['filefield_paths'] = array(
'fields' => array(
'type' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => ''
),
'field' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => ''
),
'filename' => array(
'type' => 'text',
'size' => 'medium',
'not null' => TRUE,
'serialize' => TRUE
),
'filepath' => array(
'type' => 'text',
'size' => 'medium',
'not null' => TRUE,
'serialize' => TRUE
),
'active_updating' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0
),
),
'unique keys' => array(
'type_field' => array('type', 'field'),
),
);
return $schema;
}
/**
* Implements hook_schema_alter().
*
* @param $schema
* The system-wide schema
*/
function filefield_paths_schema_alter(&$schema) {
$schema['file_managed']['fields']['origname'] = array(
'description' => 'Original name of the file.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
);
}
/**
* Implements hook_install().
*/
function filefield_paths_install() {
// Add origname field to {file_managed}, and populate with the current
// filenames.
db_add_field('file_managed', 'origname', array(
'description' => 'Original name of the file with no path components. Used by the filefield_paths module.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
));
db_update('file_managed')
->expression('origname', 'filename')
->execute();
}
/**
* Implements hook_uninstall().
*/
function filefield_paths_uninstall() {
db_drop_field('file_managed', 'origname');
}
/**
* Implements hook_update_last_removed().
*/
function hook_update_last_removed() {
return 6103;
}
/**
* Implements hook_update_dependencies().
*/
function filefield_paths_dependencies() {
// Update 7103 uses the {file_managed} table, so make sure it is available.
$dependencies['filefield_paths'][7103] = array(
'system' => 7034,
);
return $dependencies;
}
/**
* Add origname field to {file_managed}.
*/
function filefield_paths_update_7103() {
// Clean-up an unused variable.
variable_del('filefield_paths_schema_version');
// Add origname field to {file_managed}, and populate with the current
// filenames.
if (!db_field_exists('file_managed', 'origname')) {
db_add_field('file_managed', 'origname', array(
'description' => 'Original name of the file with no path components. Used by the filefield_paths module.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
));
}
db_update('file_managed')
->expression('origname', 'filename')
->condition('origname', '')
->execute();
}
/**
* Add active updating flag to {filefield_paths}
*/
function filefield_paths_update_7104() {
db_add_field('filefield_paths', 'active_updating', array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => '0'
));
// migrate variable to filefield_paths table
$result = db_query("SELECT name, value FROM {variable} WHERE name LIKE 'ffp_%%_field_%%'");
foreach ($result as $row) {
if (preg_match('/ffp_(.+)_field_(.+)$/', $row->name, $match)) {
$active_updating = unserialize($row->value);
if ($active_updating) {
db_update('filefield_paths')
->fields(array(
'active_updating' => $active_updating
))
->condition('type', $match[1])
->condition('field', $match[2])
->execute();
}
variable_del($row->name);
}
}
}
/**
* Correct the default value for {filefield_paths}.active_updating field.
*/
function filefield_paths_update_7105() {
db_change_field('filefield_paths', 'active_updating', 'active_updating', array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0
));
}
/**
* Increase length of 'type' and 'field' columns.
*/
function filefield_paths_update_7106() {
db_change_field('filefield_paths', 'type', 'type', array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => ''
));
db_change_field('filefield_paths', 'field', 'field', array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => ''
));
}

View File

@@ -0,0 +1,466 @@
<?php
/**
* @file
* Contains core functions for the FileField Paths module.
*/
/**
* Include additional files.
*/
foreach (module_list() as $module) {
if (file_exists($file = dirname(__FILE__) . "/modules/{$module}.inc")) {
require_once $file;
}
}
/**
* Implements hook_form_alter().
*/
function filefield_paths_form_alter(&$form, $form_state, $form_id) {
$ffp = array();
// Invoke hook_filefield_paths_form_alter().
foreach (module_implements('filefield_paths_form_alter') as $module) {
$function = "{$module}_filefield_paths_form_alter";
$function($form, $ffp);
}
// If supporting module enabled, show FileField Paths settings form.
if (count($ffp) > 0) {
$entity_info = entity_get_info($form['#instance']['entity_type']);
$fields = module_invoke_all('filefield_paths_field_settings');
foreach ($ffp as $field_name => $field_data) {
$active_updating = 0;
$results = db_select('filefield_paths', 'f')
->fields('f')
->condition('type', $field_data['type'])
->condition('field', $field_name)
->execute()
->fetchAllAssoc('type');
if (!empty($results[$field_data['type']])) {
$result = $results[$field_data['type']];
foreach ($fields as &$field) {
$field['settings'] = unserialize($result->{$field['sql']});
}
unset($field);
$active_updating = $result->active_updating;
}
$count = 0;
foreach ($fields as $name => $field) {
$count++;
if (isset($field['form']) && is_array($field['form'])) {
$keys = array_keys($field['form']);
for ($i = 1; $i < count($field['form']); $i++) {
$field['form'][$keys[$i]]['#weight'] = ($count - 1) * 3 + 2 + $i;
$field['form'][$keys[$i]]['#element_validate'] = array('token_element_validate');
$field['form'][$keys[$i]]['#token_types'] = array('file', $entity_info['token type']);
}
unset($keys);
$field_data['form_path'] = array_merge_recursive($field_data['form_path'], $field['form']);
}
$field_data['form_path']['#tree'] = TRUE;
$field_data['form_path'][$name]['#weight'] = ($count - 1) * 3;
// Set defualt value for patterns.
if (isset($field['settings']['value'])) {
$field_data['form_path'][$name]['#default_value'] = $field['settings']['value'];
if (isset($field['data'])) {
foreach ($field['data'] as $key => $value) {
$field_data['form_path'][$value]['#default_value'] = $field['settings'][$key];
}
}
}
$field_data['form_path']["{$name}_cleanup"] = array(
'#type' => 'fieldset',
'#title' => t('@title cleanup settings', array('@title' => $field['title'])),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => ($count - 1) * 3 + 1,
'#attributes' => array(
'class' => array("{$name} cleanup")
)
);
// Cleanup field with Pathauto module.
$field_data['form_path']["{$name}_cleanup"]["{$name}_pathauto"] = array(
'#type' => 'checkbox',
'#title' => t('Cleanup using Pathauto') . '.',
'#default_value' => isset($field['settings']['pathauto'])
? $field['settings']['pathauto']
: 0
,
'#description' => t('Cleanup @title using', array('@title' => $field['title'])) . ' ' . l(t('Pathauto settings'), 'admin/config/search/path/settings'),
);
if (!module_exists('pathauto')) {
$field_data['form_path']["{$name}_cleanup"]["{$name}_pathauto"]['#disabled'] = TRUE;
$field_data['form_path']["{$name}_cleanup"]["{$name}_pathauto"]['#default_value'] = 0;
}
// Convert field to lower case.
$field_data['form_path']["{$name}_cleanup"]["{$name}_tolower"] = array(
'#type' => 'checkbox',
'#title' => t('Convert to lower case') . '.',
'#default_value' => isset($field['settings']['tolower'])
? $field['settings']['tolower']
: 0
,
'#description' => t('Convert @title to lower case', array('@title' => $field['title'])) . '.'
);
// Transliterate field with Transliteration module.
$field_data['form_path']["{$name}_cleanup"]["{$name}_transliterate"] = array(
'#type' => 'checkbox',
'#title' => t('Transliterate') . '.',
'#default_value' => isset($field['settings']['transliterate'])
? $field['settings']['transliterate']
: 0
,
'#description' => t('Transliterate @title', array('@title' => $field['title'])) . '.'
);
if (!module_exists('transliteration')) {
$field_data['form_path']["{$name}_cleanup"]["{$name}_transliterate"]['#disabled'] = TRUE;
$field_data['form_path']["{$name}_cleanup"]["{$name}_transliterate"]['#default_value'] = 0;
}
}
// Replacement patterns for field.
$field_data['form_path']['token_tree'] = array(
'#type' => 'fieldset',
'#title' => t('Replacement patterns'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => theme('token_tree', array('token_types' => array('file', $entity_info['token type']))),
'#weight' => 10,
);
// Retroactive updates.
$field_data['form_path']['retroactive_update'] = array(
'#type' => 'checkbox',
'#title' => t('Retroactive update'),
'#description' => t('Move and rename previously uploaded files') . '.' .
'<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
t('This feature should only be used on developmental servers or with extreme caution') . '.',
'#weight' => 11
);
// Active updating.
$field_data['form_path']['active_updating'] = array(
'#type' => 'checkbox',
'#title' => t('Active updating'),
'#default_value' => $active_updating,
'#description' => t('Actively move and rename previously uploaded files as required') . '.' .
'<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
t('This feature should only be used on developmental servers or with extreme caution') . '.',
'#weight' => 12
);
if (!in_array('filefield_paths_form_submit', $form['#submit'])) {
$form['#submit'][] = 'filefield_paths_form_submit';
}
}
}
}
/**
* Implements hook_form_submit().
*/
function filefield_paths_form_submit($form, &$form_state) {
$ffp = array();
// Invoke hook_filefield_paths_form_submit().
foreach (module_implements('filefield_paths_form_submit') as $module) {
$function = "{$module}_filefield_paths_form_submit";
$function($form_state, $ffp);
}
if (count($ffp) > 0) {
$retroactive_update = FALSE;
$fields = module_invoke_all('filefield_paths_field_settings');
foreach ($ffp as $field_name => $field_data) {
$cols = array(
'type' => $field_data['type'],
'field' => $field_name,
'active_updating' => $form_state['values']["ffp_{$field_name}"]['active_updating'],
);
foreach ($fields as $name => &$field) {
$cols[$field['sql']] = array(
'value' => $form_state['values']["ffp_{$field_name}"][$name],
'tolower' => $form_state['values']["ffp_{$field_name}"]["{$name}_cleanup"]["{$name}_tolower"],
'pathauto' => $form_state['values']["ffp_{$field_name}"]["{$name}_cleanup"]["{$name}_pathauto"],
'transliterate' => $form_state['values']["ffp_{$field_name}"]["{$name}_cleanup"]["{$name}_transliterate"],
);
// Store additional settings from add-on modules.
if (isset($field['data'])) {
foreach ($field['data'] as $key => $value) {
$cols[$field['sql']][$key] = $form_state['values']["ffp_{$field_name}"][$value];
}
}
}
$results = db_select('filefield_paths', 'f')
->fields('f')
->condition('type', $cols['type'])
->condition('field', $cols['field'])
->execute()
->fetchAll();
// Update existing entry.
if (!empty($results)) {
drupal_write_record('filefield_paths', $cols, array('type', 'field'));
}
// Create new entry.
else {
drupal_write_record('filefield_paths', $cols);
}
if ($form_state['values']["ffp_{$field_name}"]['retroactive_update']) {
$retroactive_update = TRUE;
$module = isset($form['#field']) ? $form['#field']['module'] : $field_name;
filefield_paths_batch_update($form_state['values']['instance']);
}
}
if ($retroactive_update) {
// Run batch.
batch_process($form_state['redirect']);
}
}
}
/**
* Set batch process to update FileField Paths.
*
* @param $instance
*/
function filefield_paths_batch_update($instance) {
$objects = array();
// Invoke hook_filefield_paths_batch_update().
if (function_exists($function = "{$instance['widget']['module']}_filefield_paths_batch_update")) {
$function($instance, $objects);
}
// Create batch.
$batch = array(
'title' => t('Updating FileField Paths'),
'operations' => array(
array('_filefield_paths_batch_update_process', array($objects, $instance))
),
);
batch_set($batch);
}
function _filefield_paths_batch_update_process($objects, $instance, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = count($objects);
$context['sandbox']['objects'] = $objects;
}
// Process nodes by groups of 5.
$count = min(5, count($context['sandbox']['objects']));
for ($i = 1; $i <= $count; $i++) {
// For each oid, load the object, update the files and save it.
$oid = array_shift($context['sandbox']['objects']);
// Invoke hook_filefield_paths_update().
if (function_exists($function = "{$instance['widget']['module']}_filefield_paths_update")) {
$function($oid, $instance);
}
// Update our progress information.
$context['sandbox']['progress']++;
}
// Inform the batch engine that we are not finished,
// and provide an estimation of the completion level we reached.
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Implements hook_entity_insert().
*/
function filefield_paths_entity_insert($entity, $type) {
filefield_paths_entity_update($entity, $type);
}
/**
* Implements hook_entity_update().
*/
function filefield_paths_entity_update($entity, $type) {
if (($ffp = filefield_paths_get_fields($entity, $type)) !== FALSE) {
$update = new stdClass;
$update->entity = FALSE;
// Process files.
foreach ($ffp['#files'] as &$file) {
// Invoke hook_filefield_paths_process_file().
foreach (module_implements('filefield_paths_process_file') as $module) {
$function = "{$module}_filefield_paths_process_file";
$settings = isset($ffp['#settings'][$file['name']]) ? $ffp['#settings'][$file['name']] : NULL;
if (!is_null($settings)) {
$function(($file['new'] || $settings['active_updating']), $file, $settings, $entity, $type, $update);
}
}
}
if ($update->entity == TRUE) {
field_attach_update($type, $entity);
}
// Cleanup temporary paths.
if ($ffp['#settings']) {
foreach ($ffp['#settings'] as $name => $field) {
// Invoke hook_filefield_paths_cleanup().
foreach (module_implements('filefield_paths_cleanup') as $module) {
$function = "{$module}_filefield_paths_cleanup";
$function($ffp, $name);
}
}
}
}
}
/**
* Implements hook_file_presave().
*/
function filefield_paths_file_presave($file) {
// Store original filename in the database.
if (empty($file->origname)) {
$file->origname = $file->filename;
}
}
/**
* Implements hook_field_delete_instance().
*/
function filefield_paths_field_delete_instance($instance) {
db_delete('filefield_paths')
->condition('type', "{$instance['entity_type']}::{$instance['bundle']}")
->condition('field', $instance['field_name'])
->execute();
}
/**
*
*/
function filefield_paths_get_fields(&$entity, $type, $op = NULL) {
$ffp = array();
$entity_info = entity_get_info();
// Invoke hook_filefield_paths_get_fields().
foreach (module_implements('filefield_paths_get_fields') as $module) {
$function = "{$module}_filefield_paths_get_fields";
$function($entity, $ffp);
}
if (count($ffp) == 0 || (isset($ffp['#types']) && !is_array($ffp['#types']))) {
return FALSE;
}
$fields = module_invoke_all('filefield_paths_field_settings');
// Load fields settings
foreach (array_keys($ffp['#types']) as $name) {
$field_type = isset($entity->{$entity_info[$type]['entity keys']['bundle']}) ? "{$type}::{$entity->{$entity_info[$type]['entity keys']['bundle']}}" : "{$type}::{$type}";
$results = db_select('filefield_paths', 'f')
->fields('f')
->condition('type', $field_type)
->condition('field', $name)
->execute()
->fetchAllAssoc('type');
if (!empty($results[$field_type])) {
$result = $results[$field_type];
foreach ($fields as $field) {
$ffp['#settings'][$name][$field['sql']] = unserialize($result->$field['sql']);
}
$ffp['#settings'][$name]['active_updating'] = $result->active_updating;
}
}
return $ffp;
}
/**
* Run regular expression over all available text-based fields.
*/
function _filefield_paths_replace_path($old, $new, &$entity, &$update) {
// Build regular expression.
$info = parse_url($old);
$info['path'] = !empty($info['path']) ? $info['path'] : '';
$absolute = str_replace("{$info['host']}{$info['path']}", '', file_create_url($old));
$relative = parse_url($absolute, PHP_URL_PATH);
$regex = str_replace('/', '\/', "({$absolute}|{$relative}|{$info['scheme']}://)(styles/.*?/{$info['scheme']}/|)({$info['host']}{$info['path']})");
// Build replacement.
$info = parse_url($new);
$replacement = "\$1\$2{$info['host']}{$info['path']}";
$fields = field_info_fields();
foreach ($fields as $name => $field) {
if ($field['module'] == 'text' && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
foreach ($entity->{$field['field_name']} as &$language) {
foreach ($language as &$item) {
$item['value'] = preg_replace("/$regex/", $replacement, $item['value']);
}
}
}
}
}
/**
* Process and cleanup strings.
*/
function filefield_paths_process_string($value, $data, $settings = array()) {
$transliterate = module_exists('transliteration') && isset($settings['transliterate']) && $settings['transliterate'];
$pathauto = module_exists('pathauto') && isset($settings['pathauto']) && $settings['pathauto'] == TRUE;
if ($pathauto == TRUE) {
module_load_include('inc', 'pathauto');
}
$paths = explode('/', $value);
foreach ($paths as &$path) {
// Process string tokens.
$path = token_replace($path, $data, array('clear' => TRUE));
// Cleanup with pathauto.
if ($pathauto == TRUE) {
$path_parts = explode('.', $path);
foreach ($path_parts as &$path_part) {
$path_part = pathauto_cleanstring($path_part);
}
$path = implode('.', $path_parts);
}
// Transliterate string.
if ($transliterate == TRUE) {
$path = transliteration_clean_filename($path);
}
}
$value = implode('/', $paths);
// Convert string to lower case.
if (isset($settings['tolower']) && $settings['tolower'] == TRUE) {
$value = drupal_strtolower($value);
}
// Ensure that there are no double-slash sequences due to empty token values.
$value = preg_replace('/\/+/', '/', $value);
return $value;
}

View File

@@ -0,0 +1,214 @@
<?php
/**
* @file
* Features module integration.
*/
/**
* Implements hook_features_api().
*
* Main info hook that features uses to determine what components are provided
*
* The 'component' for this module is named 'filefield_paths' not just
* 'filefield_paths' to follow recommended practice documented in features.api
*
* We export individual filefield_paths instances, although seldom on their
* own, usually as part of a bigger package. When a content type or field is
* being exported, these settings come along for the ride.
*/
function filefield_paths_features_api() {
return array(
'filefield_paths' => array(
'name' => t('FileField Paths'),
'default_hook' => 'default_filefield_paths',
'feature_source' => TRUE,
'default_file' => FEATURES_DEFAULTS_INCLUDED_COMMON,
),
);
}
/**
* Implements hook_features_export_options().
*
* List all FileField Paths config settings currently available for export. This
* adds each of the configurations to the features UI where they can be chosen
* for bundling.
*
* This UI may be entirely unneccessary if we only ever export as a part of
* something else (individual fields settings), but it's here for completeness.
*
* @return array A keyed array of items, suitable for use with a FormAPI select
* or checkboxes element.
*/
function filefield_paths_features_export_options() {
$options = array();
$results = db_select('filefield_paths', 'f')
->fields('f', array('type', 'field'))
->execute();
foreach ($results as $row) {
// Generate the unique keys that can identify the row
// "{entity_type}::{bundle}::{field_name}" eg "node::story::field_illustration"
$key = "{$row->type}::{$row->field}";
$options[$key] = $key;
}
return $options;
}
/**
* Implements hook_features_export().
*
* Process the export array for a given component.
*
* Normally, we will be adding this as a child in the pipe of
* field_features_export, so that when a filefield instance is exported, this
* setting was published along with it.
*/
function filefield_paths_features_export($data, &$export, $module_name = '') {
$export['dependencies']['filefield_paths'] = 'filefield_paths';
$pipe = array();
foreach ($data as $identifier) {
if ($wrapper = filefield_paths_load($identifier)) {
$export['features']['filefield_paths'][$identifier] = $identifier;
if ($wrapper['filename']['transliterate'] || $wrapper['filepath']['transliterate']) {
$export['dependencies']['transliteration'] = 'transliteration';
}
if ($wrapper['filename']['pathauto'] || $wrapper['filepath']['pathauto']) {
$export['dependencies']['pathauto'] = 'pathauto';
}
if (module_exists('strongarm')) {
$variable_map = features_get_default_map('variable');
$pipe['variable'][] = "ffp_{$wrapper['type']}_{$wrapper['field']}";
}
}
}
return $pipe;
}
/**
* Attach our own export routine as a piped export that happens below any
* filefield path that is getting exported.
*
* The component name for fields is 'field'.
*
* HOOK_features_pipe_COMPONENT_alter()
*
* This captures each field export, and adds ourself to the dependencies and
* exports when that field is exported.
*/
function filefield_paths_features_pipe_field_alter(&$pipe, $data, $export) {
foreach ($data as $field_identifier) {
// Convert field identifier to FileField Paths format.
$field_identifier = str_replace('-', '::', $field_identifier);
// Field export is exporting a field named $field_identifier.
// If that is a filefield, we should attach ourselves as a subprocess (pipe).
// .. actually, don't need to check the field type,
// just see if we have some filefield_path
// settings that use the same $field_identifier key!
if (filefield_paths_load($field_identifier)) {
// So add this setting as a piped child of the filed when it gets exported.
$pipe['filefield_paths'][$field_identifier] = $field_identifier;
}
}
}
/**
* Return the required path settings for the named filefield instance.
*
* A CRUD utility for filefield_paths
*
* @param a unique identifier for the given field instance -
* {$entity_type::$bundle::$field}.
*
* @return a row array from the filefield_paths DB table - with the 'serialized'
* blobs unpacked.
*/
function filefield_paths_load($identifier) {
list($entity_type, $bundle, $field_name) = explode('::', $identifier);
$result = db_select('filefield_paths', 'f')
->fields('f')
->condition('type', "{$entity_type}::{$bundle}")
->condition('field', $field_name)
->execute();
if ($row = $result->fetchAssoc()) {
$ffp = array();
// Each cell in the row gets exposed, retrieve the schema to figure this out.
$schema = drupal_get_schema('filefield_paths');
foreach ($schema['fields'] as $field => $field_def) {
$ffp[$field] = empty($field_def['serialize']) ? $row[$field] : unserialize($row[$field]);
}
return $ffp;
}
return NULL;
}
/**
* Delete the identified row
*
* A CRUD utility for filefield_paths
*/
function filefield_paths_delete($identifier) {
list($entity_type, $bundle, $field_name) = explode('::', $identifier);
db_delete('filefield_paths')
->condition('type', "{$entity_type}::{$bundle}")
->condition('field', $field_name)
->execute();
}
/**
* Implements hook_features_export_render()
*
* Return the PHP code that represents a dump of the settings listed as $data
*/
function filefield_paths_features_export_render($module, $data) {
$code = array();
$code[] = ' $settings = array();';
$code[] = '';
$translatables = array();
foreach ($data as $item_id) {
$item = filefield_paths_load($item_id);
if (empty($item)) {
watchdog('filefield_paths', "Failed to retrieve the filefield path settings '%item_id' while preparing the feature export code.", array('%item_id' => $item_id), WATCHDOG_WARNING);
continue;
}
$code[] = " // Exported {$item_id}";
$export = features_var_export($item, ' ');
$code[] = " \$settings['{$item_id}'] = {$export};";
}
$code[] = '';
$code[] = ' return $settings;';
$code = implode("\n", $code);
return array('default_filefield_paths' => $code);
}
/**
* Implements hook_features_export_revert().
*/
function filefield_paths_features_revert($module) {
filefield_paths_features_rebuild($module);
}
/**
* Create/recreate the items based on the data array. Data should contain a
* number of filefield_paths definitions.
*
* Implements hook_features_export_rebuild().
*
* Data just need to be put straight into the database as rows.
*/
function filefield_paths_features_rebuild($module) {
if ($defaults = features_get_default('filefield_paths', $module)) {
foreach ($defaults as $filefield_paths_id => $filefield_paths) {
// Delete any previous settings for this item.
if (filefield_paths_load($filefield_paths_id)) {
filefield_paths_delete($filefield_paths_id);
}
drupal_write_record('filefield_paths', $filefield_paths);
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* @file
* File module integration.
*/
/**
* Implements hook_filefield_paths_form_alter().
*/
function file_filefield_paths_form_alter(&$form, &$ffp) {
if (isset($form['#field']) && $form['#field']['type'] == 'file' && isset($form['instance']['settings']['file_directory'])) {
$ffp[$form['#field']['field_name']] = array(
'show' => TRUE,
'type' => "{$form['instance']['entity_type']['#value']}::{$form['instance']['bundle']['#value']}",
'form_path' => &$form['instance']["ffp_{$form['#field']['field_name']}"],
'file_path_default' => $form['instance']['settings']['file_directory']['#default_value']
);
// Create path settings fieldset
$ffp[$form['#field']['field_name']]['form_path'] = array(
'#type' => 'fieldset',
'#title' => t('File Path settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 1,
);
$ffp[$form['#field']['field_name']]['form_path']['file_path'] = $form['instance']['settings']['file_directory'];
$ffp[$form['#field']['field_name']]['form_path']['file_path']['#title'] = t('File path');
$form['instance']['settings']['file_directory']['#access'] = FALSE;
}
}
/**
* Implements hook_filefield_paths_form_submit().
*/
function file_filefield_paths_form_submit(&$form_state, &$ffp) {
if (isset($form_state['values']['form_id']) && $form_state['values']['form_id'] == 'field_ui_field_edit_form') {
$form_state['values']["ffp_{$form_state['values']['instance']['field_name']}"] = $form_state['values']['instance']["ffp_{$form_state['values']['instance']['field_name']}"];
$ffp[$form_state['values']['instance']['field_name']] = array(
'type' => "{$form_state['values']['instance']['entity_type']}::{$form_state['values']['instance']['bundle']}",
);
$form_state['values']['instance']['settings']['file_directory'] = $form_state['values']["ffp_{$form_state['values']['instance']['field_name']}"]['file_path'];
}
}
/**
* Implements hook_filefield_paths_get_fields().
*/
function file_filefield_paths_get_fields(&$entity, &$ffp) {
if (is_object($entity)) {
$fields = field_info_fields();
foreach ($fields as $name => $field) {
if ($field['type'] == 'file' && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
foreach ($entity->{$field['field_name']} as $language) {
foreach ($language as $file) {
$new = isset($file['new']) ? $file['new'] : FALSE;
$file = file_load($file['fid']);
$ffp['#files'][] = array(
'field' => (array) $file,
'module' => $field['module'],
'name' => $field['field_name'],
'new' => $new || $file->timestamp == REQUEST_TIME,
);
$ffp['#types'][$field['field_name']] = TRUE;
}
}
}
}
}
}
/**
* Implements hook_filefield_paths_batch_update().
*/
function file_filefield_paths_batch_update($instance, &$objects) {
$query = new EntityFieldQuery();
$result = $query->entityCondition('entity_type', $instance['entity_type'])
->entityCondition('bundle', array($instance['bundle']))
->fieldCondition($instance['field_name'])
->execute();
$objects = array_keys($result[$instance['entity_type']]);
}
/**
* Implements hook_filefield_paths_update().
*/
function file_filefield_paths_update($oid, $instance) {
$entity = current(entity_load($instance['entity_type'], array($oid)));
// Flag files for update.
if (isset($entity->{$instance['field_name']})) {
foreach ($entity->{$instance['field_name']} as &$language) {
foreach ($language as &$file) {
if (!is_array($file) || empty($file['uri'])) {
continue;
}
$file['new'] = TRUE;
}
}
}
// Process Entity.
filefield_paths_entity_update($entity, $instance['entity_type']);
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* @file
* FileField Paths module integration.
*/
/**
* Implements hook_filefield_paths_field_settings().
*/
function filefield_paths_filefield_paths_field_settings() {
return array(
'file_path' => array(
'title' => 'File path',
'sql' => 'filepath',
'form' => array(
'file_path' => array(
'#maxlength' => 512,
'#size' => 128,
),
),
),
'file_name' => array(
'title' => 'File name',
'sql' => 'filename',
'form' => array(
'file_name' => array(
'#type' => 'textfield',
'#title' => t('File name'),
'#default_value' => '[file:ffp-name-only-original].[file:ffp-extension-original]',
),
),
)
);
}
/**
* Implements hook_filefield_paths_process_file().
*/
function filefield_paths_filefield_paths_process_file($new, &$file, $settings, &$entity, $type, &$update) {
if ($new && !empty($file['field']['filename'])) {
$field = field_info_field($file['name']);
$token_data = array('file' => file_load($file['field']['fid']), $type => $entity);
// Process filename.
$file['filename']['old'] = $file['field']['filename'];
$file['filename']['new'] = !empty($settings['filename']['value'])
? filefield_paths_process_string($settings['filename']['value'], $token_data, $settings['filename'])
: $file['field']['filename'];
// Process filepath.
$file['filepath']['old'] = $file['field']['uri'];
$file['filepath']['new'] = "{$field['settings']['uri_scheme']}://" . filefield_paths_process_string($settings['filepath']['value'] . "/{$file['filename']['new']}", $token_data, $settings['filepath']);
// Finalize files if necessary.
$dirname = dirname($file['filepath']['new']);
if ($dirname != dirname($file['field']['uri']) || $file['filename']['new'] != $file['field']['filename']) {
if (file_prepare_directory($dirname, FILE_CREATE_DIRECTORY) && file_move((object) $file['field'], $file['filepath']['new'])) {
$update->entity = TRUE;
// Fix reference to old paths.
$file['filepath']['new'] = str_replace($file['filename']['old'], $file['filename']['new'], $file['filepath']['new']);
// Process regular expression.
_filefield_paths_replace_path($file['filepath']['old'], $file['filepath']['new'], $entity, $update);
// Store new filename in file Array
$file['field']['filename'] = $file['filename']['new'];
$file['field']['uri'] = $file['filepath']['new'];
}
}
}
}
/**
* Implements hook_filefield_paths_cleanup().
*/
function filefield_paths_filefield_paths_cleanup($ffp, $name) {
foreach ($ffp['#files'] as $file) {
if (isset($file['filepath'])) {
$scheme = file_uri_scheme($file['filepath']['old']);
$paths = explode('/', str_replace("{$scheme}://", '', dirname($file['filepath']['old'])));
while ($paths) {
if (drupal_rmdir("{$scheme}://" . implode('/', $paths)) == TRUE) {
array_pop($paths);
continue;
}
break;
}
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* @file
* Image module integration.
*/
/**
* Implements hook_filefield_paths_form_alter().
*/
function image_filefield_paths_form_alter(&$form, &$ffp) {
if (isset($form['#field']) && $form['#field']['type'] == 'image' && isset($form['instance']['settings']['file_directory'])) {
$ffp[$form['#field']['field_name']] = array(
'show' => TRUE,
'type' => "{$form['instance']['entity_type']['#value']}::{$form['instance']['bundle']['#value']}",
'form_path' => &$form['instance']["ffp_{$form['#field']['field_name']}"],
'file_path_default' => $form['instance']['settings']['file_directory']['#default_value']
);
// Create path settings fieldset
$ffp[$form['#field']['field_name']]['form_path'] = array(
'#type' => 'fieldset',
'#title' => t('Image Path settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 1,
);
$ffp[$form['#field']['field_name']]['form_path']['file_path'] = $form['instance']['settings']['file_directory'];
$ffp[$form['#field']['field_name']]['form_path']['file_path']['#title'] = t('File path');
$form['instance']['settings']['file_directory']['#access'] = FALSE;
}
}
/**
* Implements hook_filefield_paths_form_submit().
*/
function image_filefield_paths_form_submit(&$form_state, &$ffp) {
if (isset($form_state['values']['form_id']) && $form_state['values']['form_id'] == 'field_ui_field_edit_form') {
$form_state['values']["ffp_{$form_state['values']['instance']['field_name']}"] = $form_state['values']['instance']["ffp_{$form_state['values']['instance']['field_name']}"];
$ffp[$form_state['values']['instance']['field_name']] = array(
'type' => "{$form_state['values']['instance']['entity_type']}::{$form_state['values']['instance']['bundle']}",
);
$form_state['values']['instance']['settings']['file_directory'] = $form_state['values']["ffp_{$form_state['values']['instance']['field_name']}"]['file_path'];
}
}
/**
* Implements hook_filefield_paths_get_fields().
*/
function image_filefield_paths_get_fields(&$entity, &$ffp) {
if (is_object($entity)) {
$fields = field_info_fields();
foreach ($fields as $name => $field) {
if ($field['type'] == 'image' && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
foreach ($entity->{$field['field_name']} as $language) {
foreach ($language as $file) {
$new = isset($file['new']) ? $file['new'] : FALSE;
$file = file_load($file['fid']);
$ffp['#files'][] = array(
'field' => (array) $file,
'module' => $field['module'],
'name' => $field['field_name'],
'new' => $new || $file->timestamp == REQUEST_TIME,
);
$ffp['#types'][$field['field_name']] = TRUE;
}
}
}
}
}
}
/**
* Implements hook_filefield_paths_batch_update().
*/
function image_filefield_paths_batch_update($instance, &$objects) {
$query = new EntityFieldQuery();
$result = $query->entityCondition('entity_type', $instance['entity_type'])
->entityCondition('bundle', array($instance['bundle']))
->fieldCondition($instance['field_name'])
->execute();
$objects = array_keys($result[$instance['entity_type']]);
}
/**
* Implements hook_filefield_paths_update().
*/
function image_filefield_paths_update($oid, $instance) {
$entity = current(entity_load($instance['entity_type'], array($oid)));
// Flag files for update.
if (isset($entity->{$instance['field_name']})) {
foreach ($entity->{$instance['field_name']} as &$language) {
foreach ($language as &$file) {
if (!is_array($file) || empty($file['uri'])) {
continue;
}
$file['new'] = TRUE;
}
}
}
// Process Entity.
filefield_paths_entity_update($entity, $instance['entity_type']);
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @file
* Token module integration.
*/
/**
* Implements hook_token_info().
*/
function filefield_paths_token_info() {
$info['tokens']['file']['ffp-name-only'] = array(
'name' => t("File name"),
'description' => t("File name without extension."),
);
$info['tokens']['file']['ffp-name-only-original'] = array(
'name' => t("File name - original"),
'description' => t("File name without extension - original."),
);
$info['tokens']['file']['ffp-extension-original'] = array(
'name' => t("File extension - original"),
'description' => t("File extension - original."),
);
return $info;
}
/**
* Implements hook_tokens().
*/
function filefield_paths_tokens($type, $tokens, array $data = array(), array $options = array()) {
$url_options = array('absolute' => TRUE);
if (isset($language)) {
$url_options['language'] = $language;
}
$sanitize = !empty($options['sanitize']);
$replacements = array();
if ($type == 'file' && !empty($data['file'])) {
$file = $data['file'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'ffp-name-only':
$info = pathinfo($file->filename);
$replacements[$original] = $info['filename'];
break;
case 'ffp-name-only-original':
$info = pathinfo($file->origname);
$replacements[$original] = $info['filename'];
break;
case 'ffp-extension-original':
$info = pathinfo($file->origname);
$replacements[$original] = $info['extension'];
break;
}
}
}
return $replacements;
}

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,18 @@
Project homepage http://drupal.org/project/imageapi_optimize
== INSTALL ==
After having enabled this module, go to admin/config/media/image-toolkit,
select 'ImageAPI Optimize' as the default toolkit and Save, then adjust your
settings on the same page.
ImageAPI can use 3rd service like Yahoo! SmushIt, all that you needs is PHP 5.2+
with JSON support.
In order to have better performance, it is preferred to compile your selected
tools and give the paths to ImageAPI Optimize.
== NOTES ==
Everytime you install a new ImageAPI contrib module, go to the ImageAPI Optimize
settings page and save the configuration.

View File

@@ -0,0 +1,12 @@
name = ImageAPI Optimize
description = An optimized version Image Toolkits
core = 7.x
files[] = imageapi_optimize.install
files[] = imageapi_optimize.module
; Information added by drupal.org packaging script on 2012-11-01
version = "7.x-1.0"
core = "7.x"
project = "imageapi_optimize"
datestamp = "1351775225"

View File

@@ -0,0 +1,20 @@
<?php
/**
* @file
* Install, update and uninstall functions
*/
function imageapi_optimize_uninstall() {
$variable_names = array(
'imageapi_optimize_service',
'imageapi_optimize_toolkit',
'imageapi_optimize_advpng',
'imageapi_optimize_optipng',
'imageapi_optimize_jpegtran',
'imageapi_optimize_jfifremove',
'imageapi_optimize_pngcrush',
);
foreach ($variable_names as $variable_name) {
variable_del($variable_name);
}
}

View File

@@ -0,0 +1,323 @@
<?php
/**
* @file
* Image optimize functionalities
*/
/**
* Implements hook_init().
*
* Abstract layer to all methods implemented by base toolkit.
*/
function imageapi_optimize_init() {
if (! $cache = cache_get('imageapi_optimize:methods')) {
$methods = _imageapi_optimize_get_methods();
}
else {
$methods = $cache->data;
}
foreach ($methods as $method) {
eval('function image_imageapi_optimize_'.$method.'($image) {
$params = array_slice(func_get_args(), 1);
return _imageapi_optimize_invoke("'. $method .'", $image, $params);
}');
}
}
/**
* Implements hook_image_toolkits().
*/
function imageapi_optimize_image_toolkits() {
return array(
'imageapi_optimize' => array(
'title' => 'ImageAPI Optimize',
'available' => TRUE,
),
);
}
/**
* Settings form for the toolkit.
*/
function image_imageapi_optimize_settings() {
$toolkits = array();
foreach (image_get_available_toolkits() as $toolkit => $info) {
if ($toolkit != 'imageapi_optimize') {
$toolkits[$toolkit] = $info;
}
}
$base_toolkit = variable_get('imageapi_optimize_toolkit', 'gd');
$form['imageapi_optimize_base_toolkit'] = array(
'#type' => 'fieldset',
'#title' => t('ImageAPI Optimize Base Toolkit'),
);
$form['imageapi_optimize_base_toolkit']['imageapi_optimize_toolkit'] = array(
'#type' => 'radios',
'#title' => t('Base toolkit'),
'#default_value' => $base_toolkit,
'#options' => $toolkits,
'#element_validate' => array('imageapi_optimize_toolkit_element_validate'),
);
$base_toolkit_settings_function = 'image_' . $base_toolkit . '_settings';
if (function_exists($base_toolkit_settings_function)) {
$base_toolkit_settings_form = $base_toolkit_settings_function();
}
$base_toolkit_settings_fieldset = array(
'#type' => 'fieldset',
'#title' => t('@toolkit Settings', array('@toolkit' => $toolkits[$base_toolkit])),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['imageapi_optimize_base_toolkit']['imageapi_optimize_toolkit_settings'] = array_merge($base_toolkit_settings_fieldset, $base_toolkit_settings_form);
$form['imageapi_optimize_service'] = array(
'#type' => 'fieldset',
'#title' => t('ImageAPI Optimize Service'),
);
$services = array(
'internal' => 'Internal tools (configurable below)',
'smushit' => 'Yahoo! Smush.It',
);
$form['imageapi_optimize_service']['imageapi_optimize_service'] = array(
'#type' => 'radios',
'#title' => t('Choose which optimization service to use'),
'#default_value' => variable_get('imageapi_optimize_service', 'internal'),
'#options' => $services,
);
$form['imageapi_optimize_service']['imageapi_optimize_internal'] = array(
'#type' => 'fieldset',
'#title' => t('Paths to internal tools'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['imageapi_optimize_service']['imageapi_optimize_internal']['imageapi_optimize_advpng'] = array(
'#type' => 'textfield',
'#title' => t('Path to advpng'),
'#default_value' => variable_get('imageapi_optimize_advpng', ''),
'#element_validate' => array('imageapi_optimize_validate_path'),
'#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a> (part of AdvanceCOMP).', array('!link' => 'http://advancemame.sourceforge.net/comp-download.html')),
);
$form['imageapi_optimize_service']['imageapi_optimize_internal']['imageapi_optimize_optipng'] = array(
'#type' => 'textfield',
'#title' => t('Path to optipng'),
'#default_value' => variable_get('imageapi_optimize_optipng', ''),
'#element_validate' => array('imageapi_optimize_validate_path'),
'#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array('!link' => 'http://optipng.sourceforge.net/')),
);
$form['imageapi_optimize_service']['imageapi_optimize_internal']['imageapi_optimize_pngcrush'] = array(
'#type' => 'textfield',
'#title' => t('Path to pngcrush'),
'#default_value' => variable_get('imageapi_optimize_pngcrush', ''),
'#element_validate' => array('imageapi_optimize_validate_path'),
'#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array('!link' => 'http://pmt.sourceforge.net/pngcrush/')),
);
$form['imageapi_optimize_service']['imageapi_optimize_internal']['imageapi_optimize_jpegtran'] = array(
'#type' => 'textfield',
'#title' => t('Path to jpegtran'),
'#default_value' => variable_get('imageapi_optimize_jpegtran', '/usr/bin/jpegtran'),
'#element_validate' => array('imageapi_optimize_validate_path'),
'#description' => t('Leave empty to skip this command. This is a part of <a href="!link">libjpeg</a> and could probably on your system.', array('!link' => 'http://ijg.org/')),
);
$form['imageapi_optimize_service']['imageapi_optimize_internal']['imageapi_optimize_jfifremove'] = array(
'#type' => 'textfield',
'#title' => t('Path to jfifremove'),
'#default_value' => variable_get('imageapi_optimize_jfifremove', ''),
'#element_validate' => array('imageapi_optimize_validate_path'),
'#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array('!link' => 'http://lyncd.com/files/imgopt/jfifremove.c')),
);
// Reloads methods because user may change toolkit
$form['#submit'][] = '_imageapi_optimize_get_methods';
return $form;
}
function imageapi_optimize_toolkit_element_validate($element) {
if ($element['#value'] == 'imageapi_optimize') {
form_set_error($element['#name'], t('You must select a different toolkit from ImageAPI Optimize itself.'));
}
}
function imageapi_optimize_validate_path($element) {
if ($errors = _imageapi_optimize_check_path($element['#value'])) {
form_set_error($element['#parents'][0], implode('<br />', $errors));
return FALSE;
}
return TRUE;
}
/**
* All ImageAPI functions call their base functions
*/
function image_imageapi_optimize_load($image) {
return _imageapi_optimize_invoke('load', $image);
}
function image_imageapi_optimize_save($image, $dst) {
if (_imageapi_optimize_invoke('save', $image, array($dst))) {
return _imageapi_optimize_optimize($image, $dst);
}
return FALSE;
}
/**
* Helper. Based on imageapi_invoke()
*/
function _imageapi_optimize_invoke($method, $image, array $params = array()) {
$function = 'image_' . variable_get('imageapi_optimize_toolkit', '') . '_' . $method;
if (function_exists($function)) {
array_unshift($params, $image);
return call_user_func_array($function, $params);
}
return FALSE;
}
/**
* Checks if a path exists and is executable
*
* Warning: it does not check if the command is harmful.
* You should keep an eye on users with "administer imageapi" permission
*/
function _imageapi_optimize_check_path($path) {
$errors = array();
if (!empty($path)) {
if (!is_file($path)) {
$errors[] = t('The specified path %file does not exist.', array('%file' => $path));
}
if (!$errors && !is_executable($path)) {
$errors[] = t('The specified path %file is not executable.', array('%file' => $path));
}
if ($errors && $open_basedir = ini_get('open_basedir')) {
$errors[] = t('PHP\'s <a href="!open-basedir">open_basedir</a> security restriction is set to %open-basedir, which may be interfering with attempts to locate %file.', array('%file' => $path, '%open-basedir' => $open_basedir, '!info-link' => url('http://php.net/features.safe-mode#ini.open-basedir')));
}
}
return $errors;
}
/**
* Optimizes image with external commands
*/
function _imageapi_optimize_optimize($image, $dst) {
$optimize_service = '_imageapi_optimize_service_'. variable_get('imageapi_optimize_service', 'internal');
$optimize_service($image, $dst);
// If optimize service fails, there is no problem. Original image is saved.
return TRUE;
}
/**
* Uses internal tools to optimize
*/
function _imageapi_optimize_service_internal($image, $dst) {
$dst = drupal_realpath($dst);
switch ($image->info['mime_type']) {
case 'image/png':
if ($cmd = variable_get('imageapi_optimize_optipng', '')) {
exec("$cmd -o5 -quiet ". escapeshellarg($dst));
}
if ($cmd = variable_get('imageapi_optimize_pngcrush', '')) {
$temp = drupal_realpath(drupal_tempnam('temporary://', 'file'));
exec("$cmd -rem alla -reduce -brute -q ". escapeshellarg($dst) ." ". escapeshellarg($temp) ." && mv ". escapeshellarg($temp) ." ". escapeshellarg($dst));
}
if ($cmd = variable_get('imageapi_optimize_advpng', '')) {
exec("$cmd -z4q ". escapeshellarg($dst), $return, $output);
}
break;
case 'image/jpeg':
if ($cmd = variable_get('imageapi_optimize_jpegtran', '')) {
_imageapi_optimize_exec("$cmd -copy none -optimize ". escapeshellarg($dst), $dst);
}
if ($cmd = variable_get('imageapi_optimize_jfifremove', '')) {
_imageapi_optimize_exec("$cmd < ". escapeshellarg($dst), $dst);
}
break;
}
return TRUE;
}
/**
* Use smushit.com to optimize
*/
function _imageapi_optimize_service_smushit($image, $dst) {
if (! function_exists('json_decode')) {
drupal_set_message(t('Required function, json_decode(), is not available.'), 'error');
return FALSE;
}
$dst = drupal_realpath($dst);
$url = 'http://www.smushit.com/ysmush.it/ws.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('files' => '@' . $dst));
$data = curl_exec($ch);
curl_close($ch);
$json = json_decode($data);
// SmushIt returns an error if it cannot optimize the image. Otherwise, it
// returns an object, with 'dest' (temporary file) and 'percent' (savings)
// among other properties.
if (!isset($json->error)) {
$result = drupal_http_request($json->dest);
if (!isset($result->error)) {
file_unmanaged_save_data($result->data, $dst, FILE_EXISTS_REPLACE);
return TRUE;
}
}
}
/**
* Saves result of a command into file
*/
function _imageapi_optimize_exec($cmd, $dst) {
ob_start();
passthru($cmd);
$output = ob_get_contents();
ob_end_clean();
file_unmanaged_save_data($output, $dst, FILE_EXISTS_REPLACE);
}
/**
* Gets all implemented methods by ImageAPI and contrib modules.
* This function takes a dozens of miliseconds CPU times.
*/
function _imageapi_optimize_get_methods() {
// The list of toolkits might not loaded yet. We call this function to get
// toolkits in separate .inc files eventually included.
image_get_available_toolkits();
$funcs = get_defined_functions();
$methods = array();
$prefix = 'image_' . variable_get('imageapi_optimize_toolkit', '') .'_';
foreach ($funcs['user'] as $func) {
if (strpos($func, $prefix) === 0) {
$method = substr($func, strlen($prefix));
if (!in_array($method, array('load', 'save', 'settings'))) {
$methods[] = $method;
}
}
}
cache_set('imageapi_optimize:methods', $methods);
watchdog('imageapi', 'Refresh ImageAPI methods');
return $methods;
}

View File

@@ -0,0 +1,27 @@
Imagecache Actions
------------------
Imagecache Actions 7.x-1.0 2012-05-19
-------------------------------------
Incompatabilities:
- Effects that operate on the transparency layer, do not automatically change
the format anymore to png. You will have to add this as a separate effect.
- File handling: the way that files (e.g. fonts, watermarks, backgrounds) are
searched for has been aligned over all effects that work with an additional
file. This may cause current styles to not being able to find specified files
anymore.
No changelog exists from before this 7.x-1.0 version. So the changelog starts
from here. 7.x-1.0 is the first reasonably well working D7 version. The 7.x-0.0
version was based on posted patches in the D7 port request issue and has never
been tested well. This 7.x-1.0 version is also not extensively tested by the
maintainers, but has been used in many (live) sites - currently (may 2012) over
5.000 reported 7.x-1.x-dev installs - so has received fairly good "test
coverage".
Current and past maintainers for Imagecache Actions
---------------------------------------------------
- dman (http://drupal.org/user/33240)
- sidneyshan (http://drupal.org/user/652426)
- fietserwin (http://drupal.org/user/750928)

View File

@@ -0,0 +1,133 @@
<?php
/**
* @file
* Drupal module stream wrapper class (module://{module}/{resource}).
*
* Provides support for accessing files in module directories.
*/
class ImageCacheActionsModuleStreamWrapper extends DrupalLocalStreamWrapper {
/**
* Parses an uri into its separate components.
*
* Example:
* module://my_module/images/mask.png will return
* array('module', 'my_module', 'images/mask.png').
*
* @return array
* An array of strings containing the separate parts of the uri:
* - scheme (module)
* - module name
* - resource name
*/
protected function parseUri() {
$scheme = '';
$module = '';
$component = '';
if (!empty($this->uri)) {
list($scheme, $target) = explode('://', $this->uri, 2);
if (!empty($target)) {
$target = trim($target, '\/');
list($module, $resource) = explode('/', $target, 2);
}
}
return array($scheme, $module, $resource);
}
/**
* Implements abstract public function getDirectoryPath()
*/
public function getDirectoryPath() {
list($scheme, $module, $component) = $this->parseUri();
return drupal_get_path('module', $module);
}
/**
* Returns the local writable target of the resource within the stream.
*
* @see DrupalLocalStreamWrapper::getTarget()
*/
protected function getTarget($uri = NULL) {
if (!empty($uri)) {
$this->setUri($uri);
}
list($scheme, $module, $component) = $this->parseUri();
return $component;
}
/**
* Returns the canonical absolute path of the URI, if possible.
*
* @see DrupalLocalStreamWrapper::getLocalPath()
*/
protected function getLocalPath($uri = NULL) {
if (!empty($uri)) {
$this->setUri($uri);
}
return parent::getLocalPath();
}
/**
* Returns the local writable target of the resource within the stream.
*
* Overrides getExternalUrl().
*
* Return the HTML URI of a public file.
*/
function getExternalUrl() {
$path = str_replace('\\', '/', $this->getTarget());
return $GLOBALS['base_url'] . '/' . $this->getDirectoryPath() . '/' . drupal_encode_path($path);
}
/*
* Unsupported methods, modes and operations because we are read-only.
*/
function chmod($mode) {
return $this->unsupportedError('chmod', STREAM_REPORT_ERRORS);
}
public function stream_open($uri, $mode, $options, &$opened_path) {
if (!in_array($mode, array('r'))) {
return $this->unsupportedError("stream_open(mode = '$mode')", $options);
}
return parent::stream_open($uri, $mode, $options, $opened_path);
}
public function stream_lock($operation) {
if (in_array($operation, array(LOCK_EX))) {
return $this->unsupportedError('stream_lock(LOCK_EX)', $options);
}
return parent::stream_lock($operation);
}
public function stream_write($data) {
return $this->unsupportedError('stream_write', STREAM_REPORT_ERRORS);
}
public function stream_flush() {
return $this->unsupportedError('stream_flush', STREAM_REPORT_ERRORS);
}
public function unlink($uri) {
return $this->unsupportedError('unlink', STREAM_REPORT_ERRORS);
}
public function rename($from_uri, $to_uri) {
return $this->unsupportedError('rename', STREAM_REPORT_ERRORS);
}
public function mkdir($uri, $mode, $options) {
return $this->unsupportedError('mkdir', $options);
}
public function rmdir($uri, $options) {
return $this->unsupportedError('rmdir', $options);
}
public function unsupportedError($method, $options) {
if ($options & STREAM_REPORT_ERRORS) {
drupal_set_message("ImageCacheActionsModuleStreamWrapper is read-only and does not support $method", 'error');
watchdog(WATCHDOG_ERROR, "ImageCacheActionsModuleStreamWrapper is read-only and does not support $method");
}
return FALSE;
}
}

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,187 @@
README for the Imagecache Actions Drupal module
-----------------------------------------------
Project page: http://drupal.org/project/imagecache_actions
Current and past maintainers for Imagecache Actions:
- dman (http://drupal.org/user/33240)
- sidneyshan (http://drupal.org/user/652426)
- fietserwin (http://drupal.org/user/750928)
Release notes for 7.x-1.0
-------------------------
- If you use custom actions, run update.php.
- If you use effects that use files (mask, overlays, underlays, text fonts),
check the way they are specified. From 7.x-1.0 on, you have to specfiy the
location using one of the schemes private://, public://, module:// or
temporary://. If no scheme is specified, the file is searched for as is, thus
relative to the current directory or as an absolute path.
- Effects that use the transparency layer (e.g. mask, rounded corners) do not
automatically convert to PNG anymore. Use the "Change file format" for that.
- There's no upgrade from D6. You will have to recreate your styles manually.
Warning:
Ongoing development in the area of e.g. making the effects more consistent,
adding and/or removing parameters or redefining their meaning, might cause
forward incompatibilities with future versions. Thus, we cannot and do not
guarantee backwards compatibility or automatic upgrade paths for future
versions.
Introduction
------------
The Imagecache Actions module provides a suite of additional image effects that
can be added to image styles. Image styles let you create derivations of images
by applying (a series of) effect(s) to it. Think of resizing, desaturating,
masking, etc.
The additional effects that Imagecache Actions provides include:
- Watermark: place a image with transparency anywhere over a source picture.
- Overlay: add photo-corners etc to the image
- Text overlay: add e.g. a copyright notice to your image.
- Color-shifting: colorize images.
- Brighten/Darken.
- Alpha blending: use a grayscale image to define the transparency layer of an
image.
- Canvas manipulation: resize the canvas and add a backgroundcolor or image.
- File Format switcher: if you need tranparency in JPGs, make them PNG. If your
PNG thumbnails are 30K each, save them as JPGs.
- Rounded corners.
- TODO: complete list, check short descrptions
These effects are grouped in submodules. Just enable the ones you want to use.
TODO: list submodules and their sets of effects.
Imagecache Actions supports both the GD toolkit from Drupal core and the
Imagemagick toolkit. However, please note that Imagemagick support is not yet
complete. Please file an issue if you encounter problems in using Imagemagick.
What is imagecache_action not?
------------------------------
Imagecache Actions does not provide a new UI or new menu items. It hooks into
the already existing image styles system (from Drupal core). See
http://drupal.org/documentation/modules/image for more information about working
with images.
A note about the name of this module
------------------------------------
Image styles are part of Drupal 7 core and are the successor of the Drupal 6
imagecache module. In Drupal 6 image styles were called (imagecache) presets and
the separate effects that made up a style were called (imagecache) actions. In
porting to D7, that name has not been changed (yet).
Augmenting the Drupal core image module
---------------------------------------
This module might also provide additional features to the Drupal core image
module. Currently no such features are implemented, but they might be in the
future, think e.g. of adding a "copy image style" feature. This allows to test
D8 image module features in real life.
Solving errors in the core image handling:
- [#1554074]: scale does not work with imagemagick when dimensions are unknown?
Which toolkit to use?
---------------------
Personally, I (fieterwin) prefer the imagemagick toolkit:
- It is better in anti-aliasing. Try to rotate an image using both toolkits and
you will see what I mean.
- It does not execute in the PHP memory space, so is not restricted by the
memory_limit PHP setting.
- The GD toolkit will, at least on my Windows configuration, keep the font file
open after a text operation, so you cannot delete, move or rename it anymore.
On the other hand: the GD toolkit is always available (in the correct version),
whereas imagemagick is not always present on shared hosting or may be present in
an antique version that might give problems.
Please also note that effects may give different results depending on the
toolkit used.
Dependencies
------------
- Drupal 7.x
- Image module from Drupal core
At least 1 of the available image toolkits:
- GD toolkit from Drupal core
- Imagemagick toolkit: http://drupal.org/project/imagemagick
Installing
----------
As usual.
Usage
-----
After enabling the module:
- Assure that the Image module from core is enabled.
- Configure your toolkit and its settings at admin/config/media/image-toolkit.
- Define image styles at admin/config/media/image-styles and add 1 or more
effects as defined by this module
- Use the image styles via e.g. the formatters of image fields.
Upgrading from D6
-----------------
There's no upgrade path defined for sites upgrading from D6 to D7. This means
that you will have to manually redefine your D6 imagecache presets as D7 image
styles. Note that actually an upgrade path would have to be defined by the
imageacache module, not this imagecache actions module. However, as there is no
D7 version of imagecache that provides an upgrade, users may post an upgrade
function to the issue queue and we will incorporate it.
Backwards compatibility
-----------------------
Future releases will not be guaranteed to be backwards compatible. Implementing
Imagemagick support e.g. might give unforeseen problems that can only be solved
by changing the details of what an effect does. furhtermore current behavior of
the image mask effect is to also change the file format to png. This effect
should not do so, and thus will probably be changed in a future release. We will
document these kind of incompatibilities in the changelog and the release notes.
File form fields
----------------
A number of effects have a file form field where the editor can define a file
name to use. This canbe e.g. for overlays, masks or fonts. The file name should
be defined using either:
1 of the (enabled) scheme's:
- public://
- private:// Preferred for site specific masks, overlay's, etc, that do not need
to be shared publicly.
- module://{module_name}/{resource-name} Introduced by the imagecache_actions
module and preferred for module provided resources, like the button overlay
of the Video Embed Field Overlay module
(http://drupal.org/project/video_embed_field_overlay).
- temporary:// Unlikely to be useful, but supported anyway as all schemes are
supported.
or a relative (to the current directory, probably Drupal root) or absolute path.
Support
-------
Via the issue queue of this project at Drupal.org.
Known problems
--------------
These are better documented in the issue queue, but might be listed here (as
well).
- Underlay does not work in imagemagick if the dimensions of both images are not
equal. As a workaround first add a canvas effect with a fully transparent
background.
- Underlay/overlay: keywords in the x and y offset fields do not work.
- Underlay does still display a message about Iamgemagick not being supported.
- Brightness values outside the -250 .. 250 range are accepted.
- Check colorfields that allow a transparency component or allow to be empty to
specify fully tranparent.

View File

@@ -0,0 +1,81 @@
Imagecache Actions roadmap
--------------------------
This is a non-contractual roadmap for the D7 branch of the imagecache actions
module. Actual release dates and completed features will largely depend on
available time. So support is always welcome. Furthermore, critical bugs may
make us release more often, but that should only change the release number in
which certain features are planned, not the timeline.
Imagecache Actions 7.x-1.0
--------------------------
Targeted release date: may 2012
- Clean up D7 issue queue
- Commit posted patches from the issue queue
- Solve easy to solve bug reports
- Do some basic testing
- Reintroduce the text action, currently living in a sandbox project at
http://drupal.org/node/1090312
- Update README.txt
- Introduce CHANGELOG.txt
- Add this ROADMAP.txt
Imagecache Actions 7.x-1.1
--------------------------
Targeted release date: summer 2012
- Check hook_help
- Solve remaining outstanding bug reports
- Keep D7 issue queue clean
- Improve Imagemagick support
- Check that all effects implement all of the effects API (especially the
dimensions passthrough or not)
- Add testing. The idea is to create a set of image styles that cover all
effects and can be used to visually check for regressions.
Automated testing would be nice, but I am not sure that we can guarantee that
jpeg or png files are binary identical on each run across different computers.
But if the number of false positives is small, it would certainly be a useful
addition.
Imagecache Actions 7.x-1.2
--------------------------
Targeted release date: end of 2012
- Look at outstanding feature requests
- Continue to keep the D7 issue queue clean
- Keep improving Imagemagick support, a.o:
- Try to get insight into what version of imagemagick is required by what
effect.
- Document which effects might produce different results.
- Refactor code
- Extract common form fields
- Extract common error handling
- Clean up comments, todo's, etc.
- Doxygen code documentation according to latest standards
- Increase the amount of lazy loaded code
Imagecache Actions 7.x-2.x
--------------------------
Targeted release date: ...
- Can we refactor effects into auto loading class based plugins? What does CTools
offer in these?
- If so, will it make implementing new effects much simpler? Because that is
what we should strive for.
- Should we rename the module to image_effects?
- If we manage to provide an easy to use plugin system, we should strive to merge
with other modules that provide effects:
- FiltersIE module (http://drupal.org/project/filtersie)
- Imagecache Effects: http://drupal.org/project/imagecache_effects (D6 only, but
are their effects more advanced then our counterparts or do they add new
effects?)
- Image watermark: http://drupal.org/project/watermark (D5 only, but is their
watermark effect more advanced?)
- ImageCache Scale-9 Actions: http://drupal.org/project/imagecache_scale9actions
- etc.

View File

@@ -0,0 +1,16 @@
name = Imagecache Autorotate
description = Autorotate image based on EXIF Orientation.
package = Media
core = 7.x
dependencies[] = image
files[] = imagecache_autorotate.install
files[] = imagecache_autorotate.module
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,27 @@
<?php
/**
* @file
* Ensure Imagecache recognises our new actions, per http://drupal.org/node/290101.
*/
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function imagecache_autorotate_enable() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function imagecache_autorotate_disable() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
}

View File

@@ -0,0 +1,144 @@
<?php
/**
* @file
* Autorotate image based on EXIF Orientation tag.
* http://sylvana.net/jpegcrop/exif_orientation.html
*
* This mini-module contributed by jonathan_hunt http://drupal.org/user/28976
* September 1, 2009
*
* Tweaked by dman to add documentation
*/
/* In Adobe PNGs and TIFF, this information MAY be present in the XMP
* metadata like so:
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
<tiff:Orientation>6</tiff:Orientation>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
*/
function imagecache_autorotate_image_effect_info() {
$effects = array();
$effects['imagecache_autorotate'] = array(
'label' => t('Autorotate'),
'help' => t('Add autorotate image based on EXIF Orientation.'),
'effect callback' => 'imagecache_autorotate_image',
'dimensions callback' => 'imagecache_autorotate_dimensions',
);
return $effects;
}
/**
* Declare dummy form, since we have no settings.
*
* @todo: A form is no longer needed, if this information still needs to be
* displayed it should probably be moved to help.
*/
function imagecache_autorotate_form() {
$form = array();
$form['help'] = array(
'#type' => 'markup',
'#value' => "<p>
<b>There are no user-configurable options for this process.</b>
</p><p>
Certain cameras can embed <em>orientation</em> information into image
files when they save them. This information is embedded in an EXIF tag
and can be used to rotate images to their correct position for display.
</p><p>
<em>Not all cameras or images contain this information.</em>
This process is only useful for those that do.
</p><p>
The expected/supported values are
<br/><strong>Tag</strong>: <code>0x0112 Orientation</code>
</p>
<ul>
<li>1 = Horizontal (normal)</li>
<li>3 = Rotate 180</li>
<li>6 = Rotate 90 CW</li>
<li>8 = Rotate 270 CW</li>
</ul>
<p>
<a href='http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html'>
EXIF Reference</a>
</p>
",
);
return $form;
}
/**
* Autorotate image based on EXIF Orientation tag.
*
* See code at
* http://sylvana.net/jpegcrop/exif_orientation.html
*
* and reference at
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
*
* @todo:
* Add horizontal and vertical flips etc.
* Need to create sample set for tests.
*/
function imagecache_autorotate_image($image, $data) {
// Test to see if EXIF is supported for image type (e.g. not PNG).
if ($image->info['mime_type'] == 'image/jpeg') {
if (!function_exists('exif_read_data')) {
watchdog('image', 'The image %file could not be auto-rotated because the exif_read_data() function is not available in this PHP installation. You probably will have to enable the exif extension.', array('%file' => $image->source));
return FALSE;
}
$exif = exif_read_data(drupal_realpath($image->source));
if (isset($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 3:
$degrees = 180;
break;
case 6:
$degrees = 90;
break;
case 8:
$degrees = 270;
break;
default:
$degrees = 0;
}
if ($degrees != 0) {
$org_width = $image->info['width'];
$org_height = $image->info['height'];
image_rotate($image, $degrees);
if (($degrees === 90 || $degrees === 270) && $image->info['width'] === $org_width) {
// The toolkit failed to alter the dimensions (imagemagick currently
// fails to do so). So we do it ourselves.
$image->info['width'] = $org_height;
$image->info['height'] = $org_width;
}
}
}
}
return TRUE;
}
/**
* Image dimensions callback; Auto-rotate.
*
* @param $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
* @param $data
* An array of attributes to use when performing the rotate effect containing
* the following items:
* - "degrees": The number of (clockwise) degrees to rotate the image.
* - "random": A boolean indicating that a random rotation angle should be
* used for this image. The angle specified in "degrees" is used as a
* positive and negative maximum.
*/
function imagecache_autorotate_dimensions(array &$dimensions, array $data) {
$dimensions['width'] = $dimensions['height'] = NULL;
}

View File

@@ -0,0 +1,857 @@
<?php
/**
* @file Helper functions for the text2canvas action for imagecache
*
* @author Dan Morrison http://coders.co.nz
*
* Individually configurable rounded corners logic contributed by canaryMason
* 2009 03 http://drupal.org/node/402112
*
* Better algorithm for trimming rounded corners from donquixote
* 2009 09 http://drupal.org/node/564036
*
*/
if (! function_exists('image_overlay') ) {
module_load_include('inc', 'imagecache_actions', 'image_overlay');
}
if (! function_exists('imagecache_actions_pos_form') ) {
module_load_include('inc', 'imagecache_actions', 'utility-form');
}
if (! function_exists('imagecache_actions_keyword_filter') ) {
module_load_include('inc', 'imagecache_actions', 'utility');
}
////////////////////////////////////////////////
// IMAGEMASK
/**
* Use a given image to mask the current canvas
*
* Implementation of imagecache_hook_form()
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_imagemask_form($data) {
// TODO: add offset/positioning/scaling support - currently the mask is applied to the supplied image without resizing and positioned at (0,0)
$form = array();
$form['effect_help_text'] = array(
'#type' => 'item',
'#title' => t('Image mask'),
'#description' => t('<p>This effect will add (or replace) a
transparency channel to your image, thereby converting it to a 32 bit PNG.
The mask file should be a grayscale image where black is fully transparent and
white is fully opaque. The referenced mask will be applied to the top left of
the image.</p>'),
);
$form['path'] = array(
'#type' => 'textfield',
'#title' => t('Mask file name'),
'#default_value' => isset($data['path']) ? $data['path'] : '',
'#description' => imagecache_actions_file_field_description(),
'#element_validate' => array('imagecache_actions_validate_file'),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_imagemask_summary($variables) {
$data = $variables['data'];
return 'file: ' . $data['path'];
}
/**
* Apply the given image file to the canvas as a mask
*
* Implementation of hook_image()
*
* @param $image
* @param $data
*/
function canvasactions_imagemask_image(&$image, $data = array()) {
$mask = imagecache_actions_image_load($data['path'], $image->toolkit);
if ($mask) {
// TODO: (sydneyshan) Consider best way to add offset support - I assume we
// would position the mask somewhere (top/left/offset px etc) and choose if
// the surrounding area is white or black (opaque or transparent) using an
// extra form element (radio). Assess exsiting positioning code first to
// reduce duplication of code. Pass the results to the following function as
// array($mask, $data). Perhaps add a 'scale mask to fit image'/'scale image
// to fit mask'/'no scale' radio group?
return image_toolkit_invoke('imagemask', $image, array($mask));
}
return FALSE;
}
/**
* Apply an image-based transparency mask using GD.
*
* &$image is an array expected to contain the details of the image to be masked
* $mask is an array expected to contain the details of the mask image
*/
function image_gd_imagemask($image, $mask = array()) {
$newPicture = imagecreatetruecolor( $image->info['width'], $image->info['height'] );
imagesavealpha( $newPicture, true );
imagealphablending( $newPicture, true);
$transparent = imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 );
imagefill( $newPicture, 0, 0, $transparent);
// Perform pixel-based alpha map application
for( $x = 0; $x < $image->info['width']; $x++ ) {
for( $y = 0; $y < $image->info['height']; $y++ ) {
// Deal with images with mismatched sizes
if ($x >= $mask->info['width'] || $y >= $mask->info['height'] ) {
imagesetpixel( $newPicture, $x, $y, $transparent );
}
else {
$alpha = imagecolorsforindex( $mask->resource, imagecolorat( $mask->resource, $x, $y ) );
$alpha = 127 - floor( $alpha[ 'red' ] / 2 );
$color = imagecolorsforindex( $image->resource, imagecolorat( $image->resource, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
}
}
}
// Copy back to original picture
imagedestroy( $image->resource );
$image->resource = $newPicture;
return TRUE;
}
/**
* Apply an image-based transparency mask using ImageMagick.
*
* &$image is an array expected to contain the details of the image to be masked
* $mask is an array expected to contain the details of the mask image
*/
function image_imagemagick_imagemask($image, $mask = NULL) {
$image->ops[] = escapeshellarg($mask->source) . ' -alpha Off -compose CopyOpacity -composite';
return TRUE;
}
////////////////////////////////////////////////
/**
* Implementation of imagecache_hook_form()
*
* Settings for preparing a canvas.
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_definecanvas_form($data) {
module_load_include('inc', 'imagecache_actions', 'utility-color');
$defaults = array(
'RGB' => array(
'HEX' => '#333333',
),
'under' => TRUE,
'exact' => array(
'width' => '',
'height' => '',
'xpos' => 'center',
'ypos' => 'center',
),
'relative' => array(
'leftdiff' => '',
'rightdiff' => '',
'topdiff' => '',
'bottomdiff' => '',
),
);
$data = array_merge($defaults, (array) $data);
$form = array(
'RGB' => imagecache_rgb_form($data['RGB']),
'help' => array(
'#type' => 'markup',
'#value' => t('Enter no color value for transparent. This will have the effect of adding clear margins around the image.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
),
'under' => array(
'#type' => 'checkbox',
'#title' => t('Resize canvas <em>under</em> image (possibly cropping)'),
'#default_value' => $data['under'],
'#description' => t('If <em>not</em> set, this will create a solid flat layer, probably totally obscuring the source image'),
),
);
$form['info'] = array('#value' => t('Enter values in ONLY ONE of the below options. Either exact or relative. Most values are optional - you can adjust only one dimension as needed. If no useful values are set, the current base image size will be used.'));
$form['exact'] = array(
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#title' => 'Exact size',
'help' => array(
'#type' => 'markup',
'#value' => t('Set the canvas to a precise size, possibly cropping the image. Use to start with a known size.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
),
'width' => array(
'#type' => 'textfield',
'#title' => t('Width'),
'#default_value' => $data['exact']['width'],
'#description' => t('Enter a value in pixels or percent'),
'#size' => 5,
),
'height' => array(
'#type' => 'textfield',
'#title' => t('Height'),
'#default_value' => $data['exact']['height'],
'#description' => t('Enter a value in pixels or percent'),
'#size' => 5,
),
);
$form['exact'] = array_merge($form['exact'], imagecache_actions_pos_form($data['exact']));
if (! $data['exact']['width'] && !$data['exact']['height']) {
$form['exact']['#collapsed'] = TRUE;
}
$form['relative'] = array(
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#title' => t('Relative size'),
'help' => array(
'#type' => 'markup',
'#value' => '<p>' . t('Set the canvas to a relative size, based on the current image dimensions. Use to add simple borders or expand by a fixed amount. Negative values may crop the image.') . '</p>',
),
'leftdiff' => array(
'#type' => 'textfield',
'#title' => t('left difference'),
'#default_value' => $data['relative']['leftdiff'],
'#size' => 6,
'#description' => t('Enter an offset in pixels.'),
),
'rightdiff' => array(
'#type' => 'textfield',
'#title' => t('right difference'),
'#default_value' => $data['relative']['rightdiff'],
'#size' => 6,
'#description' => t('Enter an offset in pixels.'),
),
'topdiff' => array(
'#type' => 'textfield',
'#title' => t('top difference'),
'#default_value' => $data['relative']['topdiff'],
'#size' => 6,
'#description' => t('Enter an offset in pixels.'),
),
'bottomdiff' => array(
'#type' => 'textfield',
'#title' => t('bottom difference'),
'#default_value' => $data['relative']['bottomdiff'],
'#size' => 6,
'#description' => t('Enter an offset in pixels.'),
),
);
if (! $data['relative']['leftdiff'] && !$data['relative']['rightdiff'] && !$data['relative']['topdiff'] && !$data['relative']['bottomdiff']) {
$form['relative']['#collapsed'] = TRUE;
}
$form['#submit'][] = 'canvasactions_definecanvas_form_submit';
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_definecanvas_summary($variables) {
$data = $variables['data'];
if ($data['exact']['width'] || $data['exact']['height']) {
$w = !empty($data['exact']['width']) ? $data['exact']['width'] : '100%';
$h = !empty($data['exact']['height']) ? $data['exact']['height'] : '100%';
$x = !empty($data['exact']['xpos']) ? $data['exact']['xpos'] : '0';
$y = !empty($data['exact']['ypos']) ? $data['exact']['ypos'] : '0';
$output = "{$w}x{$h} ($x, $y)";
}
else {
$output = ' left:' . $data['relative']['leftdiff'];
$output .= ' right:' . $data['relative']['rightdiff'];
$output .= ' top:' . $data['relative']['topdiff'];
$output .= ' bottom:' . $data['relative']['bottomdiff'];
}
$output .= theme('imagecacheactions_rgb', array('RGB' => $data['RGB']));
$output .= ($data['under']) ? t(" <b>under</b> image ") : t(" <b>over</b> image ");
return $output;
}
/**
* Implementation of hook_image()
*
* Creates a solid background canvas
*
* Process the imagecache action on the passed image
*
* @param $image
* array defining an image file, including :
*
* $image- >source as the filename,
*
* $image->info array
*
* $image->resource handle on the image object
*/
function canvasactions_definecanvas_effect($image, $data) {
// May be given either exact or relative dimensions.
if ($data['exact']['width'] || $data['exact']['height']) {
// Allows only one dimension to be used if the other is unset.
if (!$data['exact']['width']) {
$data['exact']['width'] = $image->info['width'];
}
if (!$data['exact']['height']) {
$data['exact']['height'] = $image->info['height'];
}
$targetsize['width'] = imagecache_actions_percent_filter($data['exact']['width'], $image->info['width']);
$targetsize['height'] = imagecache_actions_percent_filter($data['exact']['height'], $image->info['height']);
$targetsize['left'] = image_filter_keyword($data['exact']['xpos'], $targetsize['width'], $image->info['width']);
$targetsize['top'] = image_filter_keyword($data['exact']['ypos'], $targetsize['height'], $image->info['height']);
}
else {
// calculate relative size
$targetsize['width'] = $image->info['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff'];
$targetsize['height'] = $image->info['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff'];
$targetsize['left'] = $data['relative']['leftdiff'];
$targetsize['top'] = $data['relative']['topdiff'];
}
// convert from hex (as it is stored in the UI)
if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
$data['RGB'] = array_merge($data['RGB'], $deduced);
}
// All the maths is done, now defer to the api toolkits;
$data['targetsize'] = $targetsize;
$success = image_toolkit_invoke('definecanvas', $image, array($data));
if ($success) {
$image->info['width'] = $targetsize['width'];
$image->info['height'] = $targetsize['height'];
}
return $success;
}
function canvasactions_definecanvas_dimensions(array &$dimensions, array $data) {
// May be given either exact or relative dimensions.
if ($data['exact']['width'] || $data['exact']['height']) {
// Allows only one dimension to be used if the other is unset.
if (!$data['exact']['width']) {
$data['exact']['width'] = $dimensions['width'];
}
if (!$data['exact']['height']) {
$data['exact']['height'] = $dimensions['height'];
}
$dimensions['width'] = imagecache_actions_percent_filter($data['exact']['width'], $dimensions['width']);
$dimensions['height'] = imagecache_actions_percent_filter($data['exact']['height'], $dimensions['height']);
}
else {
// calculate relative size
$dimensions['width'] = $dimensions['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff'];
$dimensions['height'] = $dimensions['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff'];
}
}
/**
* Draw a color (or transparency) behind an image
*
* $targetsize is an array expected to contain a width,height and a left,top
* offset.
*/
function image_gd_definecanvas($image, $data = array()) {
$targetsize = $data['targetsize'];
$RGB = $data['RGB'];
$newcanvas = imagecreatetruecolor($targetsize['width'], $targetsize['height']);
imagesavealpha($newcanvas, TRUE);
imagealphablending($newcanvas, FALSE);
imagesavealpha($image->resource, TRUE);
if ($RGB['HEX']) {
// Set color, allow it to define transparency, or assume opaque.
$background = imagecolorallocatealpha($newcanvas, $RGB['red'], $RGB['green'], $RGB['blue'], $RGB['alpha']);
}
else {
// No color, attempt transparency, assume white
$background = imagecolorallocatealpha($newcanvas, 255, 255, 255, 127);
}
imagefilledrectangle($newcanvas, 0, 0, $targetsize['width'], $targetsize['height'], $background);
# imagealphablending($newcanvas, TRUE);
if ($data['under']) {
$canvas_object = (object) array(
'resource' => $newcanvas,
'info' => array(
'width' => $targetsize['width'],
'height' => $targetsize['height'],
'mime_type' => $image->info['mime_type'],
'extension' => $image->info['extension'],
),
'toolkit' => $image->toolkit,
);
image_overlay($image, $canvas_object, $targetsize['left'], $targetsize['top'], 100, TRUE);
}
else {
$image->resource = $newcanvas;
}
return TRUE;
}
/**
* Draw a color (or transparency) behind an image
* $targetsize is an array expected to contain a width,height and a left,top
* offset.
*
* See http://www.imagemagick.org/script/command-line-options.php#extent
* @todo: reset gravity?
*/
function image_imagemagick_definecanvas($image, $data = array()) {
$backgroundcolor = $data['RGB']['HEX'] != '' ? '#'. $data['RGB']['HEX'] : 'None';
$image->ops[] = '-background '. escapeshellarg($backgroundcolor);
$compose_operator = $data['under'] ? 'src-over' : 'dst-over';
$image->ops[] = "-compose $compose_operator";
$targetsize = $data['targetsize'];
$geometry = sprintf('%dx%d', $targetsize['width'], $targetsize['height']);
if ($targetsize['left'] || $targetsize['top']) {
$geometry .= sprintf('%+d%+d', -$targetsize['left'], -$targetsize['top']);
}
$image->ops[] = "-extent $geometry";
return TRUE;
}
////////////////////////////////////////////////
/**
* Place a given image under the current canvas
*
* Implementation of imagecache_hook_form()
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_canvas2file_form($data) {
// if (image_get_toolkit() != 'gd') {
// drupal_set_message('Overlays are not currently supported by using imagemagick. This effect requires GD image toolkit only.', 'warning');
// }
$defaults = array(
'xpos' => '0',
'ypos' => '0',
'alpha' => '100',
'path' => '',
'dimensions' => 'original',
);
$data = array_merge($defaults, (array) $data);
$form = imagecache_actions_pos_form($data);
$form['alpha'] = array(
'#type' => 'textfield',
'#title' => t('opacity'),
'#default_value' => $data['alpha'],
'#size' => 6,
'#description' => t('Opacity: 0-100. Be aware that values other than 100% may be slow to process.'),
);
$form['path'] = array(
'#type' => 'textfield',
'#title' => t('file name'),
'#default_value' => $data['path'],
'#description' => imagecache_actions_file_field_description(),
'#element_validate' => array('imagecache_actions_validate_file'),
);
$form['dimensions'] = array(
'#type' => 'radios',
'#title' => t('final dimensions'),
'#default_value' => $data['dimensions'],
'#options' => array(
'original' => 'original (dimensions are retained)',
'background' => 'background (image will be forced to match the size of the background)',
'minimum' => 'minimum (image may be cropped)',
'maximum' => 'maximum (image may end up with gaps)',
),
'#description' => t('What to do when the background image is a different size from the source image. Backgrounds are not tiled, but may be arbitrarily large.'),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_canvas2file_summary($variables) {
$data = $variables['data'];
$file = $data['path'];
return "xpos:{$data['xpos']} , ypos:{$data['ypos']} alpha:{$data['alpha']}%. file: $file, dimensions:{$data['dimensions']}";
}
/**
* Place the source image on the current background
*
* Implementation of hook_image()
*
* Note - this is currently incompatable with imagemagick, due to the way it
* addresses $image->resource directly - a gd only thing.
*
* @param $image
* @param $data
*/
function canvasactions_canvas2file_image(&$image, $data = array()) {
$underlay = imagecache_actions_image_load($data['path'], $image->toolkit);
if ($underlay) {
// To handle odd sizes, we will resize/crop the background image to the
// desired dimensions before starting the merge. The built-in
// imagecopymerge, and the watermark library both do not allow overlays to
// be bigger than the target.
// Adjust size
$crop_rules = array(
'xoffset' => 0,
'yoffset' => 0,
);
if (empty($data['dimensions'])) {
$data['dimensions'] = 'original';
}
switch ($data['dimensions']) {
case 'original':
// If the underlay is smaller than the target size,
// then when preparing the underlay by cropping it,
// the offsets may need to be negative
// which will produce a 'cropped' image larger than the original.
// In this case, we need to calculate the position of the bg image
// in relation to the space it will occupy under the top layer
#$crop_rules['xoffset'] = $underlay->info['width'] - $image->info['width'] ;
$crop_rules['width'] = $image->info['width'];
$crop_rules['height'] = $image->info['height'];
break;
case 'background':
$crop_rules['width'] = $underlay->info['width'];
$crop_rules['height'] = $underlay->info['height'];
break;
case 'minimum':
$crop_rules['width'] = min($underlay->info['width'], $image->info['width']);
$crop_rules['height'] = min($underlay->info['height'], $image->info['height']);
break;
case 'maximum':
$crop_rules['width'] = max($underlay->info['width'], $image->info['width']);
$crop_rules['height'] = max($underlay->info['height'], $image->info['height']);
break;
}
// imageapi crop assumes upsize is legal.
// Crop both before processing to avoid unwanted processing.
image_crop_effect($underlay, $crop_rules);
# BUG - this doesn't position either
// Actually this fails because imagecache_crop fills it with solid color when 'cropping' to a larger size.
#imagecache_crop_image($image, $crop_rules);
#dpm(get_defined_vars());
// This func modifies the underlay image by ref, placing the current canvas on it
if (image_overlay($image, $underlay, $data['xpos'], $data['ypos'], $data['alpha'], TRUE)) {
#$image->resource = $underlay->resource;
$image = $underlay;
return TRUE;
}
}
return FALSE;
}
/**
* Image dimensions callback; canvas2file (underlay/background).
*
* @param array $dimensions
* Dimensions to be modified - an associative array containing the items
* 'width' and 'height' (in pixels).
* @param $data
* An associative array containing the effect data.
*/
function canvasactions_canvas2file_dimensions(array &$dimensions, array $data) {
if ($data['dimensions'] !== 'original') {
$underlay = imagecache_actions_image_load($data['path']);
if ($underlay) {
switch ($data['dimensions']) {
case 'background':
$dimensions['width'] = $underlay->info['width'];
$dimensions['height'] = $underlay->info['height'];
break;
case 'minimum':
$dimensions['width'] = min($underlay->info['width'], $dimensions['width']);
$dimensions['height'] = min($underlay->info['height'], $dimensions['height']);
break;
case 'maximum':
$dimensions['width'] = max($underlay->info['width'], $dimensions['width']);
$dimensions['height'] = max($underlay->info['height'], $dimensions['height']);
break;
}
}
}
}
/**
* Place a given image on top of the current canvas
*
* Implementation of imagecache_hook_form()
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_file2canvas_form($data) {
$defaults = array(
'xpos' => '',
'ypos' => '',
'alpha' => '100',
'path' => '',
);
$data = array_merge($defaults, (array) $data);
$form = array(
'help' => array(
'#type' => 'markup',
'#value' => t('Note that using a transparent overlay that is larger than the source image may result in unwanted results - a solid background.'),
),
);
$form += imagecache_actions_pos_form($data);
$form['alpha'] = array(
'#type' => 'textfield',
'#title' => t('opacity'),
'#default_value' => $data['alpha'],
'#size' => 6,
'#description' => t('Opacity: 0-100. <b>Warning:</b> Due to a limitation in the GD toolkit, using an opacity other than 100% requires the system to use an algorithm that\'s much slower than the built-in functions. If you want partial transparency, you are better to use an already-transparent png as the overlay source image.'),
);
$form['path'] = array(
'#type' => 'textfield',
'#title' => t('file name'),
'#default_value' => $data['path'],
'#description' => imagecache_actions_file_field_description(),
'#element_validate' => array('imagecache_actions_validate_file'),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_file2canvas_summary($variables) {
$data = $variables['data'];
return '<strong>' . $data['path'] . '</strong> x:' . $data['xpos'] . ', y:' . $data['ypos'] . ' alpha:' . (@$data['alpha'] ? $data['alpha'] : 100) . '%';
}
/**
* Place the source image on the current background
*
* Implementation of hook_image()
*
*
* @param $image
* @param $data
*/
function canvasactions_file2canvas_image($image, $data = array()) {
$overlay = imagecache_actions_image_load($data['path']);
if ($overlay) {
if (!isset($data['alpha']) ) {
$data['alpha'] = 100;
}
return image_overlay($image, $overlay, $data['xpos'], $data['ypos'], $data['alpha']);
}
return FALSE;
}
///////////////////////////////////////////////////////////////////
/**
* Place the source image on top of the current canvas
*
* Implementation of imagecache_hook_form()
*
*
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_source2canvas_form($data) {
$defaults = array(
'xpos' => '',
'ypos' => '',
'alpha' => '100',
'path' => '',
);
$data = array_merge($defaults, (array) $data);
$form = imagecache_actions_pos_form($data);
$form['alpha'] = array(
'#type' => 'textfield',
'#title' => t('opacity'),
'#default_value' => $data['alpha'],
'#size' => 6,
'#description' => t('Opacity: 0-100.'),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_source2canvas_summary($variables) {
$data = $variables['data'];
return 'xpos:' . $data['xpos'] . ', ypos:' . $data['ypos'] . ' alpha:' . $data['alpha'] . '%';
}
/**
* Place the source image on the current background
*
* Implementation of hook_image()
*
*
* @param $image
* @param $data
*/
function canvasactions_source2canvas_image($image, $data = array()) {
$overlay = image_load($image->source, $image->toolkit);
return image_overlay($image, $overlay, $data['xpos'], $data['ypos'], $data['alpha']);
}
/**
* Switch between presets depending on logic
*
* Implementation of imagecache_hook_form()
*
* @param $data array of settings for this action
* @return a form definition
*/
function canvasactions_aspect_form($data) {
$defaults = array(
'ratio_adjustment' => 1,
'portrait' => NULL,
'landscape' => NULL,
);
$data = array_merge($defaults, (array)$data);
$form = array(
'help' => array(
'#type' => 'markup',
'#value' => t('You must create the two presets to use <em>before</em> enabling this process.'),
)
);
$styles = image_style_options(TRUE);
$form['portrait'] = array(
'#type' => 'select',
'#title' => t('Style to use if the image is portrait (vertical)'),
'#default_value' => $data['portrait'],
'#options' => $styles,
);
$form['landscape'] = array(
'#type' => 'select',
'#title' => t('Style to use if the image is landscape (horizontal)'),
'#default_value' => $data['landscape'],
'#options' => $styles,
);
$form['ratio_adjustment'] = array(
'#type' => 'textfield',
'#title' => t('Ratio Adjustment (advanced)'),
'#size' => 3,
'#default_value' => $data['ratio_adjustment'],
'#description' => t("
This allows you to bend the rules for how different the proportions need to be to trigger the switch.
<br/>If the (width/height)*n is greater than 1, use 'landscape', otherwise use 'portrait'.
<br/>When n = 1 (the default) it will switch between portrait and landscape modes.
<br/>If n > 1, images that are slightly wide will still be treated as portraits.
If n < 1 then blunt portraits will be treated as landscape.
"),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_aspect_summary($variables) {
$data = $variables['data'];
$ratio_adjustment = '';
if ($data['ratio_adjustment'] != 1) {
$ratio_adjustment = " (switch at 1:{$data['ratio_adjustment']})";
}
return 'Portrait size: <strong>'. $data['portrait'] . '</strong>. Landscape size: <strong>'. $data['landscape'] .'</strong>'. $ratio_adjustment ;
}
/**
* Choose the action and trigger that.
*
* Implementation of hook_image()
*
* @param $image
* @param $data
*/
function canvasactions_aspect_image($image, $data = array()) {
$ratio_adjustment = 0 + $data['ratio_adjustment'];
if (!$ratio_adjustment) {
$ratio_adjustment = 1;
}
$aspect = $image->info['width'] / $image->info['height'];
// width / height * adjustment. If > 1, it's wide.
$style_name = (($aspect * $ratio_adjustment) > 1) ? $data['landscape'] : $data['portrait'];
$style = image_style_load($style_name);
if (empty($style_name)) {
// Required preset has gone missing?
watchdog('imagecache_canvasactions', "When running 'aspect' action, I was unable to load sub-action %style_name. Either it's been deleted or the DB needs an update", array('%style_name' => $style_name), WATCHDOG_ERROR);
return FALSE;
}
// Run the preset actions ourself.
// Cannot invoke a preset from the top as it handles filenames, not image objects.
// Ripped from imagecache_build_derivative()
foreach ($style['effects'] as $sub_effect) {
// These actions really should interpret the parameters themselves.
foreach (array('height', 'width') as $param) {
if (isset($sub_effect['data'][$param])) {
$sub_effect['data'][$param] = imagecache_actions_percent_filter($sub_effect['data'][$param], $image->info[$param]);
}
}
foreach (array(
'xoffset' => 'width',
'yoffset' => 'height',
) as $param => $direction) {
if (isset($sub_effect['data'][$param])) {
$sub_effect['data'][$param] = image_filter_keyword($sub_effect['data'][$param], $image->info[$direction], $sub_effect['data'][$direction]);
}
}
image_effect_apply($image, $sub_effect);
}
return TRUE;
}
/**
* Image dimensions callback; Aspect.
*
* @param array $dimensions
* Dimensions to be modified - an associative array containing the items
* 'width' and 'height' (in pixels).
* @param $data
* An associative array containing the effect data.
*/
function canvasactions_aspect_dimensions(array &$dimensions, array $data) {
// @todo: use callbacks or passthrough setting from the styles themselves?
$dimensions['width'] = NULL;
$dimensions['height'] = NULL;
}

View File

@@ -0,0 +1,31 @@
name = Imagecache Canvas Actions
description = Actions for manipulating image canvases layers, including watermark and background effect. Also an aspect switcher (portrait/landscape)
package = Media
core = 7.x
dependencies[] = imagecache_actions
dependencies[] = image
files[] = canvasactions.inc
files[] = imagecache_canvasactions.install
files[] = imagecache_canvasactions.module
files[] = rounded_corners.inc
files[] = tests/cheap_dropshadow.imagecache_preset.inc
files[] = tests/keyword_positioning.imagecache_preset.inc
files[] = tests/positioned_underlay.imagecache_preset.inc
files[] = tests/rotate_alpha.imagecache_preset.inc
files[] = tests/rotate_alpha_gif.imagecache_preset.inc
files[] = tests/rotate_scale.imagecache_preset.inc
files[] = tests/rotate_scale_alpha.imagecache_preset.inc
files[] = tests/rounded.imagecache_preset.inc
files[] = tests/rounded_bl.imagecache_preset.inc
files[] = tests/rounded_flattened.imagecache_preset.inc
files[] = tests/watermark_100.imagecache_preset.inc
files[] = tests/watermark_50.imagecache_preset.inc
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,27 @@
<?php
/**
* @file Set up new canvas actions. Tell imagecache.module about them
*/
/**
* Need to flush the cache when this module is enabled or disabled
*/
function imagecache_canvasactions_install() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
drupal_set_message(t('Additional image style actions should now be available in the presets !settings_link', array('!settings_link' => l(t('settings'), 'admin/config/media/image-styles'))));
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function imagecache_canvasactions_uninstall() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* @file A collection of canvas (layer) type manipulations for imagecache -
* including "Watermark"
*
* Based on first draft of the code by Dimm (imagecache.module 5--1)
* http://drupal.org/node/184816
*
* Rewritten and ported to Imagecache actions API (imagecache.module 5--2) by
* dman http://coders.co.nz/
*
*
* Notes about imagecache action extensions. For each action:
*
* 1: Implement imagecache_HOOK_form($formdata) to define the config form.
*
* 1a: Implement theme_imagecache_HOOK_form if needed - optional
*
* 2: Implement imagecache_HOOK_image($image, $data) to DO the process
*
* 3: Implement theme_imagecache_HOOK($element) to return a text description of
* the setting
*
* 4: Declare the action in HOOK_imagecache_actions()
*
*
* API ref for hook_image()
*
* @param $image array defining an image file, including :
*
* $image- >source as the filename,
*
* $image->info array
*
* $image->resource handle on the image object
*
* @param $action array of settings as defined in your form.
*
*/
// During devel, caching is pointless. Flush it
// imagecache_action_definitions(TRUE);
if (! function_exists('imagecache_actions_calculate_relative_position') ) {
module_load_include('inc', 'imagecache_canvasactions', 'utility');
}
// @todo There doesn't seem to be a way to specify a file in hook_image_effect_info
// so placing this here for the time being.
module_load_include('inc', 'imagecache_canvasactions', 'canvasactions');
module_load_include('inc', 'imagecache_canvasactions', 'rounded_corners');
// imageapi extensions
module_load_include('inc', 'imagcache_actions', 'image_overlay.inc');
function imagecache_canvasactions_image_effect_info() {
$effects = array();
$effects['canvasactions_definecanvas'] = array(
'label' => t('Define canvas'),
'help' => t('Define the size of the working canvas and background color, this controls the dimensions of the output image.'),
'effect callback' => 'canvasactions_definecanvas_effect',
'dimensions callback' => 'canvasactions_definecanvas_dimensions',
'form callback' => 'canvasactions_definecanvas_form',
'summary theme' => 'canvasactions_definecanvas_summary',
);
$effects['canvasactions_imagemask'] = array(
'label' => t('Image mask'),
'help' => t(' Choose the file image you wish to use as a mask, and apply it to the canvas.'),
'effect callback' => 'canvasactions_imagemask_image',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_imagemask_form',
'summary theme' => 'canvasactions_imagemask_summary',
);
$effects['canvasactions_file2canvas'] = array(
'label' => t('Overlay (watermark)'),
'help' => t('Choose the file image you wish to use as an overlay, and position it in a layer on top of the canvas.'),
'effect callback' => 'canvasactions_file2canvas_image',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_file2canvas_form',
'summary theme' => 'canvasactions_file2canvas_summary',
);
$effects['canvasactions_canvas2file'] = array(
'label' => t('Underlay (background)'),
'help' => t('Choose the file image you wish to use as an background, and position the processed image on it.'),
'effect callback' => 'canvasactions_canvas2file_image',
'dimensions callback' => 'canvasactions_canvas2file_dimensions',
'form callback' => 'canvasactions_canvas2file_form',
'summary theme' => 'canvasactions_canvas2file_summary',
);
$effects['canvasactions_source2canvas'] = array(
'label' => t('Overlay: source image to canvas'),
'help' => t('Places the source image onto the canvas for compositing.'),
'effect callback' => 'canvasactions_source2canvas_image',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_source2canvas_form',
'summary theme' => 'canvasactions_source2canvas_summary',
);
$effects['canvasactions_roundedcorners'] = array(
'label' => t('Rounded Corners'),
'help' => t('This is true cropping, not overlays, so the result <em>can</em> be transparent.'),
'effect callback' => 'canvasactions_roundedcorners_image',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_roundedcorners_form',
'summary theme' => 'canvasactions_roundedcorners_summary',
);
$effects['canvasactions_aspect'] = array(
'label' => t('Aspect switcher'),
'help' => t('Use different effects depending on whether the image is landscape of portrait shaped. This re-uses other preset definitions, and just chooses between them based on the rule.'),
'effect callback' => 'canvasactions_aspect_image',
'dimensions callback' => 'canvasactions_aspect_dimensions',
'form callback' => 'canvasactions_aspect_form',
'summary theme' => 'canvasactions_aspect_summary',
);
return $effects;
}
/**
* Need to register the theme functions we expect to use
*/
function imagecache_canvasactions_theme() {
$util_dir = drupal_get_path('module', 'imagecache_actions');
return array(
'canvasactions_definecanvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL),
),
'canvasactions_imagemask_summary' => array(
'file' => 'canvasactions.inc',
'arguments' => array('element' => NULL),
),
'canvasactions_file2canvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL),
),
'canvasactions_source2canvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL),
),
'canvasactions_canvas2file_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL),
),
'canvasactions_roundedcorners_summary' => array(
'file' => 'rounded_corners.inc',
'variables' => array('data' => NULL),
),
'canvasactions_aspect_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL),
),
);
}

View File

@@ -0,0 +1,325 @@
<?php
/**
* @file Routines for rounded corners
*/
/**
* Set radius for corner rounding
*
* Implementation of imagecache_hook_form()
*
* @param $action array of settings for this action
* @return a form definition
*/
function canvasactions_roundedcorners_form($action) {
if (image_get_toolkit() != 'gd') {
drupal_set_message('Rounded corners are not currently supported on all versions of imagemagick. This effect works best with GD image toolkit only.', 'warning');
}
drupal_add_js(drupal_get_path('module', 'imagecache_actions') . '/imagecache_actions.jquery.js');
$defaults = array(
'radius' => '16',
#'antialias' => TRUE,
'independent_corners_set' => array(
'independent_corners' => FALSE,
'radii' => array(
'tl' => 0,
'tr' => 0,
'bl' => 0,
'br' => 0,
),
),
);
$action = array_merge($defaults, (array) $action);
$form['radius'] = array(
'#type' => 'textfield',
'#title' => t('radius'),
'#default_value' => $action['radius'],
'#size' => 2,
);
$form['independent_corners_set'] = array(
'#type' => 'fieldset',
'#title' => t('Individual Corner Values'),
'#collapsible' => TRUE,
'#collapsed' => (! $action['independent_corners_set']['independent_corners']),
);
$form['independent_corners_set']['independent_corners'] = array(
'#type' => 'checkbox',
'#title' => t('Set Corners Independently'),
'#default_value' => $action['independent_corners_set']['independent_corners'],
);
$corners = array(
'tl' => t("Top Left Radius"),
'tr' => t("Top Right Radius"),
'bl' => t("Bottom Left Radius"),
'br' => t("Bottom Right Radius"),
);
// Loop over the four corners and create field elements for them.
$form['independent_corners_set']['radii'] = array(
'#type' => 'item',
'#id' => 'independent-corners-set',
);
foreach ($corners as $attribute => $label) {
$form['independent_corners_set']['radii'][$attribute] = array(
'#type' => 'textfield',
'#title' => $label,
'#default_value' => 0 + $action['independent_corners_set']['radii'][$attribute],
'#size' => 2,
);
}
/*
$form['antialias'] = array(
'#type' => 'checkbox',
'#title' => t('antialias'),
'#return_value' => TRUE,
'#default_value' => $action['antialias'],
'#description' => t('Attempt antialias smoothing when drawing the corners'),
);
*/
$form['notes'] = array(
'#type' => 'markup',
'#value' => t('
Note: the rounded corners effect uses true alpha transparency masking.
This means that this effect <b>will fail to be saved</b> on jpegs
<em>unless</em> you either <ul>
<li>convert the image to PNG (using the coloractions filter for that),</li>
<li>define a canvas underneath it (using canvasactions-define-canvas) or</li>
<li>underlay a solid color (using coloractions-alpha-flatten) or</li>
<li>underlay a background image (canvasactions-underlay)</li>
</ul>
as a later part of this imagecache pipeline.
<br/>
'),
);
return $form;
}
function canvasactions_roundedcorners_image($image, $action = array()) {
$independent_corners = !empty($action['independent_corners_set']['independent_corners']);
if (!$independent_corners) {
// set the independant corners to all be the same.
$corners = array('tl', 'tr', 'bl', 'br');
foreach ($corners as $key) {
// Use the all-the-same radius setting.
$action['independent_corners_set']['radii'][$key] = $action['radius'];
}
}
return image_toolkit_invoke('roundedcorners', $image, array($action));
}
/**
* Trim rounded corners off an image, using an anti-aliasing algorithm.
*
* Implementation of hook_image()
*
* Note, this is not image toolkit-agnostic yet! It just assumes GD.
* We can abstract it out once we have something else to abstract to.
* In the meantime just don't.
*
* 'handcoded' rounded corners logic contributed by donquixote 2009-08-31
*
* @param $image
* @param $action
*/
function image_gd_roundedcorners($image, $action = array()) {
// Read settings.
$width = $image->info['width'];
$height = $image->info['height'];
$radius = $action['radius'];
$independent_corners = !empty($action['independent_corners_set']['independent_corners']);
$corners = array('tl', 'tr', 'bl', 'br');
$im = &$image->resource;
// Prepare drawing on the alpha channel.
imagesavealpha($im, TRUE);
imagealphablending($im, FALSE);
foreach ($corners as $key) {
if ($independent_corners && isset($action['independent_corners_set']['radii'][$key])) {
$r = $action['independent_corners_set']['radii'][$key];
}
else {
// Use the all-the-same radius setting.
$r = $radius;
}
// key can be 'tl', 'tr', 'bl', 'br'.
$is_bottom = ($key{0} == 'b');
$is_right = ($key{1} == 'r');
// dx and dy are in "continuous coordinates",
// and mark the distance of the pixel middle to the image border.
for ($dx = .5; $dx < $r; ++$dx) {
for ($dy = .5; $dy < $r; ++$dy) {
// ix and iy are in discrete pixel indices,
// counting from the top left
$ix = floor($is_right ? $width -$dx : $dx);
$iy = floor($is_bottom ? $height -$dy : $dy);
// Color lookup at ($ix, $iy).
$color_ix = imagecolorat($im, $ix, $iy);
$color = imagecolorsforindex($im, $color_ix);
// Do not process opacity if transparency is 100%. Just jump...
// Opacity is always 0 on a transparent source pixel.
if ($color['alpha'] != 127) {
$opacity = _canvasactions_roundedcorners_pixel_opacity($dx, $dy, $r);
if ($opacity >= 1) {
// we can finish this row,
// all following pixels will be fully opaque.
break;
}
if (isset($color['alpha'])) {
$color['alpha'] = 127 - round($opacity * (127 - $color['alpha']));
}
else {
$color['alpha'] = 127 - round($opacity * 127);
}
// Value should not be more than 127, and not less than 0.
$color['alpha'] = ($color['alpha'] > 127) ? 127 : (($color['alpha'] < 0) ? 0 : $color['alpha']);
}
$color_ix = imagecolorallocatealpha($im, $color['red'], $color['green'], $color['blue'], $color['alpha']);
imagesetpixel($im, $ix, $iy, $color_ix);
}
}
}
return TRUE;
}
/**
* Calculate the transparency value for a rounded corner pixel
*
* @param $x
* distance from pixel center to image border (left or right)
* should be an integer + 0.5
*
* @param $y
* distance from pixel center to image border (top or bottom)
* should be an integer + 0.5
*
* @param $r
* radius of the rounded corner
* should be an integer
*
* @return float
* opacity value between 0 (fully transparent) and 1 (fully opaque).
*
* OPTIMIZE HERE! This is a really tight loop, potentially getting called
* thousands of times
*/
function _canvasactions_roundedcorners_pixel_opacity($x, $y, $r) {
if ($x < 0 || $y < 0) {
return 0;
}
else if ($x > $r || $y > $r) {
return 1;
}
$dist_2 = ($r -$x) * ($r -$x) + ($r -$y) * ($r -$y);
$r_2 = $r * $r;
if ($dist_2 > ($r + 0.8) * ($r + 0.8)) {
return 0;
}
else if ($dist_2 < ($r -0.8) * ($r -0.8)) {
return 1;
}
else {
// this pixel needs special analysis.
// thanks to a quite efficient algorithm, we can afford 10x antialiasing :)
$opacity = 0.5;
if ($x > $y) {
// cut the pixel into 10 vertical "stripes"
for ($dx = -0.45; $dx < 0.5; $dx += 0.1) {
// find out where the rounded corner edge intersects with the stripe
// this is plain triangle geometry.
$dy = $r - $y - sqrt($r_2 - ($r -$x -$dx) * ($r -$x -$dx));
$dy = ($dy > 0.5) ? 0.5 : (($dy < -0.5) ? -0.5 : $dy);
// count the opaque part of the stripe.
$opacity -= 0.1 * $dy;
}
}
else {
// cut the pixel into 10 horizontal "stripes"
for ($dy = -0.45; $dy < 0.5; $dy += 0.1) {
// this is the math:
// ($r-$x-$dx)^2 + ($r-$y-$dy)^2 = $r^2
// $dx = $r - $x - sqrt($r^2 - ($r-$y-$dy)^2)
$dx = $r - $x - sqrt($r_2 - ($r -$y -$dy) * ($r -$y -$dy));
$dx = ($dx > 0.5) ? 0.5 : (($dx < -0.5) ? -0.5 : $dx);
$opacity -= 0.1 * $dx;
}
}
return ($opacity < 0) ? 0 : (($opacity > 1) ? 1 : $opacity);
}
}
/**
* imageapi_roundedcorners
*/
function image_imagemagick_roundedcorners($image, $action = array()) {
// Based on the imagemagick documentation.
// http://www.imagemagick.org/Usage/thumbnails/#rounded
// Create arc cut-outs, then mask them.
// Draw black triangles and white circles.
// draw circle is center: x,y, and a point on the perimeter
$corners = array('tl', 'tr', 'bl', 'br');
$radii = $action['independent_corners_set']['radii'];
$width = $image->info['width'];
$height = $image->info['height'];
$tl = $radii['tl'];
$tr = $radii['tr'];
$bl = $radii['bl'];
$br = $radii['br'];
$drawmask = '';
if ($tl) {
$drawmask .= " fill black polygon 0,0 0,{$tl} {$tl},0";
$drawmask .= " fill white circle {$tl},{$tl} {$tl},0";
}
if ($tr) {
$right = $width -$tr;
$drawmask .= " fill black polygon {$right},0 {$width},0 {$width},{$tr}";
$drawmask .= " fill white circle {$right},{$tr} {$right},0";
}
if ($bl) {
$bottom = $height -$bl;
$drawmask .= " fill black polygon 0,{$bottom} 0,{$height} {$bl},{$height}";
$drawmask .= " fill white circle {$bl},{$bottom} 0,{$bottom}";
}
if ($br) {
$bottom = $height -$br;
$right = $width -$br;
$drawmask .= " fill black polygon {$right},{$height} {$width},{$bottom} {$width},{$height}";
$drawmask .= " fill white circle {$right},{$bottom} {$width},{$bottom}";
}
$draw = ' -draw ' . escapeshellarg($drawmask);
$compose = ' ' . escapeshellcmd('(') . " +clone -threshold -1 $draw " . escapeshellcmd(')') . ' +matte -compose CopyOpacity -composite ';
$image->ops[] = $compose;
return TRUE;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_canvasactions_roundedcorners($variables) {
$element = $variables['element'];
$data = $element['#value'];
if (!empty($data['independent_corners_set']['independent_corners'])) {
$dimens = join('px | ', $data['independent_corners_set']['radii']) . 'px';
}
else {
$dimens = "Radius: {$data['radius']}px";
}
return $dimens;
}

View File

@@ -0,0 +1,114 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['cheap_dropshadow'] = array (
'name' => 'cheap_dropshadow',
'#weight' => '3.3',
'effects' =>
array (
-1 => array (
'weight' => '-1',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
0 =>
array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_definecanvas',
'data' =>
array (
'RGB' =>
array (
'HEX' => '999999',
),
'under' => 0,
'exact' =>
array (
'width' => '',
'height' => '',
'xpos' => 'center',
'ypos' => 'center',
),
'relative' =>
array (
'leftdiff' => '0',
'rightdiff' => '0',
'topdiff' => '0',
'bottomdiff' => '0',
),
),
),
1 =>
array (
'weight' => '1',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_definecanvas',
'data' =>
array (
'RGB' =>
array (
'HEX' => '',
),
'under' => 1,
'exact' =>
array (
'width' => '',
'height' => '',
'xpos' => 'center',
'ypos' => 'center',
),
'relative' =>
array (
'leftdiff' => '20',
'rightdiff' => '0',
'topdiff' => '20',
'bottomdiff' => '0',
),
),
),
2 =>
array (
'weight' => '2',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_source2canvas',
'data' =>
array (
'xpos' => 0,
'ypos' => 0,
'alpha' => '100',
),
),
3 =>
array (
'weight' => '3',
'module' => 'image',
'name' => 'image_scale',
'data' =>
array (
'width' => '200',
'height' => '100%',
'upscale' => 0,
),
),
4 => array (
'weight' => '10',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,62 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['keyword_positioning'] = array (
'name' => 'keyword_positioning',
'#weight' => 4.2,
'effects' => array (
array (
'weight' => '-1',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => 'right',
'ypos' => 'top',
'alpha' => '100',
'path' => drupal_get_path('module', 'imagecache_testsuite') . "/grid-240x160.png",
),
),
array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => '25%',
'ypos' => 'bottom-10%',
'path' => 'misc/druplicon.png',
),
),
array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => '0%',
'ypos' => 'top+10%',
'path' => 'misc/druplicon.png',
),
),
array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => 'right+50',
'ypos' => '50%',
'path' => 'misc/druplicon.png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,47 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['positioned_underlay'] = array (
'name' => 'positioned_underlay',
'#weight' => 4.4,
'effects' => array (
0 => array (
'module' => 'image',
'name' => 'image_scale',
'weight' => '0',
'data' => array (
'width' => '200',
'height' => '',
'upscale' => 0,
),
),
1 => array (
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_canvas2file',
'weight' => '1',
'data' => array (
'xpos' => '50',
'ypos' => 'bottom+50',
'alpha' => '100',
'path' => "$filepath/shiny-bg.png",
'dimensions' => 'background',
),
),
4 => array (
'weight' => '10',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,35 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rotate_alpha'] = array (
'name' => 'rotate_alpha',
'#weight' => 1.4,
'effects' => array (
1 => array (
'weight' => '1',
'module' => 'image',
'name' => 'image_rotate',
'data' => array (
'degrees' => '15',
'random' => 0,
'bgcolor' => '',
),
),
3 => array (
'weight' => '3',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -0,0 +1,36 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rotate_alpha_gif'] = array (
'name' => 'rotate_alpha_gif',
'#weight' => 1.5,
'effects' => array (
1 => array (
'weight' => '1',
'module' => 'image',
'name' => 'image_rotate',
'data' => array (
'degrees' => '15',
'random' => 0,
'bgcolor' => '',
),
),
3 => array (
'weight' => '3',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/gif',
),
),
),
);

View File

@@ -0,0 +1,38 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rotate_scale'] = array (
'name' => 'rotate_scale',
'#weight' => 1.2,
'effects' => array (
1 => array (
'weight' => '1',
'module' => 'image',
'name' => 'image_rotate',
'data' => array (
'degrees' => '15',
'random' => 0,
'bgcolor' => '',
),
),
2 => array (
'weight' => '2',
'module' => 'image',
'name' => 'image_scale',
'data' => array (
'width' => '',
'height' => '150',
'upscale' => TRUE,
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,59 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rotate_scale_alpha'] = array (
'name' => 'rotate_scale_alpha',
'#weight' => 1.6,
'effects' => array (
1 => array (
'weight' => '1',
'module' => 'image',
'name' => 'image_rotate',
'data' => array (
'degrees' => '65',
'random' => 0,
'bgcolor' => '',
),
),
/*
* imageapi resize is NOT alpha-safe. This test case proves the bug.
* A work-around is to change format before resizing.
2 => array (
'weight' => '2',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
*/
3 => array (
'weight' => '3',
'module' => 'image',
'name' => 'image_scale',
'data' => array (
'width' => '',
'height' => '150',
'upscale' => TRUE,
),
),
4 => array (
'weight' => '4',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' => array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,34 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rounded'] = array (
'name' => 'rounded',
'#weight' => 3.0,
'effects' => array (
1 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_roundedcorners',
'data' => array (
'radius' => '25',
'antialias' => true,
),
),
2 => array (
'weight' => '3',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' =>array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,43 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rounded_bl'] = array (
'name' => 'rounded_bl',
'#weight' => 3.1,
'effects' => array (
1 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_roundedcorners',
'data' => array (
'radius' => '50',
'independent_corners_set' => array (
'independent_corners' => 1,
'radii' => array (
'tl' => '10',
'tr' => '0',
'bl' => '50',
'br' => '10',
),
),
'antialias' => true,
),
),
2 => array (
'weight' => '3',
'module' => 'imagecache_coloractions',
'name' => 'coloractions_convert',
'data' =>array (
'format' => 'image/png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,59 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['rounded_flattened'] = array (
'name' => 'rounded_flattened',
'#weight' => 3.3,
'effects' => array (
1 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_roundedcorners',
'data' => array (
'radius' => '50',
'independent_corners_set' => array (
'independent_corners' => 1,
'radii' => array (
'tr' => '100',
'br' => '0',
'tl' => '0',
'bl' => '0',
),
),
'antialias' => true,
),
),
2 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_definecanvas',
'data' => array (
'RGB' => array (
'HEX' => '333333',
),
'under' => 1,
'exact' => array (
'width' => '',
'height' => '',
'xpos' => 'center',
'ypos' => 'center',
),
'relative' => array (
'leftdiff' => '2',
'rightdiff' => '2',
'topdiff' => '2',
'bottomdiff' => '2',
),
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,28 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['watermark_100'] = array (
'name' => 'watermark_100',
'#weight' => 4.0,
'effects' => array (
0 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => '10',
'ypos' => '5',
'alpha' => '100',
'path' => 'misc/druplicon.png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,28 @@
<?php
// $ID: $
/**
* @file
* Test imagecache preset.
*
* Created on Dec 29, 2009
*
* @author 'dman' Dan Morrison http://coders.co.nz/
*/
$presets['watermark_50'] = array (
'name' => 'watermark_50',
'#weight' => 4.1,
'effects' => array (
0 => array (
'weight' => '0',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array (
'xpos' => 'right+20',
'ypos' => 'bottom',
'alpha' => '50',
'path' => 'misc/druplicon.png',
),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,19 @@
name = Imagecache Color Actions
description = Additional ImageCache actions, providing color-shifting, brightness and alpha transparency effects.
package = Media
core = 7.x
dependencies[] = imagecache_actions
dependencies[] = image
files[] = imagecache_coloractions.install
files[] = imagecache_coloractions.module
files[] = transparency.inc
files[] = tests/green.imagecache_preset.inc
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,27 @@
<?php
/**
* @file Set up new color actions. Tell imagecache.module about them
*/
/**
* Need to flush the cache when this module is enabled or disabled
*/
function imagecache_coloractions_install() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
drupal_set_message(t('Additional image style actions should now be available in the presets !settings_link', array('!settings_link' => l(t('settings'), 'admin/config/media/image-styles'))));
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function imagecache_coloractions_uninstall() {
if (function_exists('imagecache_action_definitions') ) {
imagecache_action_definitions(TRUE);
}
cache_clear_all('imagecache_actions', 'cache');
}

View File

@@ -0,0 +1,501 @@
<?php
/**
* @file
* Additional actions for imagecache processing.
*
* Exposes some of the simpler PHP 'imagefilter' actions (colorshift,
* brightness, negative)
* - A transparency masker for merging with backgrounds.
* - A pseudo - file conversion feature.
*
* Compatible with the 2008 revision (imagecache 2)
*
* @author dan http://coders.co.nz
* @author sydneyshan http://enigmadigital.net.au
*/
// During devel, caching is pointless. Flush it
//imagecache_action_definitions(TRUE);
if (! function_exists('imagecache_actions_calculate_relative_position') ) {
module_load_include('inc', 'imagecache_actions', 'utility');
}
module_load_include('inc', 'imagecache_actions', 'utility-color');
// @todo There doesn't seem to be a way to specify a file in hook_image_effect_info
// so placing this here for the time being.
module_load_include('inc', 'imagecache_coloractions', 'transparency');
/**
* hook_image_effect_info()
*
* Return the descriptions for the supported actions.
*/
function imagecache_coloractions_image_effect_info() {
$effects = array();
$effects['coloractions_colorshift'] = array(
'label' => t('Color Shift'),
'help' => t('Adjust image colors.'),
'effect callback' => 'coloractions_colorshift_image',
'dimensions passthrough' => TRUE,
'form callback' => 'coloractions_colorshift_form',
'summary theme' => 'coloractions_colorshift_summary',
);
$effects['imagecache_coloroverlay'] = array(
'label' => t('Color Overlay'),
'help' => t('Apply a color tint to an image (retaining blacks and whites).'),
'effect callback' => 'coloractions_coloroverlay_image',
'dimensions passthrough' => TRUE,
'form callback' => 'coloractions_coloroverlay_form',
'summary theme' => 'coloractions_coloroverlay_summary',
);
$effects['coloractions_brightness'] = array(
'label' => t('Brightness'),
'help' => t('Adjust image brightness.'),
'effect callback' => 'coloractions_brightness_image',
'dimensions passthrough' => TRUE,
'form callback' => 'coloractions_brightness_form',
'summary theme' => 'coloractions_brightness_summary',
);
$effects['coloractions_inverse'] = array(
'label' => t('Negative Image'),
'help' => t('Invert colors and brightness.'),
'effect callback' => 'coloractions_inverse_image',
'dimensions passthrough' => TRUE,
);
// @todo Convert may need a little more work.
$effects['coloractions_convert'] = array(
'label' => t('Change file format'),
'help' => t('Choose to save the image as a different filetype.'),
'effect callback' => 'coloractions_convert_image',
'dimensions passthrough' => TRUE,
'form callback' => 'coloractions_convert_form',
'summary theme' => 'coloractions_convert_summary',
);
$effects['imagecache_alpha'] = array(
'label' => t('Alpha Transparency'),
'help' => t('Adjust transparency.'),
'effect callback' => 'imagecache_alpha_image',
'dimensions passthrough' => TRUE,
'form callback' => 'imagecache_alpha_form',
'summary theme' => 'coloractions_alpha_summary',
);
return $effects;
}
/**
* hook_theme()
*/
function imagecache_coloractions_theme() {
return array(
'coloractions_colorshift_summary' => array(
'variables' => array('data' => NULL),
),
'coloractions_coloroverlay_summary' => array(
'variables' => array('data' => NULL),
),
'coloractions_alpha_summary' => array(
'variables' => array('data' => NULL),
),
'coloractions_brightness_summary' => array(
'variables' => array('data' => NULL),
),
'coloractions_convert_summary' => array(
'variables' => array('data' => NULL),
),
);
}
/**
* Implementation of imagecache_hook_form()
*
* Settings for colorshift actions.
*
* @param $action array of settings for this action
* @return a form definition
*/
function coloractions_colorshift_form($action) {
$defaults = array(
'RGB' => array(
'HEX' => '#FF0000',
),
);
$action = array_merge($defaults, (array) $action);
$form = array('#theme' => 'imagecache_rgb_form');
$form['RGB'] = imagecache_rgb_form($action['RGB']);
$form['note'] = array('#value' => t("<p>
Note that colorshift is a mathematical filter that doesn't always
have the expected result.
To shift an image precisely TO a target color,
desaturate (greyscale) it before colorizing.
The hue (color wheel) is the <em>direction</em> the
existing colors are shifted. The tone (inner box) is the amount.
Keep the tone half-way up the left site of the color box
for best results.
</p>"));
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_coloractions_colorshift_summary($variables) {
return theme_imagecacheactions_rgb($variables['data']);
}
/**
* Implementation of hook_image()
*
* Process the imagecache action on the passed image
*
* Just converts and passes the vals to the all-purpose 'filter' action
*/
function coloractions_colorshift_image($image, $data = array()) {
// convert color from hex (as it is stored in the UI)
if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
$data['RGB'] = array_merge($data['RGB'], $deduced);
}
return image_toolkit_invoke('colorshift', $image, array($data));
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_gd_colorshift($image, $data = array()) {
$RGB = $data['RGB'];
if (!function_exists('imagefilter')) {
module_load_include('inc', 'imagecache_actions', 'imagefilter');
}
return imagefilter($image->resource, 4, $RGB['red'], $RGB['green'], $RGB['blue']);
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_imagemagick_colorshift($image, $data = array()) {
$RGB = $data['RGB'];
$image->ops[] = "-fill rgb" . escapeshellcmd('(') . "{$RGB['red']},{$RGB['green']},{$RGB['blue']}" . escapeshellcmd(')') . " -colorize 50" . escapeshellcmd('%');
return TRUE;
}
/**
* Implementation of imagecache_hook_form()
*
* Settings for coloroverlay actions.
*
* @param $action array of settings for this action
* @return a form definition
*/
function coloractions_coloroverlay_form($action) {
$defaults = array(
'RGB' => array(
'HEX' => '#E2DB6A',
),
);
$action = array_merge($defaults, (array) $action);
$form = array('#theme' => 'imagecache_rgb_form');
$form['RGB'] = imagecache_rgb_form($action['RGB']);
$form['note'] = array('#value' => t("<p>
Note that color overlay is a mathematical filter that doesn't always
have the expected result.
To shift an image precisely TO a target color,
desaturate (greyscale) it before colorizing.
The hue (color wheel) is the <em>direction</em> the
existing colors are shifted. The tone (inner box) is the amount.
Keep the tone half-way up the left site of the color box
for best results.
</p>"));
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_coloractions_coloroverlay_summary($variables) {
return theme_imagecacheactions_rgb($variables['data']);
}
/**
* Implementation of hook_image()
*
* Process the imagecache action on the passed image
*
* Just converts and passes the vals to the all-purpose 'filter' action
*/
function coloractions_coloroverlay_image($image, $data = array()) {
// convert color from hex (as it is stored in the UI)
if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
$data['RGB'] = array_merge($data['RGB'], $deduced);
}
return image_toolkit_invoke('coloroverlay', $image, array($data));
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_gd_coloroverlay($image, $data = array()) {
$RGB = $data['RGB'];
$w = $image->info['width'];
$h = $image->info['height'];
for($y=0;$y<$h;$y++) {
for($x=0;$x<$w;$x++) {
$rgb = imagecolorat($image->resource, $x, $y);
$source = imagecolorsforindex($image->resource, $rgb);
if($source['red'] <= 128){
$final_r = (2 * $source['red'] * $RGB['red'])/256;
}else{
$final_r = 255 - (((255 - (2 * ($source['red'] - 128))) * (255 - $RGB['red']))/256);
}
if($source['green'] <= 128){
$final_g = (2 * $source['green'] * $RGB['green'])/256;
}else{
$final_g = 255 - (((255 - (2 * ($source['green'] - 128))) * (255 - $RGB['green']))/256);
}
if($source['blue'] <= 128){
$final_b = (2 * $source['blue'] * $RGB['blue'])/256;
}else{
$final_b = 255 - (((255 - (2 * ($source['blue'] - 128))) * (255 - $RGB['blue']))/256);
}
$final_colour = imagecolorallocatealpha($image->resource, $final_r, $final_g, $final_b, $source['alpha']);
imagesetpixel($image->resource, $x, $y, $final_colour);
}
}
return TRUE;
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_imagemagick_coloroverlay($image, $data = array()) {
$RGB = $data['RGB'];
$image->ops[] = escapeshellcmd('(') . " +clone +matte -fill rgb" . escapeshellcmd('(') . "{$RGB['red']},{$RGB['green']},{$RGB['blue']}" . escapeshellcmd(')') . " -colorize 100" . escapeshellcmd('%') . " +clone +swap -compose overlay -composite " . escapeshellcmd(')') . " -compose SrcIn -composite";
return TRUE;
}
/**
* Implementation of imagecache_hook_form()
*
* Settings for colorshift actions.
*
* @param $action array of settings for this action
* @return a form definition
*/
function coloractions_brightness_form($action) {
$default = array('filter_arg1' => '100');
$action = array_merge($default, (array) $action);
$form = array();
$form['help'] = array('#value' => t("The brightness effect seldom looks good on its own, but can be useful to wash out an image before making it transparent - eg for a watermark."));
$form['filter_arg1'] = array(
'#type' => 'textfield',
'#title' => t('Brightness'),
'#description' => t('-255 - +255'),
'#default_value' => $action['filter_arg1'],
'#size' => 3,
);
return $form;
}
/**
* Implementation of hook_image()
*
* Process the imagecache action on the passed image
*/
function coloractions_brightness_image($image, $data = array()) {
return image_toolkit_invoke('brightness', $image, array($data));
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_gd_brightness($image, $data = array()) {
if (!function_exists('imagefilter')) {
module_load_include('inc', 'imagecache_actions', 'imagefilter'); }
return imagefilter($image->resource, 2, $data['filter_arg1']);
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_imagemagick_brightness($image, $data = array()) {
$image->ops[] = "-modulate " . (int)(100 + ( $data['filter_arg1'] / 128 * 100 ));
return TRUE;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_coloractions_brightness_summary($variables) {
return t("Adjust") . " : " . $variables['data']['filter_arg1'];
}
/**
* Implementation of imagecache_hook_form()
*
* No settings.
*
* @param $action array of settings for this action
* @return a form definition
*/
function coloractions_inverse_form($action) {
$form = array();
return $form;
}
/**
* Implementation of hook_image()
*
* Process the imagecache action on the passed image
*/
function coloractions_inverse_image(&$image, $data = array()) {
return image_toolkit_invoke('inverse', $image, array($data));
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_gd_inverse(&$image, $data = array()) {
if (!function_exists('imagefilter')) {
module_load_include('inc', 'imagecache_actions', 'imagefilter');
}
return imagefilter($image->resource, 0);
}
/**
* Implementation of hook_{toolkit}_{effect}()
*/
function image_imagemagick_inverse(&$image, $data = array()) {
// TODO
return FALSE;
}
/**
* Implementation of imagecache_hook_form()
*
* @param $action array of settings for this action
* @return a form definition
*/
function coloractions_convert_form($action) {
$form = array(
'help' => array(
'#type' => 'markup',
'#value' => t("If you've been using transparencies in the process, the result may get saved as a PNG (as the image was treated as a one in in-between processes). If this is not desired (file sizes may get too big) you should use this process to force a flatten action before saving. "),
),
'help2' => array(
'#type' => 'markup',
'#value' => t("For technical reasons, changing the file format within imagecache does <em>not</em> change the filename suffix. A png may be saved as a *.jpg or vice versa. This may confuse some browsers and image software, but most of them have no trouble. "),
),
'format' => array(
'#title' => t("File format"),
'#type' => 'select',
'#default_value' => isset($action['format']) ? $action['format'] : 'image/png',
'#options' => coloractions_file_formats(),
),
'quality' => array(
'#type' => 'textfield',
'#title' => t('Quality'),
'#description' => t('Override the default image quality. Works for Imagemagick only. Ranges from 0 to 100. For jpg, higher values mean better image quality but bigger files. For png it is a combination of compression and filter'),
'#size' => 10,
'#maxlength' => 3,
'#default_value' => isset($action['quality']) ? $action['quality'] : '75',
'#field_suffix' => '%',
),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_coloractions_convert_summary($variables) {
$data = $variables['data'];
$formats = coloractions_file_formats();
if ($formats[$data['format']] == 'jpg') {
return t('Convert to: @format, quality: @quality%', array(
'@format' => $formats[$data['format']],
'@quality' => $data['quality']
));
}
else {
return t("Convert to") .": ". $formats[$data['format']];
}
}
/**
* Implementation of hook_image()
*
* Process the imagecache action on the passed image
*/
function coloractions_convert_image($image, $data = array()) {
$formats = coloractions_file_formats();
$image->info['mime_type'] = $data['format'];
$image->info['extension'] = $formats[$data['format']];
image_toolkit_invoke('convert_image', $image, array($data));
return TRUE;
}
/**
* Implementation of hook_{toolkit}_{effect}()
*
* image_toolkit_invoke will exit with an error when no implementation is
* provided for the active toolkit so provide an empty operation for the GD
* tookit
*/
function image_gd_convert_image($image, $data) {
return TRUE;
}
/**
* Implements hook_{toolkit}_{effect}().
*
* Converting the image format with imagemagick is done by prepending the output
* format to the target file separated by a colon (:). This is done with
* hook_imagemagick_arguments_alter(), see below.
*/
function image_imagemagick_convert_image($image, $data) {
$image->ops['output_format'] = $image->info['extension'];
$image->ops['custom_quality_value'] = (int) $data['quality'];
return TRUE;
}
/**
* Implements hook_imagemagick_arguments_alter.
*
* This hook moves a change in output format from the args (action list) to the
* destination format setting within the context.
*/
function imagecache_coloractions_imagemagick_arguments_alter(&$args, &$context) {
if (isset($args['output_format'])) {
$context['destination_format'] = $args['output_format'];
unset($args['output_format']);
}
if (isset($args['custom_quality_value'])) {
$args['quality'] = sprintf('-quality %d', $args['custom_quality_value']);
unset($args['custom_quality_value']);
}
}
/**
* Mini mime-type list
*/
function coloractions_file_formats() {
return array('image/jpeg' => 'jpg', 'image/gif' => 'gif', 'image/png' => 'png');
}

View File

@@ -0,0 +1,239 @@
<?php
/**
* @file Helper functions for the alpha action for imagecache
*
* @author dan http://coders.co.nz
*/
/**
* Implementation of imagecache_hook_form()
*
* Settings for alpha actions.
*
* @param $action array of settings for this action
* @return a form definition
*/
function imagecache_alpha_form($action) {
$defaults = array(
'flatten' => FALSE,
'RGB' => array('HEX' => '#000000'),
'opacity' => 0.5,
);
$action = array_merge($defaults, (array) $action);
$form = array();
$form['help'] = array(
'#markup' => t("
<p>You can <em>either</em> set the alpha values of the image to a fixed
amount by defining opacity, <em>or</em> choose a color and let the
darkness of the image pixels define an opacity.
These are different effects. Don't do both
or you will just get a plain block of color of a certain opacity.
")
);
$form['opacity'] = array(
'#type' => 'textfield',
'#title' => t('Opacity'),
'#default_value' => $action['opacity'],
'#size' => 3,
'#description' => t("
A decimal between 0 and 1.
You can define the amount of transparency to apply, eg 0.8 (80%) opacity
will make the image slightly transparent.
Use this with <em>no</em> fill color defined for normal results.
If you follow up by flattening the image onto white or grey,
this will have the effect of partial desaturation.
"),
);
$form['description'] = array('#value' => t(
"<p>Alpha toning is an advanced method of greyscaling or colorizing.
It works using transparency, not colour matching.
The results of this filter are excellent for using as watermarks,
and for 'sepia' type imprints on coloured or textured backgrounds.
It converts dark areas of the image to opaque, light to transparent.</p>
<p>Note that if you are working with JPEGs, this alpha effect will not last into the final image
<em>unless</em> you either <strong>flatten</strong> this image against a background color
or image in a later process or <strong>convert</strong> it to a PNG before saving
using available imagecache actions.</p>"
));
$form['RGB'] = imagecache_rgb_form($action['RGB']);
$form['RGB']['#type'] = 'fieldset';
$form['RGB']['#title'] = t('Fill Color');
$form['RGB']['HEX']['#description'] = t("
Although this image will end up as an alpha transparency mask,
it still has to have some colour to be visible.
Black is safe. Dark Sepia #704214 is good too.
Set it to nothing to not perform any color shift.
");
$form['flatten'] = array(
'#type' => 'checkbox',
'#title' => t('Flatten Transparency'),
'#default_value' => $action['flatten'],
'#return_value' => TRUE,
'#description' => t("The opposite of adding alpha transparency, 'flatten' will place the given colour solidly behind the image. Use this if you can't trust IE, or you really do want the image filled in with a solid colour."),
);
return $form;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_imagecache_alpha($variables) {
$element = $variables['element'];
return ($element['#value']['flatten'] ? t("Flatten") : t("Transparent"))
. ($element['#value']['opacity'] ? " : " . ($element['#value']['opacity'] * 100) . '%' : '')
. " : " . theme_imagecacheactions_rgb($element['#value']['RGB']);
}
/**
* Given an image, manipulate the transparancy behaviour.
*
* implementation of hook_image()
*
* Either convert light parts of an image to see-through, or place a solid
* colour behind areas that would otherwise be see-though
*
* An imagecache_action_hook() . Handle a pipelined image transformation.
*
* To save a partially transparent image, the image resource must be switched to PNG.
* REMEMBER TO SWITCH IT BACK if needed
*
* @param $image handle on the image definition, including a gd image resource
* to act upon
* @param $data settings for this process.
* @return bool success
*/
function imagecache_alpha_image($image, $data = array()) {
if (! $data['flatten']) {
// given an image, convert dark areas to opaque,
// light to transparent,
return png_color2alpha($image, $data['RGB']['HEX'], $data['opacity']);
}
else {
// Do the opposite, flatten the transparency ONTO the given colour
$info = $image->info;
if (!$info) {
watchdog("imagecache", "Problem converting image to fill behind. Source image returned no info");
#dsm($source);
return; // error
}
$base_image = imagecreatetruecolor($info['width'], $info['height']);
imagesavealpha($base_image, TRUE);
imagealphablending($base_image, FALSE);
// Start with a solid colour
$background_rgb = imagecache_actions_hex2rgba($data['RGB']['HEX']);
// Setting the background colour here solid is what flattens the image
$background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0);
// But what I really want to do is set it
// coloured rgb AND 100% transparent, in the hope that
// a failure to render transparency would instead render
// THAT colour.
$background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0);
// But that still doesn't work.
// Yet somehow I've seen transparent images that fallback to white, not silver.
imagefill( $base_image, 0, 0, $background_color );
// And set the overlay behaviour back again
imagealphablending($base_image, TRUE);
// Place the current image over it
$foreground = $image->resource;
$success = imagecopy($base_image, $image->resource, 0, 0, 0, 0, $info['width'], $info['height']);
$image->resource = $base_image;
return TRUE;
}
}
/**
* This achives a tonal effect by converting the images combined tone and
* existing transparency into one shade value. This is then used as the ALPHA
* transparency for that pixel, while the whole thing is coloured the same
* shade. Images 'greytoned' in this manner should sit smoothly on any
* background.
*
* With no color set, use the existing hue.
*
* To save a partially transparent image, the image resource must be switched to PNG.
* ... or maybe not. Just flatten it yourself, or switch the format yourself.
* This hack would produce side effects otherwise.
*
* This algorithm runs maths per-pixel, and therefore is incredibly much more
* inefficient than any native routine. Will kill the server on large images.
*
* @param $opacity between 0 transparent and 1 solid.
*/
function png_color2alpha($image, $color, $opacity = NULL) {
$info = $image->info;
if (!$info) {
return FALSE;
}
$im1 = $image->resource;
imagesavealpha($im1, TRUE);
imagealphablending($im1, FALSE);
if ($color) {
$background = imagecache_actions_hex2rgba($color);
}
$width = imagesx($im1);
$height = imagesy($im1);
if (($width * $height) > (1200 * 1200)) {
watchdog('imagecache_actions', __FUNCTION__ . " on {$image->source}. Image is TOO BIG to run the per-pixel algorithm. Aborting.");
return TRUE;
}
for ($i = 0; $i < $height; $i++) {
//this loop traverses each row in the image
for ($j = 0; $j < $width; $j++) {
//this loop traverses each pixel of each row
// Get the color & alpha info of the current pixel.
$retrieved_color = imagecolorat($im1, $j, $i); // an index
$rgba_array = imagecolorsforindex($im1, $retrieved_color);
$alpha = 127;
// Calculate the total shade value of this pixel.
// If the rule sets a color, then the darkness of the existing
// pixel will define the desired alpha value.
if ($color) {
$lightness = ( $rgba_array['red'] + $rgba_array['green'] + $rgba_array['blue'] ) / 3;
// Need to flip the numbers around before doing maths.
#$opacity = 1-($rgba_array['alpha']/127);
#$darkness = 1-($lightness/256); // 0 is white, 1 is black
#$visibility = $darkness * $opacity;
#$alpha = (1-$visibility) * 127;
$alpha = (1 - ((1 -($lightness / 256)) * (1 -($rgba_array['alpha'] / 127)))) * 127;
}
// If color is NOT set, then the existing color is passed though, only
// made somewhat transparent.
if (!$color) {
$background = $rgba_array;
}
if ($opacity) {
// It's a user-defined alpha value
$alpha = $alpha * $opacity;
}
// Paint the pixel.
$color_to_paint = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $alpha);
imagesetpixel($image->resource, $j, $i, $color_to_paint);
}
}
return TRUE;
}

View File

@@ -0,0 +1,144 @@
README
------
README for the custom actions effect module.
Dependencies
------------
Hard dependencies:
- Imagecache actions.
- Image (Drupal core).
Soft dependencies/recommended modules:
- Imagemagick (preferred toolkit).
- PHP filter (Drupal core).
Toolkit
-------
Personally, I prefer the imagemagick toolkit:
- It is better in anti-aliasing, try to rotate an image using both toolkits and
you will see what I mean.
- It does not execute in the PHP memory space, so is not restricted by the
memory_limit PHP setting.
- The GD toolkit will, at least on my Windows configuration, keep the font file
open after a text operation, so you cannot delete, move or rename it anymore.
- This module does a better job with Imagemagick (see below).
Installing
----------
As usual. After enabling the module you can add custom actions to images.
Custom action PHP snippets
--------------------------
Given the correct permission, the custom action effect allows you to write your
own PHP snippet that does the requested processing on the image. How it can do
so, depends on the toolkit.
For all toolkits, the snippet should return true to indicate success and false
to indicate failure.
GD
--
The GD image resource is available in $image->resource. You can call the GD
functions on this resource. This effect will query the width and height after
your processing, so you don't have to change that yourself.
Imagemagick
-----------
All real image processing is done at the end, if all effects have added their
command line arguments to the $image->ops array. So your custom action should
add the imagemagick commands and its parameters by adding new string entries to
the end of that array.
If your commands change the width or height of the resulting image, you should
record so by changing $image->info['width'] and/or $image->info['height'].
General
-------
To ease your task, this effect makes some information regarding the image being
processed available in 2 variables: $image and $image_context. These variables
are readily available in your snippet.
$image is an associative array containing:
- source: string, the source of the image, e.g. public://photo.jpg
- info: array, example data:
- width (int) 180
- height (int) 180
- extension (string) png
- mime_type (string) image/png
- file_size (int) 4417
- toolkit: string, imagemagick or GD
$image_context is an associative array containing:
- effect_data: array, the data of this image effect, example data for the custom
action effect:
- php (string)
- managed_file: object|null. A managed file object containing these properties:
- fid (string) 2
- uid (string) 1
- filename (string) photo.jpg
- uri (string) public://photo.jpg
- filemime (string) image/jpeg
- filesize (string) 445751
- status (string) 1
- timestamp (string) 1327525851
- metatags Array [0]
- rdf_mapping Array [0]
- referring_entities: array|null. A nested array with (fully loaded) entities
referring to the current image. The 1st level of entries is keyed by the field
name, the 2nd by entity type, and the 3rd by entity id. Example data:
- field_photo Array [1]
- node Array [1]
- 12 Object of: stdClass
- nid (string) 12
- vid (string) 12
- type (string) page
- author ...
- timestamp ...
- ...
- entity: object|null, the 1st entity in referring_entities. This is for easy
access to the referring entity if it may be assumed that only 1 entity is
referring to the current image.
- image_field: array|null, the 1st image field in entity that is referring to
the current image. This is for easy access to the image field data if it may
be assumed that only 1 image field is referring to the current image. Example
data:
- fid (int) 2
- alt (string) ...
- title (string) ...
- ...
Of course there are many other possible useful globals. Think of:
- base_url
- base_path
- base_root
- is_https
- user
- language
and of course $_SERVER and $_GET.
Using these information you can access entity data as follows:
Specific case (1 entity, of known entity_type, referring to the image):
<?php
$entity_type = 'node';
$field_name = 'my_field';
$entity = $image_context['entity'];
$field = field_get_items($entity_type, $entity, $field_name);
?>
Or the more general case (not knowing the referring type, or multiple entities
that may be referring to the image):
<?php
$referring_entities = $image_context['referring_entities'];
foreach ($referring_entities as $field_name => $field_referring_entities) {
foreach ($field_referring_entities as $entity_type => $entities) {
foreach ($entities as $entity_id => $entity) {
$field = field_get_items($entity_type, $entity, $field_name);
}
}
}
?>

View File

@@ -0,0 +1,17 @@
name = Imagecache Custom Actions
description = Allow direct PHP code manipulation of imagecache images.
package = Media
core = 7.x
dependencies[] = imagecache_actions
dependencies[] = image
files[] = imagecache_customactions.install
files[] = imagecache_customactions.module
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,23 @@
<?php
/**
* Rename 'text' to 'php' in custom action effect data
*/
function imagecache_customactions_update_7100(&$sandbox) {
$effects = db_select('image_effects')
->fields('image_effects')
->condition('name', 'imagecache_customactions', '=')
->execute()
->fetchAll();
foreach ($effects as $effect) {
$data = unserialize($effect->data);
if (array_key_exists('text', $data)) {
$data['php'] = $data['text'];
unset($data['text']);
$data = serialize($data);
db_update('image_effects')
->condition('ieid', $effect->ieid)
->fields(array('data' => $data))
->execute();
}
}
}

View File

@@ -0,0 +1,251 @@
<?php
/**
* @file Allow advanced users to code their own PHP image manipulation routines
* as part of imagecache processing.
*
* @author Originally contributed by crea http://drupal.org/node/325103#comment-
* 1076011
*
* @author merged into imagecache_actions by dman http://coders.co.nz
*
* Needs review - currently a security risk etc
*/
/**
*
* Notes about imagecache action extensions. For each action:
*
* 1: Implement imagecache_HOOK_form($formdata) to define the config form.
*
* 1a: Implement theme_imagecache_HOOK_form if needed - optional
*
* 2: Implement imagecache_HOOK_image($image, $data) to DO the process
*
* 3: Impliment theme_imagecache_HOOK($element) to return a text description of
* the setting
*
* 4: Declare the action in HOOK_imagecache_actions()
*
*
* API ref for hook_image()
*
* @param $image array defining an image file, including :
*
* $image- >source as the filename,
*
* $image->info array
*
* $image->resource handle on the image object
*
* @param $action array of settings as defined in your form.
*/
function imagecache_customactions_image_effect_info() {
$effects = array();
$effects['imagecache_customactions'] = array(
'label' => t('Custom action'),
'help' => t('Runs custom PHP code.'),
'effect callback' => 'imagecache_customactions_image',
'dimensions callback' => 'imagecache_customactions_dimensions',
'form callback' => 'imagecache_customactions_form',
);
$effects['imagecache_subroutine'] = array(
'label' => t('Subroutine'),
'help' => t('Runs another defined preset on the image.'),
'effect callback' => 'imagecache_subroutine_image',
'dimensions callback' => 'imagecache_subroutine_dimensions',
'form callback' => 'imagecache_subroutine_form',
);
return $effects;
}
/**
* Implementation of theme_hook() for imagecache_customactions.module
*/
function imagecache_customactions_theme() {
return array(
'imagecache_subroutine' => array(
'render element' => 'element',
),
);
}
/**
* Implements hook_form().
*
* @param $action array of settings for this action
* @return a form definition
*/
function imagecache_customactions_form($data) {
// Add defaults.
$data += array('php' => 'return TRUE;');
// Note: we also need to check for the existence of the module: admin has
// all rights, so user_acccess(...) returns TRUE even if the module is not
// enabled and the permission does not exist.
$allow_dynamic = user_access('use PHP for settings') && module_exists('php');
// @todo: for imagemagick, the PHP code should add a set of commands to the
// ops aray of $image. Document this in description.
$form = array(
'php' => array(
'#type' => 'textarea',
'#rows' => 10,
'#title' => t('PHP code'),
'#default_value' => $data['php'],
'#disabled' => !$allow_dynamic,
'#description' => t("<p>A piece of PHP code that modifies the image.
It should return a boolean indicating success or failure.
You will need the '%use_php' permission, defined by the 'PHP filter' module.
See the help for an extensive explanation of the possibilities.</p>",
array('%use_php' => t('Use PHP for settings'))),
'#wysiwyg' => FALSE,
),
);
return $form;
}
/**
* Implements hook_image().
*
* @param $image
* @param $data
*/
function imagecache_customactions_image($image, $data) {
// Check that the PHP filter module is enabled.
$result = module_exists('php');
if ($result) {
// Get context about the image.
$GLOBALS['image_context'] = imagecache_actions_get_image_context($image, $data);
$GLOBALS['image'] = $image;
$result = php_eval('<'.'?php global $image, $image_context; ' . $data['php'] . ' ?'.'>');
// php_eval returns '1' if the snippet returns true.
$result = $result === '1';
unset($GLOBALS['image']);
unset($GLOBALS['image_context']);
}
if ($result && $image->toolkit == 'GD') {
$image->info['width'] = imagesx($image->resource);
$image->info['height'] = imagesy($image->resource);
}
return $result;
}
/**
* Image dimensions callback; Custom action.
*
* @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
* @param array $data
* An array with the effect options.
*/
function imagecache_customactions_dimensions(array &$dimensions, array $data) {
// @todo: add form field asking if dimensions stay the same (or if they know
// the new dimesions).
$dimensions['width'] = NULL;
$dimensions['height'] = NULL;
}
/**
* Implementation of theme_hook() for imagecache_ui.module
*/
function theme_imagecache_customactions($element) {
// TODO: Should this theme imagecache_customactions be declared in hook_theme()?
$data = $element['#value'];
return "<em><strong>" . $data['text'] . "</strong></em>";
}
/**
* Subroutine - an imagecache action that just calls another one.
*
* Contributed by Alan D
* http://drupal.org/node/618784
*
* Reworked into customactions by dman 2010-07
*/
/**
* Config form for this preset.
*
* Implementation of imagecache_hook_form()
*
* @param $action array of settings for this action
* @return a form definition
*/
function imagecache_subroutine_form($action) {
$action = (array) $action;
$form = array();
// List available presets
$presets = array();
foreach (image_styles(TRUE) as $preset) {
$presets[$preset['name']] = $preset['name'];
}
$form['subroutine_presetname'] = array(
'#type' => 'select',
'#title' => t('Preset to call'),
'#default_value' => $action['subroutine_presetname'],
'#options' => $presets,
);
return $form;
}
/**
* Actually invoke the action - which means just handing off to the named real
* preset to do the job.
*
* Implementation of hook_image()
*
* @param $image
* @param $action
*/
function imagecache_subroutine_image($image, $data) {
if ($preset = image_style_load($data['subroutine_presetname'])) {
foreach ($preset['effects'] as $effect) {
image_effect_apply($image, $effect);
}
}
return TRUE;
}
/**
* Image dimensions callback; Subroutine.
*
* @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
* @param array $data
* An array with the effect options.
*/
function imagecache_subroutine_dimensions(array &$dimensions, array $data) {
// @todo: dimensions
// @todo: call dimensions callback on subroutine style.
$dimensions['width'] = NULL;
$dimensions['height'] = NULL;
}
/**
* This lets the user see what parameters were selected for the action
*/
function theme_imagecache_subroutine($variables) {
$element = $variables['element'];
$data = $element['#value'];
if ($preset = imagecache_preset_by_name($data['subroutine_presetname'])) {
return t('%name (pid: !presetid)', array('%name' => $preset['presetname'], '!presetid' => $preset['presetid']));
}
return t('<span class="error">Invalid reference. The referenced preset may have been deleted!</span>');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -0,0 +1,146 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Aspect Switcher
</title>
</head>
<body>
<h3>
Using aspect switcher to create a 'square' setting.
</h3>
<p>
This is done through "chaining" two switches - "is it quite
wide or not?", then "is it quite tall or not?" which leaves
us with "must be square then."
</p>
<p>
First we create the three sizes we will be using,
small-landscape, small-square, small-portrait. I'll just set
those up with scale_and_crop.
</p>
<p>
We want wide images up to a ratio of 1:0.75 to be rendered
wide. We want squarish images, with an aspect between 1:0.75
and 1:1.25 to be rendered square, and anything taller to be
rendered tall.
</p>
<p>
To do this, we chain 2 rules. We need to build them
backwards, the smaller sub-rule first, but to understand, I'l
list them top down.
</p>
<p>
Rule 1. is the master rule, <strong>3-aspects</strong>
</p>
<p>
if ratio is less than 1:.75, use small-landscape. If greater,
<strong>proceed to rule 2</strong>.
</p>
<p>
</p>
<p>
Rule 2. <strong>square-or-portrait</strong>
</p>
<p>
if ratio is less than 1:1.25, use small-square. If greater,
use small-portrait.
</p>
<p>
To do this, we use the aspect switcher to link to the two
sizes, and the <em>ratio adjustment</em> to move
the switching point a little. Set the ratio adjustment to
1.25
</p>
<p>
With these (5!) rules in place, you can get the desired
effect. This is a little trickier than just making a 'square'
setting, but it allows for the required fudge factor to
handle almost-square images.
</p>
<p>
You can nudge the adjustment factor to be looser or tighter.
You can create even more chained rules, and define a
'super-wide' size.
</p>
<table border="1" cellpadding="1" cellspacing="1">
<tbody>
<tr>
<td rowspan="1" colspan="1">
small-landscape
</td>
<td rowspan="1" colspan="1">
[Scale And Crop]&nbsp;width: 200, height: 100
</td>
</tr>
<tr>
<td rowspan="1" colspan="1">
small-portrait
</td>
<td rowspan="1" colspan="1">
[Scale And Crop]&nbsp;width: 100, height: 200
</td>
</tr>
<tr>
<td rowspan="1" colspan="1">
small-square
</td>
<td rowspan="1" colspan="1">
[Scale And Crop]&nbsp;width: 140, height: 140
</td>
</tr>
<tr>
<td rowspan="1" colspan="1">
small-square-or-portrait
</td>
<td rowspan="1" colspan="1">
[Aspect Switcher] Portrait
size:&nbsp;<strong>small-portrait</strong>. Landscape
size:&nbsp;<strong>small-square</strong>&nbsp;(switch
at 1:1.25)
</td>
</tr>
<tr>
<td rowspan="1" colspan="1">
small-3-aspects
</td>
<td rowspan="1" colspan="1">
[Aspect Switcher]&nbsp;Portrait
size:&nbsp;<strong>small-square-or-portrait</strong>.
Landscape
size:&nbsp;<strong>small-landscape</strong>&nbsp;(switch
at 1:.75)
</td>
</tr>
</tbody>
</table>
<p>
&nbsp;The illustration shows the result of this set-up on a
collection of images. The listed dimensions are those of the
source images. You'll see that the mostly-square ones are
rendered square.
</p>
<img src="../docs/aspect-chaining.png" alt="Illustration of several different sized images passing through the above ruleset."/>
<p>
The rule being applied is: 1 Is it wide?
</p>
<p>
For image 250x300, the aspect is ( 250/300 = 0.83 ) Normally
that number (less than 1) would be classified as 'portrait',
and with the adjustment (*0.75) that is still true, so the
processing passes through to the portrait preset.
</p>
<p>
rule #2 it it tall?
</p>
<p>
This preset however does a different set of maths, and
multiplies the aspect by 1.25, producing a result that causes
it to trigger to 'landscape' choice. 'landscape' at this
point is set to be the 'square' preset. And we get what we
wanted.
</p>
</body>
</html>

View File

@@ -0,0 +1,238 @@
README
------
README for the Image effect text module.
Author Erwin Derksen (fietserwin: http://drupal.org/user/750928)
Dependencies
------------
Hard dependencies:
- Imagecache actions.
- Image (Drupal core).
Soft dependencies/recommended modules:
- Imagemagick (preferred toolkit).
- PHP filter (Drupal core).
Toolkit
-------
Personally, I prefer the imagemagick toolkit:
- It is better in anti-aliasing, try to rotate an image using both toolkits and
you will see what I mean.
- It does not execute in the PHP memory space, so is not restricted by the
memory_limit PHP setting.
- The GD toolkit will, at least on my Windows configuration, keep the font file
open after a text operation, so you cannot delete, move or rename it anymore.
- This module does a better job with Imagemagick (see below).
Installing
----------
As usual. After enabling the module you can add texts to images. This image
effect works with both the GD and imagemagick toolkit, though results differ
depending on the tookit you use.
More information about the effect data options
----------------------------------------------
Font
----
You have to supply the font file to use. The font type supported depend on the
toolkit in use, but at least ttf files will always work. This option accepts
either:
1 of the (enabled) scheme's:
- public://
- private:// Preferred for site specific masks, overlay's, etc, that do not need
to be shared publicly.
- module://{module_name}/{resource-name} Introduced by the imagecache_actions
module and preferred for module provided resources, like the button overlay
of the Video Embed Field Overlay module
(http://drupal.org/project/video_embed_field_overlay).
- temporary:// Unlikely to be useful, but supported anyway as all schemes are
supported.
or a relative (to the current directory, probably Drupal root) or absolute path.
Text position
-------------
The text position defines the point in the image where you want to place (align)
your text. It starts at the top left corner of the image with postion 0,0 and
the positive directions are to the right and down.
The definition of the vertical position differs per toolkit. For GD it is the
position of the font baseline, while for Imagemagick it is the bottom of the
bounding box, i.e the descender or beard line in typography terminology.
Text alignment
--------------
You can align your text with regard to the text position. Possible horizontal
alignments are left (default), center and right. Vertical alignments are top,
center and bottom (default).
Note: Given
- the way that GD uses the vertical text position (as baseline),
- and the way this module implements (vertical) alignment (translating the
(vertical) position using the calculated bounding box),
vertical alignment with the GD toolkit is a bit off. You will have to compensate
for this yourself.
Rotation
--------
The text can be rotated before being overlaid on the image. The vlaue is in
degrees, so 90 degrees is straight down. Positive values are rotated clockwise,
negative values counter clockwise.
In Imagemagick the text is rotated around the text position. Thus centered text
is rotated around its own center. GD, on the other hand, always rotates around
the left bottom (baseline) position, regardless the text alignment. Using
rotation with a non default alignment (left bottom) will give surprising
results.
Text source
-----------
Note: this module is not build to handle multi line texts. Not when the text
contains new lines and/or carriage returns, and not to split a given text over
multiple lines given an available width. Using "\n" in GD seems to work though.
The text to place on the image may come from different sources:
- Static: the text to place on the image is static and is defined in the image
effect data. Use this e.g. for a fixed copyright notice.
- PHP: the text to place on the iamge comes from a piece of PHP code that should
return the text to place on the image. Only users with the 'use PHP for
settings' permission are allowed to use this source. This permission and the
evaluation of the PHP code come from the PHP filter module which is part of
Drupal core and thus needs to be enabled, also during image generation.
- To alleviate the need to enable the PHP filter module, 2 commonly used sources
for dynamic texts are directly available without any coding, namely the alt
and title properties of the image field linked to the image at hand. Note that
multiple image fields, possibly in different languages, may be referring to
the image that is being processed. This module will take the first image field
it finds to extract the alt and title. If the field in itself is multi
lingual, thus not a synced field, the current language will be taken, which is
the language of the user that happens to request this styled image first.
PHP snippets to determine the text
----------------------------------
Given the correct permission, you can write your own PHP snippet to compute the
text to display. To ease this task, this module makes some information regarding
the image being processed available in 2 variables: $image and $image_context.
These variables are readily available in your snippet.
$image is an associative array containing:
- source: string, the source of the image, e.g. public://photo.jpg
- info: array, example data:
- width (int) 180
- height (int) 180
- extension (string) png
- mime_type (string) image/png
- file_size (int) 4417
- toolkit: string, imagemagick or GD
$image_context is an associative array containing:
- effect_data: array, the data of this image effect, example data for the text
effect:
- size (string) 12
- xpos (string) center
- ypos (string) center
- halign (string) left
- valign (string) bottom
- RGB Array [1]
- HEX (string) 000000
- alpha (string) 100
- angle (string) 0
- fontfile (string:10) lhandw.ttf
- text_source (string) text
- text (string) Hello World!
- php (string) return 'Hello World!'
- managed_file: object|null. A managed file object containing these properties:
- fid (string) 2
- uid (string) 1
- filename (string) photo.jpg
- uri (string) public://photo.jpg
- filemime (string) image/jpeg
- filesize (string) 445751
- status (string) 1
- timestamp (string) 1327525851
- metatags Array [0]
- rdf_mapping Array [0]
- referring_entities: array|null. A nested array with (fully loaded) entities
referring to the current image. The 1st level of entries is keyed by the field
name, the 2nd by entity type, and the 3rd by entity id. Example data:
- field_photo Array [1]
- node Array [1]
- 12 Object of: stdClass
- nid (string) 12
- vid (string) 12
- type (string) page
- author ...
- timestamp ...
- ...
- entity: object|null, the 1st entity in referring_entities. This is for easy
access to the referring entity if it may be assumed that only 1 entity is
referring to the current image.
- image_field: array|null, the 1st image field in entity that is referring to
the current image. This is for easy access to the image field data if it may
be assumed that only 1 image field is referring to the current image. Example
data:
- fid (int) 2
- alt (string) ...
- title (string) ...
- ...
Of course there are many other possible useful globals. Think of:
- base_url
- base_path
- base_root
- is_https
- user
- language
and of course $_SERVER and $_GET.
Using these information you can access entity data as follows:
Specific case (1 entity, of known entity_type, referring to the image):
<?php
$entity_type = 'node';
$field_name = 'my_field';
$entity = $image_context['entity'];
$field = field_get_items($entity_type, $entity, $field_name);
?>
Or the more general case (not knowing the referring type, or multiple entities
that may be referring to the image):
<?php
$referring_entities = $image_context['referring_entities'];
foreach ($referring_entities as $field_name => $field_referring_entities) {
foreach ($field_referring_entities as $entity_type => $entities) {
foreach ($entities as $entity_id => $entity) {
$field = field_get_items($entity_type, $entity, $field_name);
}
}
}
?>
TODO
----
- Vertical alignment: add baseline as vertical alignment and make both toolkits
behave the same for any given vertical alignment.
- Rotation and alignment. Imagemagick seems to be more correct. Can GD made to
do the same?
- Language and alt/title: what if the first user to pass by and that generates
the image is in a language that has no alt/title?
- Newlines: seem to work in GD, not in Imagemagick.
To quote http://www.imagemagick.org/Usage/text/#draw:
As of IM version 6.2.4, the "-draw text" operation no longer understands the use
of '\n' as meaning newline, or the use of percent '%' image information escapes.
(See Drawing a Percent Bug). These abilities, and problems, however remain
available in the new IM v6 operator "-annotate". See the Annotate Text Drawing
Operator below.

View File

@@ -0,0 +1,602 @@
<?php
/**
* Implementation of image_effects_text.
*/
/**
* Real implementation of hook_help() called by image_effects_text_help().
*
* Experimental diagnostic page to assist locating valid fonts on the system.
* Only tuned for Ubuntu so far. I've been unable do find ubiquitous tools that
* provide useful font listings.'
*/
function image_effects_text_help_inc($path, $arg) {
$output = "<p>
For text rendering to work on a server, we <em>must</em>
know the system path to the font <em>file</em>, not just the name.
Font library handling differs too much on different systems and
the available PHP toolkits are unable to return good diagnostics.
</p><p>
On Debian/Ubuntu, you may find your fonts in and under
<code>/usr/share/fonts/truetype/</code>
eg <code>'/usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf'</code>
</p><p>
On OSX, they are probably in <code>/Library/Fonts/</code>
eg <code>'/Library/Fonts/Times New Roman Bold Italic.ttf'</code>
</p><p>
On Windows, they are probably in <code>C:\\WINDOWS\Fonts\</code>
eg <code>'C:\\WINDOWS\\Fonts\\comic.ttf'</code>
</p><p>
Of course, this will change if you deploy to a different server!
so the best approach is to place your own TTF font file inside your private
or public files directory and use that. Just give the filename with the
'private://' or 'public://' scheme prefix and it should be found.
</p>
";
$output .= t("<p>Files directory is !files</p>", array('!files' => variable_get('file_public_path', conf_path() . '/files')));
return $output;
}
/**
* Builds the form structure for the overlay text image effect.
*
* Note that this is not a complete form, it only contains the portion of the
* form for configuring the effect options. Therefore it does not not need to
* include metadata about the effect, nor a submit button.
*
* @param array $data
* The current configuration for this image effect.
*
* @return array
* The form definition for this effect.
*/
function image_effects_text_form_inc($data) {
// Use of functions imagecache_file_...() creates a dependency on file utility.inc.
module_load_include('inc', 'imagecache_actions', 'utility');
// Use of function imagecache_rgb_form() creates a dependency on file utility-color.inc.
module_load_include('inc', 'imagecache_actions', 'utility-color');
// Note: we also need to check for the existence of the module: admin has
// all rights, so user_acccess(...) returns TRUE even if the module is not
// enabled and the permission does not exist.
// A user without the 'use PHP for settings' permission (defined by the core
// PHP filter module) may not:
// - Select the 'PHP code' text source option if it is currently not selected.
// - Change the 'PHP code' textarea.
$allow_dynamic = user_access('use PHP for settings') && module_exists('php');
$defaults = array(
'size' => 12,
'angle' => 0,
'xpos' => '0',
'ypos' => '0',
'halign' => 'left',
'valign' => 'bottom',
'RGB' => array('HEX' => '#000000'),
'alpha' => 100,
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => 'Hello World!',
'php' => 'return \'Hello World!\'',
);
$data += $defaults;
$form = array(
'size' => array(
'#type' => 'textfield',
'#title' => t('Font size'),
'#default_value' => $data['size'],
'#description' => t('The font size in points. Only in GD1 this is in pixels.'),
'#size' => 3,
),
'xpos' => array(
'#type' => 'textfield',
'#title' => t('X offset'),
'#default_value' => $data['xpos'],
'#description' => t('Enter an offset in pixels or use a keyword: <em>left</em>, <em>center</em>, or <em>right</em>. Syntax like <em>right-20</em> is also valid.'),
'#size' => 10,
),
'ypos' => array(
'#type' => 'textfield',
'#title' => t('Y offset'),
'#default_value' => $data['ypos'],
'#description' => t('Enter an offset in pixels or use a keyword: <em>top</em>, <em>center</em>, or <em>bottom</em>. Syntax like <em>bottom-20</em> is also valid.'),
'#size' => 10,
),
'halign' => array(
'#type' => 'select',
'#title' => t('Horizontal alignment'),
'#default_value' => $data['halign'],
'#description' => t('The horizontal alignment of the text around the given %xpos.', array('%xpos' => t('X offset'))),
'#options' => array('left' => t('Left'), 'center' => t('Center'), 'right' => t('Right')),
),
'valign' => array(
'#type' => 'select',
'#title' => t('Vertical alignment'),
'#default_value' => $data['valign'],
'#description' => t('The vertical alignment of the text around the given %ypos.', array('%ypos' => t('Y offset'))),
'#options' => array('top' => t('Top'), 'center' => t('Center'), 'bottom' => t('Bottom')),
),
'RGB' => imagecache_rgb_form($data['RGB']),
'alpha' => array(
'#type' => 'textfield',
'#title' => t('Opacity'),
'#default_value' => $data['alpha'] ? $data['alpha'] : 100,
'#size' => 3,
'#description' => t('Opacity: 1-100.'),
),
'angle' => array(
'#type' => 'textfield',
'#title' => t('Angle'),
'#default_value' => $data['angle'],
'#description' => t('Angle: The angle in degrees, with 0 degrees being left-to-right reading text. Higher values represent a counter-clockwise rotation. For example, a value of 90 would result in bottom-to-top reading text.'),
'#size' => 3,
),
'fontfile' => array(
'#type' => 'textfield',
'#title' => t('Font file name'),
'#default_value' => $data['fontfile'],
'#description' => imagecache_actions_file_field_description(),
'#element_validate' => array('imagecache_actions_validate_file'),
),
'text_help' => array(
'#type' => 'item',
'#title' => t('Text'),
'#description' => t('<p>Select the source of the text:</p>
<ul>
<li><strong>Image alt</strong>: the alt text of an image field referring to this image is taken.</li>
<li><strong>Image title</strong>: the title text of an image field referring to this image is taken.</li>
<li><strong>Static text</strong>: a text that will be the same for each image, e.g. a copyright message. You can define the text in the text field below the drop down.</li>
<li><strong>PHP code</strong>: a piece of PHP code that returns the text to display. You can define the PHP code in the text area below the drop down. You will need the \'%use_php\' permission, defined by the \'PHP filter\' module.</li>
</ul>
<p>See the help for an extensive explanation of the possibilities.</p>',
array('%use_php' => t('Use PHP for settings'))),
),
'text_source' => array(
'#type' => 'select',
'#title' => t('Text source'),
'#default_value' => $data['text_source'],
'#options' => array('alt' => t('Image alt'), 'title' => t('Image title'), 'text' => t('Static text'), 'php' => t('PHP code')),
),
'text' => array(
'#type' => 'textfield',
'#title' => t('Text'),
'#default_value' => $data['text'],
'#states' => array(
'visible' => array(':input[name="data[text_source]"]' => array('value' => 'text')),
),
),
'php' => array(
'#type' => 'textarea',
'#rows' => 5,
'#title' => t('PHP code'),
'#default_value' => $data['php'],
'#disabled' => !$allow_dynamic,
'#states' => array(
'visible' => array(':input[name="data[text_source]"]' => array('value' => 'php')),
),
),
);
if (!$allow_dynamic && $data['text_source'] !== 'php') {
unset($form['text_fieldset']['text_source']['#options']['php']);
}
$form['#element_validate'][] = 'image_effects_text_form_validate';
return $form;
}
/**
* Element validation callback for the effect form.
* @see http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#element_validate
*/
function image_effects_text_form_validate($element, &$form_state, $form) {
if (!is_numeric($element['size']['#value']) || $element['size']['#value'] <= 0) {
form_error($element['size'], t('%field must be a positive number.', array('%field' => t('Font size'))));
}
if (!is_numeric($element['alpha']['#value']) || $element['alpha']['#value'] < 0 || $element['alpha']['#value'] > 100) {
form_error($element['alpha'], t('%field must be a number between 1 and 100.', array('%field' => t('Opacity'))));
}
if (!is_numeric($element['angle']['#value'])) {
form_error($element['angle'], t('%field must be a number.', array('%field' => t('Angle'))));
}
}
/**
* Implementation of theme_hook() for text image effect.
*
* @param array $variables
* An associative array containing:
* - data: The current configuration for this resize effect.
*
* @return string
* The HTML for the summary of a text image effect.
* @ingroup themeable
*/
function theme_image_effects_text_summary($variables) {
$data = $variables['data'];
switch ($data['text_source']) {
case 'alt':
$text = 'image alt';
break;
case 'title':
$text = 'image title';
break;
case 'text':
$text = $data['text'];
break;
case 'php':
$text = 'PHP code';
break;
}
return 'Text: ' . $text . '; Position: ' . $data['xpos'] . ',' . $data['ypos'] . '; Alignment: ' . $data['halign'] . ',' . $data['valign'];
}
/**
* (Real implementation of) Image effect callback; Overlay text on an image
* resource.
*
* @param object $image
* An image object returned by image_load().
*
* @param array $data
* An array of attributes to use when performing the resize effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
* - "height": An integer representing the desired height in pixels.
*
* @return boolean
* true on success, false on failure to apply the effect.
*/
function image_effects_text_effect_inc($image, $data) {
// Use of imagecache_actions_hex2rgba() ,the imagecache_file_...() functions,
// and imagecache_actions_get_image_context() create a dependency on
// file utility.inc.
module_load_include('inc', 'imagecache_actions', 'utility');
// Massage the data and pass it on to the toolkit dependent part.
// Start with a straight copy.
$params = $data;
// Find out where the font file is located and if it is readable.
$params['fontpath'] = imagecache_actions_find_file($data['fontfile']);
if ($params['fontpath'] === FALSE) {
drupal_set_message(t("Failed to locate the requested font %fontfile. Cannot overlay text onto image.", array('%fontfile' => $data['fontfile'])), 'error');
return FALSE;
}
// Get the text to overlay.
$params['text'] = image_effects_text_get_text($image, $params);
if ($params['text'] === FALSE) {
drupal_set_message(t("Failed to evaluate text (is the 'PHP Filter' module enabled?). Not overlaying text."), 'error');
return FALSE;
}
// Parse offsets
$params['xpos'] = image_effects_text_get_offset($data['xpos'], $image->info['width'], $image->info['height'], $image->info['width']);
$params['ypos'] = image_effects_text_get_offset($data['ypos'], $image->info['width'], $image->info['height'], $image->info['height']);
// Convert color from hex (as it is stored in the UI).
$params['RGB'] = $data['RGB'];
if($params['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($params['RGB']['HEX'])) {
$params['RGB'] += $deduced;
}
// Make int's of various parameters
$params['size'] = (int) $params['size'];
$params['xpos'] = (int) $params['xpos'];
$params['ypos'] = (int) $params['ypos'];
// Hand over to toolkit
return image_toolkit_invoke('image_effects_text', $image, array($params));
}
/**
* GD toolkit specific implementation of this image effect.
*
* @param object $image
* @param array $params
* An array containing the parameters for this effect.
*
* @return bool
* true on success, false otherwise.
*/
function image_gd_image_effects_text($image, $params) {
// Convert color and alpha to GD alpha and color value.
// GD alpha value: 0 = opaque, 127 = transparent.
$params['alpha'] = (int) ((1 - ($params['alpha'] / 100)) * 127);
$color = imagecolorallocatealpha($image->resource, $params['RGB']['red'], $params['RGB']['green'], $params['RGB']['blue'], $params['alpha']);
if ($color !== FALSE) {
$bounds = NULL;
// Adjust Y position for vertical alignment (if different from bottom).
if ($params['valign'] !== 'bottom') {
// Get bounding box.
// PHP Manual: "This function requires both the GD library and the » FreeType library."
// So it is not more demanding than imagettftext, which we need anyway.
$bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']);
if ($bounds === FALSE) {
drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment settings.'), 'warning');
}
else {
// Get height of bounding box.
$height = $bounds[1] - $bounds[7];
// Shift ypos down (full height on bottom, half the height on center).
$params['ypos'] += $params['valign'] === 'center' ? (int) ($height/2) : $height;
}
}
// Adjust X position for horizontal alignment (if different from left).
if ($params['halign'] !== 'left') {
// Get bounding box.
// PHP Manual: "This function requires both the GD library and the » FreeType library."
// So it is not more demanding than imagettftext, which we need anyway.
if ($bounds === NULL) {
$bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']);
if ($bounds === FALSE) {
drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment.'), 'warning');
}
}
if ($bounds !== FALSE) {
// Get width of bounding box.
$width = $bounds[2] - $bounds[0];
// Shift xpos to the left (full width on right, half the width on center).
$params['xpos'] -= $params['halign'] === 'center' ? (int) ($width/2) : $width;
}
}
// PHP Manual: "This function requires both the GD library and the » FreeType library."
$bounds = imagettftext($image->resource, $params['size'], $params['angle'], $params['xpos'], $params['ypos'], $color, $params['fontpath'], $params['text']);
return $bounds !== FALSE;
}
return FALSE;
}
/**
* Imagemagick toolkit specific implementation of this image effect.
*
* Text in Imagemagick:
* @link http://www.imagemagick.org/script/command-line-options.php?#draw
* @link http://www.imagemagick.org/script/command-line-options.php?#annotate
*
* UTF-8/non-ascii characters
* Though the online imagemagick manual mentions some problems with accented
* characters, it worked fine for me in a Windows Vista shell. TBC on other
* OS'es (including linux)
* (@see: http://www.imagemagick.org/Usage/windows/#character_encoding)
*
* Alignment in Imagemagick:
* This is not directly supported, though a justicifcation option has been
* proposed: @link http://www.imagemagick.org/Usage/bugs/future/#justification.
*
* What we do have is the gravity option:
* @link http://www.imagemagick.org/Usage/annotating/#gravity
* Gravity is used to position a text, but it also automatically applies a
* justification based on that placement. So we use gravity here for alignment,
* but will thus have to rebase our positioning.
*
* Gravity |halign|valign |hpos change|vpos change
* ------------------------------------------------
* NorthWest left top 0 0
* North center top -width/2 0
* NorthEast right top -width 0
* West left center 0 -height/2
* Center center center -width/2 -height/2
* East right center -width -height/2
* SouthWest left bottom 0 -height
* South center bottom -width/2 -height
* SouthEast right bottom -width -height
*/
function image_imagemagick_image_effects_text($image, $params) {
static $alignments2gravity = array(
'left' => array(
'top' => array(
'gravity' => 'NorthWest',
'tx' => 0,
'ty' => 0,
),
'center' => array(
'gravity' => 'West',
'tx' => 0,
'ty' => -0.5,
),
'bottom' => array(
'gravity' => 'SouthWest',
'tx' => 0,
'ty' => 1, // reversed translation
),
),
'center' => array(
'top' => array(
'gravity' => 'North',
'tx' => -0.5,
'ty' => 0,
),
'center' => array(
'gravity' => 'Center',
'tx' => -0.5,
'ty' => -0.5,
),
'bottom' => array(
'gravity' => 'South',
'tx' => -0.5,
'ty' => 1, // reversed translation
),
),
'right' => array(
'top' => array(
'gravity' => 'NorthEast',
'tx' => 1, // reversed translation
'ty' => 0,
),
'center' => array(
'gravity' => 'East',
'tx' => 1, // reversed translation
'ty' => -0.5,
),
'bottom' => array(
'gravity' => 'SouthEast',
'tx' => 1, // reversed translation
'ty' => 1, // reversed translation
),
),
);
// Convert color and alpha to Imagemagick rgba color argument.
$alpha = $params['alpha'] / 100;
$color = 'rgba(' . $params['RGB']['red']. ',' . $params['RGB']['green'] . ',' . $params['RGB']['blue'] . ','. $alpha . ')';
// Alignment
$alignment_corrections = $alignments2gravity[$params['halign']][$params['valign']];
$gravity = $alignment_corrections['gravity'];
if ($alignment_corrections['tx'] > 0) {
$params['xpos'] = (int) ($alignment_corrections['tx'] * $image->info['width'] - $params['xpos']);
}
else {
$params['xpos'] += (int) ($alignment_corrections['tx'] * $image->info['width']);
}
if ($alignment_corrections['ty'] > 0) {
$params['ypos'] = (int) ($alignment_corrections['ty'] * $image->info['height'] - $params['ypos']);
}
else {
$params['ypos'] += (int) ($alignment_corrections['ty'] * $image->info['height']);
}
// Define the quote to use around the text. This is part of the argument of
// the -draw command and thus should NOT be the shell argument enclosing
// character.
$quote = strstr($_SERVER['SERVER_SOFTWARE'], 'Win32') || strstr($_SERVER['SERVER_SOFTWARE'], 'IIS') ? "'" : '"';
// and subsequently escape the use of that quote within the text.
$text = $params['text'];
$text = str_replace($quote, "\\$quote", $text);
$image->ops[] = '-font ' . escapeshellarg($params['fontpath']);
$image->ops[] = "-pointsize {$params['size']}";
$image->ops[] = '-fill ' . escapeshellarg($color);
// See issue http://drupal.org/node/1561214, Bootstrap should reset locale settings to UTF-8.
setlocale(LC_ALL, 'C.UTF-8');
$image->ops[] = '-draw ' . escapeshellarg("gravity $gravity translate {$params['xpos']},{$params['ypos']} rotate {$params['angle']} text 0,0 $quote$text$quote");
return TRUE;
}
/**
* UTILITY
*/
/**
* Convert a position into an offset in pixels.
*
* Position may be a number of additions and/or subtractions of:
* - An value, positive or negative, in pixels.
* - A, positive or negative, percentage (%). The given percentage of the
* current dimension will be taken.
* - 1 of the keywords:
* * top: 0
* * bottom: the height of the current image
* * left: 0
* * right: the width of the current image
* * center: 50% (of the current dimension)
* Examples:
* 0, 20, -20, 90%, 33.3% + 10, right, center - 20, 300 - center, bottom - 50.
* Note:
* The algorithm will accept many more situations, though the result may be hard
* to predict.
*
* @param int $width
* The length of the horizontal dimension.
* @param int $height
* The length of the vertical dimension.
* @param int $length
* The length of the current dimension (should be either width or height).
* @param string $position
* The string defining the position.
*
* @return number
* The computed offset in pixels.
*/
function image_effects_text_get_offset($position, $width, $height, $length) {
$value = 0;
$tokens = preg_split('/ *(-|\+) */', $position, 0, PREG_SPLIT_DELIM_CAPTURE);
$sign = 1;
foreach ($tokens as $token) {
switch ($token) {
case '+';
// Ignore, doesn't change the sign
break;
case '-';
// Flip the sign.
$sign = -$sign;
break;
case 'top':
case 'left':
// Actually, top and left are a no-op.
$value += $sign * 0;
$sign = 1;
break;
case 'bottom':
// Use height of the image, even if this is for the horizontal position.
$value += $sign * $height;
$sign = 1;
break;
case 'right':
// Use width of the image, even if this is for the vertical position.
$value += $sign * $width;
$sign = 1;
break;
case 'center':
// half the current dimension as provided by $length.
$value += $sign * $length/2;
$sign = 1;
break;
default:
// Value: absolute or percentage
if (substr($token, -strlen('%')) === '%') {
$percentage = ((float) substr($token, 0, -strlen('%'))) / 100.0;
$value += $sign * ($percentage * $length);
}
else {
$value += $sign * (float) $token;
}
$sign = 1;
break;
}
}
return $value;
}
/**
* Get the text to use for this image.
*
* @param object $image
* The image the current effect is to be applied to.
* @param array $data
* An array containing the effect data.
*
* @return string
* Plain string to be placed on the image.
*/
function image_effects_text_get_text($image, $data) {
if ($data['text_source'] === 'text') {
$text = $data['text'];
}
else {
// Get context about the image.
$image_context = imagecache_actions_get_image_context($image, $data);
if ($data['text_source'] === 'alt' || $data['text_source'] === 'title') {
// Existence of an image field is not guaranteed, so check for that first.
$text = isset($image_context['image_field'][$data['text_source']]) ? $image_context['image_field'][$data['text_source']] : '';
}
else { // $data['text_source'] === 'php'
// Process the php using php_eval (rather than eval), but with GLOBAL
// variables, so they can be passed successfully.
$GLOBALS['image_context'] = $image_context;
$GLOBALS['image'] = $image;
// We don't need to check_plain() the resulting text, as the text is not
// rendered in a browser but processed on the server.
$text = module_exists('php') ? php_eval('<'.'?php global $image, $image_context; ' . $data['php'] . ' ?'.'>') : '';
unset($GLOBALS['image']);
unset($GLOBALS['image_context']);
}
}
return $text;
}

View File

@@ -0,0 +1,14 @@
name = Image Text Effects
description = Display simple or dynamic captions on images.
package = Media
core = 7.x
dependencies[] = image
dependencies[] = imagecache_actions
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,25 @@
<?php
/**
* @file Set up new text effects.
*
* @todo: is this cache we are clearing still in use?
*/
/**
* Implements hook_install().
*
* Need to flush the cache when this module is enabled or disabled.
*/
function image_effects_text_install() {
cache_clear_all('imagecache_actions', 'cache');
drupal_set_message(t('Additional image effects to add text should now be available in the effects list on !settings_link', array('!settings_link' => l(t('settings'), 'admin/config/media/image-styles'))));
}
/**
* Implements hook_uninstall().
*
* This hook implementation clears the imagecache_actions cache.
*/
function image_effects_text_uninstall() {
cache_clear_all('imagecache_actions', 'cache');
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @file Provide text manipulation effects for image styles.
*
* Ported by dman
* from http://drupal.org/node/264862#comment-865490 by patrickharris
*
* Ported to D7 by fietserwin
* from imagecache_textactions 6.x-1.8.
* - The module has been renamed to follow D7 terminology:
* * imagecache -> image
* * action(s) -> effect(s)
* resulting in image_effects_text.
* - The .module file is kept as small as possible. The real work is done in the
* .inc file.
* - Function and parameter naming has been changed to match the image effects
* in the core image module.
*/
/**
* Implements hook_image_effect_info().
*
* Defines information about the supported effects.
*/
function image_effects_text_image_effect_info() {
$effects = array();
$effects['image_effects_text'] = array(
'label' => t('Text'),
'help' => t('Add static or dynamic (coded) text to an image.'),
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
);
return $effects;
}
/**
* Implements hook_help().
*/
function image_effects_text_help($path, $arg) {
if ($path === 'admin/advanced_help' && count($arg) >= 3 && $arg[2] === 'image_effects_text'
|| $path === 'admin/help/image_effects_text') {
module_load_include('inc', 'image_effects_text', 'image_effects_text');
return image_effects_text_help_inc($path, $arg);
}
else if ($path === 'admin/help#image_effects_text') {
// This path just checks if there is (non-empty) help, so it can place a
// link to it.
return ' ';
}
}
/**
* Implements hook_theme().
*
* We register theme functions for the effect summaries.
*/
function image_effects_text_theme() {
return array(
'image_effects_text_summary' => array(
'variables' => array('data' => NULL),
'file' => 'image_effects_text.inc'
),
);
}
/**
* Builds the form structure for the overlay text image effect.
*/
function image_effects_text_form($data) {
module_load_include('inc', 'image_effects_text', 'image_effects_text');
return image_effects_text_form_inc($data);
}
/**
* Callback to perform the image effect on the given image.
*/
function image_effects_text_effect($image, $data) {
module_load_include('inc', 'image_effects_text', 'image_effects_text');
return image_effects_text_effect_inc($image, $data);
}

View File

@@ -0,0 +1,10 @@
README
------
README for the Image effect text test module.
This module contians 2 image styles to test text effects. Additionally an image
containing a grid and a font are added.`
To make the styles work correctly, please copy the image to the public files
directory, and the font to the parent directory (root directory of the Image
Effects Tesxt module itself).

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,481 @@
<?php
/**
* @file
* image_effects_text_test.features.inc
*/
/**
* Implements hook_image_default_styles().
*/
function image_effects_text_test_image_default_styles() {
$styles = array();
// Exported image style: text-rotate-test
$styles['text-rotate-test'] = array(
'name' => 'text-rotate-test',
'effects' => array(
32 => array(
'label' => 'Échelle',
'help' => 'La mise à l\'échelle maintiendra les proportions originales de l\'image. Si une seule dimension est précisée, l\'autre dimension sera calculée automatiquement.',
'effect callback' => 'image_scale_effect',
'dimensions callback' => 'image_scale_dimensions',
'form callback' => 'image_scale_form',
'summary theme' => 'image_scale_summary',
'module' => 'image',
'name' => 'image_scale',
'data' => array(
'width' => '800',
'height' => '',
'upscale' => 0,
),
'weight' => '1',
),
33 => array(
'label' => 'Overlay (watermark)',
'help' => 'Choose the file image you wish to use as an overlay, and position it in a layer on top of the canvas.',
'effect callback' => 'canvasactions_file2canvas_image',
'form callback' => 'canvasactions_file2canvas_form',
'summary theme' => 'canvasactions_file2canvas_summary',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array(
'xpos' => 0,
'ypos' => 0,
'alpha' => '100',
'path' => 'grid800x600.png',
),
'weight' => '2',
),
34 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '100',
'ypos' => '200',
'halign' => 'left',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '45',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '100,200 45 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '3',
),
35 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '300',
'ypos' => '200',
'halign' => 'center',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '45',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '300,200 45 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '4',
),
37 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '500',
'ypos' => '200',
'halign' => 'right',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '45',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '500,200 45 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '5',
),
38 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '100',
'ypos' => '500',
'halign' => 'left',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '-30',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '100,500 -30 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '7',
),
39 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '300',
'ypos' => '500',
'halign' => 'center',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '-30',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '300,500 -30 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '8',
),
40 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '600',
'ypos' => '400',
'halign' => 'right',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '-30',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '600,400 -30 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '9',
),
),
);
// Exported image style: text-test
$styles['text-test'] = array(
'name' => 'text-test',
'effects' => array(
23 => array(
'label' => 'Overlay (watermark)',
'help' => 'Choose the file image you wish to use as an overlay, and position it in a layer on top of the canvas.',
'effect callback' => 'canvasactions_file2canvas_image',
'form callback' => 'canvasactions_file2canvas_form',
'summary theme' => 'canvasactions_file2canvas_summary',
'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_file2canvas',
'data' => array(
'xpos' => 'left',
'ypos' => 'top',
'alpha' => '100',
'path' => 'grid800x600.png',
),
'weight' => '-10',
),
22 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '100',
'ypos' => '200',
'halign' => 'left',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '100,200 left,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '-8',
),
24 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '300',
'ypos' => '200',
'halign' => 'left',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '300,200 left,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '3',
),
25 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '500',
'ypos' => '200',
'halign' => 'left',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '500,200 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '4',
),
26 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '100',
'ypos' => '400',
'halign' => 'center',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '100,400 center,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '5',
),
27 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '300',
'ypos' => '400',
'halign' => 'center',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '300,400 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '6',
),
28 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '500',
'ypos' => '400',
'halign' => 'center',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '500,400 center, bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '7',
),
29 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '100',
'ypos' => '500',
'halign' => 'right',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '100,500 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '8',
),
30 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '300',
'ypos' => '500',
'halign' => 'right',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '300,500 right,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '9',
),
31 => array(
'label' => 'Texte',
'help' => 'Add static or dynamic (coded) text to an image.',
'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect',
'summary theme' => 'image_effects_text_summary',
'module' => 'image_effects_text',
'name' => 'image_effects_text',
'data' => array(
'size' => '18',
'xpos' => '500',
'ypos' => '500',
'halign' => 'right',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => '500,500 right,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '10',
),
),
);
return $styles;
}

View File

@@ -0,0 +1,17 @@
name = "Image Effects Text test"
description = "Image effects that test the text effect"
core = "7.x"
package = "Features"
version = "7.x-1.0-dev"
project = "image_effects_text_test"
dependencies[] = "image"
dependencies[] = "image_effects_text"
features[image][] = "text-rotate-test"
features[image][] = "text-test"
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,7 @@
<?php
/**
* @file
* Code for the Image Effects Text test feature.
*/
include_once('image_effects_text_test.features.inc');

View File

@@ -0,0 +1,176 @@
<?php
/**
* @file extension to imageapi, provide an overlay action for blending two
* layers, preserving transparency.
*/
// Not sure where this library will live between upgrade versions.
// Be careful to not conflict with another copy of myself.
if (! function_exists('image_overlay')) {
/**
* Place one image over another
*
* @param object $image
* An image object.
* @param object $overlay
* An image object.
* @param $x
* Position of the overlay
* @param $y
* Position of the overlay
* @param $alpha
* Transparency of the overlay from 0-100. 0 is totally transparent. 100
* (default) is totally opaque.
* @param $reverse
* BOOL flag to indicate the 'overlay' actually goes under the image. As
* the imageapi callbacks modify the $image object by reference, this is needed
* to replace the old image resource with the new one.
* @return bool success
*
* @ingroup imageapi
*/
function image_overlay($image, &$layer, $x, $y, $alpha = 100, $reverse = FALSE) {
$x = imagecache_actions_keyword_filter($x, $image->info['width'], $layer->info['width']);
$y = imagecache_actions_keyword_filter($y, $image->info['height'], $layer->info['height']);
return image_toolkit_invoke('overlay', $image, array($layer, $x, $y, $alpha, $reverse));
}
/**
* Place one image over another
* This modifies the passed image by reference
*
* This func is nominated for inclusion in imageapi package. Until then, we do
* it ourselves.
*
* NOTE that the PHP libraries are not great at merging images SO we include a
* library that does it pixel-by-pixel which is INCREDIBLY inefficient. If this
* can be improved, in a way that supports all transparency, please let us know!
*
* A watermark is layer onto image, return the image. An underlay is image onto
* layer, return the layer. Almost identical, but seeing as we work with
* resource handles, the handle needs to be swapped before returning.
*
* @ingroup imageapi
* @param $image
* Base imageapi object.
* @param $overlay
* May be a filename or an imageAPI object
* @param $x
* Position of the overlay
* @param $y
* Position of the overlay
* @param $alpha
* Transparency of the overlay from 0-100. 0 is totally transparent. 100
* (default) is totally opaque.
* @param $reverse
* BOOL flag to indicate the 'overlay' actually goes under the image. As
* the imageapi callbacks modify the $image object by reference, this is needed
* to replace the old image resource with the new one.
* @return bool success
*/
function image_gd_overlay($image, $layer, $x, $y, $alpha = 100, $reverse = FALSE) {
if (empty($layer->resource)) {
trigger_error("Invalid input to " . __FUNCTION__ . " 'layer' is not a valid resource");
#dpm($layer);
return FALSE;
}
// If the given alpha is 100%, we can use imagecopy - which actually works,
// Is more efficient, and seems to retain the overlays partial transparancy
// Still does not work great for indexed gifs though?
if ($reverse) {
$upper = &$image;
$lower = &$layer;
}
else {
$upper = &$layer;
$lower = &$image;
}
if ($alpha == 100 && ($upper->info['mime_type'] != 'image/gif')) {
imagealphablending($lower->resource, TRUE);
imagesavealpha($lower->resource, TRUE);
imagealphablending($upper->resource, TRUE);
imagesavealpha($upper->resource, TRUE);
imagecopy($lower->resource, $upper->resource, $x, $y, 0, 0, $upper->info['width'], $upper->info['height']);
imagedestroy($upper->resource);
$image->resource = $lower->resource;
$image->info = $lower->info;
}
else {
// Else imagecopymerge fails and we have to use the slow library.
module_load_include('inc', 'imagecache_actions', 'watermark');
$watermark = new watermark();
$result_img = $watermark->create_watermark($lower->resource, $upper->resource, $x, $y, $alpha);
// Watermark creates a new image resource, so clean up both old images.
imagedestroy($lower->resource);
imagedestroy($upper->resource);
$image->resource = $result_img;
$image->info = $lower->info;
}
return TRUE;
}
/**
* Improvements on this are welcomed!
*
* Please be aware of the limitations of imagemagick libraries out there - the
* versions distributed on hosted servers (if any) are often several years
* behind. Using the latest imagemagick release features will make this function
* unusable in real deployments.
*
* @param $image
* Base imageapi object.
* @param $layer
* May be a filename or an imageAPI object, gets placed on top
* If using reverse, this is going to be the result we carry on working with.
*/
function image_imagemagick_overlay($image, $layer, $x = 0, $y = 0, $alpha = 100, $reverse = FALSE) {
// In imagemagick terms:
// - $image is the destination (the image being constructed)
// - $layer is the source (the source of the current operation)
// Add the layer image to the imagemagick command line.
$image->ops[] = escapeshellarg($layer->source) . ' ';
// Set its offset. Offset arguments require a sign in front.
if ($x >= 0) {
$x = "+$x";
}
if ($y >= 0) {
$y = "+$y";
}
$image->ops[] = " -geometry $x$y";
// And compose it with the destination.
if ($alpha == 100) {
// Lay one image over the other. The transparency channel of the upper
// image and/or its dimensions (being smaller than the lower image) will
// determine what remains visible of the lower image).
//
// Note: In explicitly setting a -compose operator we reset/overwrite any
// previously set one (former versions could produce erroneous results
// in combination with other effects before this one).
if ($reverse) {
$compose_operator = 'dst-over';
}
else {
$compose_operator = 'src-over';
}
$image->ops[] = "-compose $compose_operator -composite ";
}
else {
// Alpha is not 100, so this image effect turns into a blend operation.
// The alpha determines what percentage of the upper image pixel will be
// taken. From the lower image pixel, 100 - alpha percent will be taken.
//
// Note 1: I'm not sure if and how transparency of one or both images is
// used in or after the blend operation.
// Note 2: As of IM v6.5.3-4 (around june 2009) we can use:
// -compose blend -define compose:args=30[,70]
$image->ops[] = "-compose blend -define compose:args=$alpha -composite ";
}
return TRUE;
}
}

View File

@@ -0,0 +1,16 @@
name = Imagecache Actions
description = Provides a number of additional image effects.
package = Media
core = 7.x
dependencies[] = image
; files with classes
files[] = ImageCacheActionsModuleStreamWrapper.php
; Information added by drupal.org packaging script on 2012-05-19
version = "7.x-1.0"
core = "7.x"
project = "imagecache_actions"
datestamp = "1337423199"

View File

@@ -0,0 +1,40 @@
/*
* UI enhancements for the imagecache edit form
*/
(function($){
/**
* Check if independent corners are enabled and disable other fields in the UI
*/
Drupal.canvasactions_roundedcorners_form_disable_fields = function () {
// To get the right effect, we have to set the 'disabled' attribute on the
// field, but set the class on the container item. Tedious.
if (!$(":checkbox#edit-data-independent-corners-set-independent-corners").attr("checked")){
$(".form-item-data-radius").removeClass("form-disabled");
$(".form-item-data-radius input").attr("disabled", false);
$("#independent-corners-set .form-item").addClass("form-disabled");
$("#independent-corners-set input").attr("disabled", true);
}
else {
$(".form-item-data-radius").addClass("form-disabled");
$(".form-item-data-radius input").attr("disabled", true);
$("#independent-corners-set .form-item").removeClass("form-disabled");
$("#independent-corners-set input").attr("disabled", false);
}
}
/**
* Trigger the update when the form is ready, and add listener to the checkbox
*/
Drupal.behaviors.canvasactions_roundedcorners = {
attach: function (context, settings) {
Drupal.canvasactions_roundedcorners_form_disable_fields();
$(":checkbox#edit-data-independent-corners-set-independent-corners").change(
function() {
Drupal.canvasactions_roundedcorners_form_disable_fields();
}
);
}
}
})(jQuery);

View File

@@ -0,0 +1,36 @@
<?php
/**
* @file Home for the most basic imagecache_action routines, for re-use
*/
/**
* Need to register the theme functions we expect to use
*/
function imagecache_actions_theme() {
return array(
'imagecacheactions_rgb_form' => array(
'file' => 'utility-color.inc',
'render element' => 'form',
),
'imagecacheactions_rgb' => array(
'file' => 'utility-color.inc',
'variables' => array('rgb' => NULL),
),
);
}
/**
* Implements hook_stream_wrappers().
*/
function imagecache_actions_stream_wrappers() {
$wrappers = array(
'module' => array(
'name' => t('Module files (read-only)'),
'class' => 'ImageCacheActionsModuleStreamWrapper',
'description' => t('Module files (read-only).'),
'type' => STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_READ_VISIBLE,
),
);
return $wrappers;
}

View File

@@ -0,0 +1,240 @@
<?php
//include this file whenever you have to use imageconvolution<6F>
//you can use in your project, but keep the comment below
//great for any image manipulation library
//Made by Chao Xu(Mgccl) 3/1/07
//www.webdevlogs.com
//V 1.0
if (!function_exists('imagefilter')) {
function imagefilter($source, $var, $arg1 = null, $arg2 = null, $arg3 = null) {
#define(<28>IMAGE_FILTER_NEGATE<54>, 0);
#define(<28>IMAGE_FILTER_GRAYSCALE<4C>, 1);
#define(<28>IMAGE_FILTER_BRIGHTNESS<53>, 2);
#define(<28>IMAGE_FILTER_CONTRAST<53>, 3);
#define(<28>IMAGE_FILTER_COLORIZE<5A>, 4);
#define(<28>IMAGE_FILTER_EDGEDETECT<43>, 5);
#define(<28>IMAGE_FILTER_EMBOSS<53>, 6);
#define(<28>IMAGE_FILTER_GAUSSIAN_BLUR<55>, 7);
#define(<28>IMAGE_FILTER_SELECTIVE_BLUR<55>, 8);
#define(<28>IMAGE_FILTER_MEAN_REMOVAL<41>, 9);
#define(<28>IMAGE_FILTER_SMOOTH<54>, 10);
$max_y = imagesy($source);
$max_x = imagesx($source);
switch ($var) {
case 0:
$y = 0;
while ($y<$max_y) {
$x = 0;
while ($x<$max_x) {
$rgb = imagecolorat($source, $x, $y);
$r = 255 - (($rgb >> 16) & 0xFF);
$g = 255 - (($rgb >> 8) & 0xFF);
$b = 255 - ($rgb & 0xFF);
$a = $rgb >> 24;
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a);
}
imagesetpixel($source, $x, $y, $new_pxl);
++$x;
}
++$y;
}
return true;
break;
case 1:
$y = 0;
while ($y<$max_y) {
$x = 0;
while ($x<$max_x) {
$rgb = imagecolorat($source, $x, $y);
$a = $rgb >> 24;
$r = ((($rgb >> 16) & 0xFF)*0.299)+((($rgb >> 8) & 0xFF)*0.587)+(($rgb & 0xFF)*0.114);
$new_pxl = imagecolorallocatealpha($source, $r, $r, $r, $a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, $r, $r, $r, $a);
}
imagesetpixel($source, $x, $y, $new_pxl);
++$x;
}
++$y;
}
return true;
break;
case 2:
$y = 0;
while ($y<$max_y) {
$x = 0;
while ($x<$max_x) {
$rgb = imagecolorat($source, $x, $y);
$r = (($rgb >> 16) & 0xFF) + $arg1;
$g = (($rgb >> 8) & 0xFF) + $arg1;
$b = ($rgb & 0xFF) + $arg1;
$a = $rgb >> 24;
$r = ($r > 255)? 255 : (($r < 0)? 0:$r);
$g = ($g > 255)? 255 : (($g < 0)? 0:$g);
$b = ($b > 255)? 255 : (($b < 0)? 0:$b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a);
}
imagesetpixel($source, $x, $y, $new_pxl);
++$x;
}
++$y;
}
return true;
break;
case 3:
$contrast = pow((100-$arg1)/100, 2);
$y = 0;
while ($y<$max_y) {
$x = 0;
while ($x<$max_x) {
$rgb = imagecolorat($source, $x, $y);
$a = $rgb >> 24;
$r = (((((($rgb >> 16) & 0xFF)/255)-0.5)*$contrast)+0.5)*255;
$g = (((((($rgb >> 8) & 0xFF)/255)-0.5)*$contrast)+0.5)*255;
$b = ((((($rgb & 0xFF)/255)-0.5)*$contrast)+0.5)*255;
$r = ($r > 255)? 255 : (($r < 0)? 0:$r);
$g = ($g > 255)? 255 : (($g < 0)? 0:$g);
$b = ($b > 255)? 255 : (($b < 0)? 0:$b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a);
}
imagesetpixel($source, $x, $y, $new_pxl);
++$x;
}
++$y;
}
return true;
break;
case 4:
$x = 0;
while ($x<$max_x) {
$y = 0;
while ($y<$max_y) {
$rgb = imagecolorat($source, $x, $y);
$r = (($rgb >> 16) & 0xFF) + $arg1;
$g = (($rgb >> 8) & 0xFF) + $arg2;
$b = ($rgb & 0xFF) + $arg3;
$a = $rgb >> 24;
$r = ($r > 255)? 255 : (($r < 0)? 0:$r);
$g = ($g > 255)? 255 : (($g < 0)? 0:$g);
$b = ($b > 255)? 255 : (($b < 0)? 0:$b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a);
}
imagesetpixel($source, $x, $y, $new_pxl);
++$y;
}
++$x;
}
return true;
break;
case 5:
return imageconvolution($source, array(array(-1, 0, -1), array(0, 4, 0), array(-1, 0, -1)), 1, 127);
break;
case 6:
return imageconvolution($source, array(array(1.5, 0, 0), array(0, 0, 0), array(0, 0, -1.5)), 1, 127);
break;
case 7:
return imageconvolution($source, array(array(1, 2, 1), array(2, 4, 2), array(1, 2, 1)), 16, 0);
break;
case 8:
for ($y = 0; $y<$max_y; $y++) {
for ($x = 0; $x<$max_x; $x++) {
$flt_r_sum = $flt_g_sum = $flt_b_sum = 0;
$cpxl = imagecolorat($source, $x, $y);
for ($j=0; $j<3; $j++) {
for ($i=0; $i<3; $i++) {
if (($j == 1) && ($i == 1)) {
$flt_r[1][1] = $flt_g[1][1] = $flt_b[1][1] = 0.5;
}
else {
$pxl = imagecolorat($source, $x-(3>>1)+$i, $y-(3>>1)+$j);
$new_a = $pxl >> 24;
//$r = (($pxl >> 16) & 0xFF);
//$g = (($pxl >> 8) & 0xFF);
//$b = ($pxl & 0xFF);
$new_r = abs((($cpxl >> 16) & 0xFF) - (($pxl >> 16) & 0xFF));
if ($new_r != 0) {
$flt_r[$j][$i] = 1/$new_r;
}
else {
$flt_r[$j][$i] = 1;
}
$new_g = abs((($cpxl >> 8) & 0xFF) - (($pxl >> 8) & 0xFF));
if ($new_g != 0) {
$flt_g[$j][$i] = 1/$new_g;
}
else {
$flt_g[$j][$i] = 1;
}
$new_b = abs(($cpxl & 0xFF) - ($pxl & 0xFF));
if ($new_b != 0) {
$flt_b[$j][$i] = 1/$new_b;
}
else {
$flt_b[$j][$i] = 1;
}
}
$flt_r_sum += $flt_r[$j][$i];
$flt_g_sum += $flt_g[$j][$i];
$flt_b_sum += $flt_b[$j][$i];
}
}
for ($j=0; $j<3; $j++) {
for ($i=0; $i<3; $i++) {
if ($flt_r_sum != 0) {
$flt_r[$j][$i] /= $flt_r_sum;
}
if ($flt_g_sum != 0) {
$flt_g[$j][$i] /= $flt_g_sum;
}
if ($flt_b_sum != 0) {
$flt_b[$j][$i] /= $flt_b_sum;
}
}
}
$new_r = $new_g = $new_b = 0;
for ($j=0; $j<3; $j++) {
for ($i=0; $i<3; $i++) {
$pxl = imagecolorat($source, $x-(3>>1)+$i, $y-(3>>1)+$j);
$new_r += (($pxl >> 16) & 0xFF) * $flt_r[$j][$i];
$new_g += (($pxl >> 8) & 0xFF) * $flt_g[$j][$i];
$new_b += ($pxl & 0xFF) * $flt_b[$j][$i];
}
}
$new_r = ($new_r > 255)? 255 : (($new_r < 0)? 0:$new_r);
$new_g = ($new_g > 255)? 255 : (($new_g < 0)? 0:$new_g);
$new_b = ($new_b > 255)? 255 : (($new_b < 0)? 0:$new_b);
$new_pxl = imagecolorallocatealpha($source, (int)$new_r, (int)$new_g, (int)$new_b, $new_a);
if ($new_pxl == false) {
$new_pxl = imagecolorclosestalpha($source, (int)$new_r, (int)$new_g, (int)$new_b, $new_a);
}
imagesetpixel($source, $x, $y, $new_pxl);
}
}
return true;
break;
case 9:
return imageconvolution($source, array(array(-1, -1, -1), array(-1, 9, -1), array(-1, -1, -1)), 1, 0);
break;
case 10:
return imageconvolution($source, array(array(1, 1, 1), array(1, $arg1, 1), array(1, 1, 1)), $arg1+8, 0);
break;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More