added missing module fixer and updated imagecache_actions

This commit is contained in:
Bachir Soussi Chiadmi 2018-03-21 17:06:08 +01:00
parent 3f7e130321
commit 0bcc558ed5
83 changed files with 6386 additions and 2517 deletions

View File

@ -1 +0,0 @@
*.patch

View File

@ -3,6 +3,140 @@ Imagecache Actions
Imagecache Actions 7.x-1.x-dev Imagecache Actions 7.x-1.x-dev
------------------------------ ------------------------------
Imagecache Actions 7.x-1.9
--------------------------
- [#2760121] by lebster, fietserwin: Add a "Perspective" effect.
- [#2947014]: PHP 7.2 warning that each() has been deprecated.
Imagecache Actions 7.x-1.8
--------------------------
- [#2917097]: PHP 7.1 Warning "A non-numeric value encountered".
- [#2905130] by bserem, fietserwin: Remove animation from moving images.
- [#2888678] by das-peter: ImageMagick: Add support for invert effect.
- [#1099300]: Notice: A non well formed numeric value encountered in
image_gd_definecanvas() (line 381 of canvasactions.inc).
- [#2719661] by SKAUGHT: Division by zero.
- [#2717789]: Prevent exif_read_data() warnings.
- Warning: Theme hook coloractions_invert_summary not found.
- [#2696225] by mnlund, fietserwin: Add support for multiply color blending.
- by fietserwin: image_styles_admin: sort image styles by label.
- [#2039379]: Add some better examples for using custom actions.
- Made more contextual information (image style and image effect) available in
the custom action and text image effects.
- [#2690337] by Ajithlal, fietserwin: Would like a feature for converting case
in text effect.
Imagecache Actions 7.x-1.7
--------------------------
- [#2671526]: Fatal error on image style flush when image styles are defined in
Features.
Imagecache Actions 7.x-1.6
--------------------------
- [#2664020]: IE fails to display images whose extension and Content-Type header
do not match the actual image type.
- [#1591484]: Underlay not working (on IM).
- [#2359523]: Follow up to improve help text.
- [#2615986] by hargobind: Effect summary in image_styles_admin.module.
- Notice: Undefined index: element in theme_coloractions_alpha_summary() (line
98 of coloractions\transparency.inc).
- Removed superfluous theme entry 'imagecache_subroutine_summary' from
autorotate sub module.
- [#2080877] by heathdutton: Interlace / Progressive effect.
- [#760438] by fietserwin: Resize Overlay / Watermark for ImageMagick.
- [#760438] by raintonr, Ace Cooper, ttkaminski, thijsvdanker, fietserwin:
Resize Overlay / Watermark (GD only).
- [#768980] by idebr, fietserwin: Blur effect.
- [#2636314]: 'Import style' can fail on newline characters.
- [#2573225]: Resize by percentage not calculating other field.
- [#2447419]: Correct erroneous call to module_load_include in
imagecache_canvasactions.module.
- [#2416805]: Remove reference to non-existent imagecache_coloractions.install.
- [#2416805]: remove deprecated jquery attr() and replace with FAPI #states
(rounded corners UI form).
- [#2580805]: Coding standards corrections.
Imagecache Actions 7.x-1.5 2014-11-29
-------------------------------------
- [#2366163]: autorotate sometimes doesn't work with ImageMagick.
- Text effect: small clean-up of hook_exit().
- [#2379359]: image_imagemagick_overlay() passes a stream wrapper to convert.
- [#2359523]: Overlay: allow to define offsets from all sides.
- [#1717436]: Add effects: color level adjustment and desaturate while retaining
alpha.
- [#2278233]: Call to undefined function imagecache_actions_get_style_label().
- [#2183721]: Duplicate/Export links just edit, they don't duplicate or export.
possible solution only.
- [#2176727]: New actions: scale / resize by percent.
- [#2190759]: hook_image_style_flush(): Prefer silent failure over clear failure.
- [#2166715]: Typo causes extra query every page load.
- [#2139091]: # sign before HEX in settings form breaks Define canvas action.
- [#2085967]: Remove .gitignore from version control.
- Text overlay: treat empty texts as normal situation.
- [#2003446]: Clean up or remove .install files, adapt .info files.
- [1854270-48]: Minor optimization for Posterize.
- Refactor: eliminate test warnings and errors.
- Refactor: standardize function and parameter naming, function order, doxygen
documentation and comments, remove spelling errors in doc and comments.
Imagecache Actions 7.x-1.4 2013-08-24
-------------------------------------
- [#1778214]: Remove project from image_effects_text_test.info
- [#1854270]: Posterize action for file size/bandwidth saving on PNGs.
- [#2060173]: Support image labels (introduced by D7.23).
- Cleaned up the output of image style export. Id's and help texts are not used
on import, so don't have to be exported.
- Repaired farbtastic colorpicker. It seems the library invocation changed
sometime in history. Rebuilt the color picker form to use #attached libraries
and simpler inline script instead. REMOVED the theme function that is now
redundant as #attached is a better way than drupal_add_js.
- [#1152736]: Support tokens in text actions for images.
- [#2025631]: Imagecache Testsuite refers to non-existent permissions.
- Drupal.org went from http to https: changed all links in our documentation and
code.
- Added ImageEffects as other image effect providing module.
Imagecache Actions 7.x-1.3 2013-06-04
-------------------------------------
- Removed some errors and warnings from the test suite code (but not all).
- [#2010560]: Fix spelling for "Update 7001".
Imagecache Actions 7.x-1.2 2013-06-01
-------------------------------------
- [#1830130]: Allow file fields as referring entities in effects with context.
- [#1446160]: Integrate with media module: Title and Alt text.
- [#1591198]: Have dimensions calculations not ignore NULL's.
- Refactored canvas effects: added parameter types in documentation and
signatures. Standardized function documentation. Comments according to Drupal
coding standards.
- [#1999140]: Canvas effects using imagemagick might fail due to earlier
-gravity settings.
- Refactored the image_effects_text_test module to no longer depend on features.
- [#1998354]: Non-ASCII characters fail to display correctly on many systems
(when using Imagemagick).
- Further code enhancements (messages, comments) to autorotate.
- [#1930728]: Autorotate does not reset EXIF orientation tag.
- [#1990620] Many config form docs missing from the D7 upgrade. Converted all
legacy instances of #type=markup into #markup='content..'
- Removed deprecated functions imagecache_actions_entities_from_references() and
imagecache_actions_fields_from_filepath() (see [#1844298]).
- [#1983168]: Custom text via PHP causing EntityMalformedException ...
- Replaced lhandw.ttf font with some real free fonts that may also be
distributed for free. Changed all text effect tests accordingly.
- Better text effect defaults (translated sample text, text visible by default,
sample font file).
- [#1986304]: Strict warning: Only variables should be passed by reference ...
- [#1981490]: Wrong filename imagecache_Actions.install.
- [#1858760]: Unable to find the file 'lhandw.ttf'. Please check the path.
Imagecache Actions 7.x-1.1 2012-12-04
-------------------------------------
- [#1591198]: Image dimensions callbacks should handle unknown (NULL) dimensions - [#1591198]: Image dimensions callbacks should handle unknown (NULL) dimensions
as valid input. as valid input.
- [#464092]: Aspect Switcher -- Need to Flush Presets. Side-effect: now allows - [#464092]: Aspect Switcher -- Need to Flush Presets. Side-effect: now allows
@ -38,7 +172,7 @@ Incompatibilities:
file. This may cause current styles to not being able to find specified files file. This may cause current styles to not being able to find specified files
anymore. anymore.
- Custom actions: custom snippets are now executed using the PHP filter module, - Custom actions: custom snippets are now executed using the PHP filter module,
meaning that the image syle editor must have the 'Use PHP for settings' meaning that the image style editor must have the 'Use PHP for settings'
permission to be able to edit the custom action snippet. permission to be able to edit the custom action snippet.
- Custom actions: information and variables that are available in your custom - Custom actions: information and variables that are available in your custom
snippet have changed. See the README.txt of the custom actions module. snippet have changed. See the README.txt of the custom actions module.
@ -54,6 +188,6 @@ coverage".
Current and past maintainers for Imagecache Actions Current and past maintainers for Imagecache Actions
--------------------------------------------------- ---------------------------------------------------
- dman (http://drupal.org/user/33240) - dman (https://drupal.org/user/33240)
- sidneyshan (http://drupal.org/user/652426) - sidneyshan (https://drupal.org/user/652426)
- fietserwin (http://drupal.org/user/750928) - fietserwin (https://drupal.org/user/750928)

View File

@ -1,15 +1,30 @@
README for the Imagecache Actions Drupal module README for the Imagecache Actions Drupal module
----------------------------------------------- -----------------------------------------------
Project page: http://drupal.org/project/imagecache_actions Project page: https://drupal.org/project/imagecache_actions
Current and past maintainers for Imagecache Actions: Current and past maintainers for Imagecache Actions:
- dman (http://drupal.org/user/33240) - dman (https://drupal.org/user/33240)
- sidneyshan (http://drupal.org/user/652426) - sidneyshan (https://drupal.org/user/652426)
- fietserwin (http://drupal.org/user/750928) - fietserwin (https://drupal.org/user/750928)
Release notes for 7.x-1.x-dev
-----------------------------
- Clear all caches after updating.
Release notes for 7.x-1.4
-------------------------
- This release supports image labels as introduced by Drupal 7.23.
- See CHANGELOG.txt for a full overview of changes.
Release notes for 7.x-1.1 Release notes for 7.x-1.1
------------------------- -------------------------
- If you use the module:// notation anywhere in an image effect, you must now
install the System Stream Wrapper module
(https://drupal.org/project/system_stream_wrapper).
- Clear the cache after updating. - Clear the cache after updating.
@ -57,27 +72,28 @@ The additional effects that Imagecache Actions provides include:
- Text overlay: add e.g. a copyright notice to your image. - Text overlay: add e.g. a copyright notice to your image.
- Color-shifting: colorize images. - Color-shifting: colorize images.
- Brighten/Darken. - Brighten/Darken.
- Alpha blending: use a grayscale image to define the transparency layer of an - Alpha blending: use a gray scale image to define the transparency layer of an
image. image.
- Canvas manipulation: resize the canvas and add a backgroundcolor or image. - Canvas manipulation: resize the canvas and add a background color or image.
- File Format switcher: if you need tranparency in JPGs, make them PNG. If your - File Format switcher: if you need transparency in JPGs, make them PNG. If your
PNG thumbnails are 30K each, save them as JPGs. PNG thumbnails are 30K each, save them as JPGs.
- Rounded corners. - Rounded corners.
- TODO: complete list, check short descrptions - TODO: complete list, check short descriptions
These effects are grouped in submodules. Just enable the ones you want to use. These effects are grouped in sub-modules. Just enable the ones you want to use.
TODO: list submodules and their sets of effects. TODO: list sub-modules and their sets of effects.
Imagecache Actions supports both the GD toolkit from Drupal core and the Imagecache Actions supports both the GD toolkit from Drupal core and the
Imagemagick toolkit. However, please note that Imagemagick support is not yet Imagemagick toolkit. However, please note that Imagemagick support is not yet
complete. Please file an issue if you encounter problems in using Imagemagick. complete. Please file an issue if you encounter problems in using Imagemagick.
What is imagecache_action not? What is imagecache_action not?
------------------------------ ------------------------------
Imagecache Actions does not provide a new UI or new menu items. It hooks into 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 the already existing image styles system (from Drupal core). See
http://drupal.org/documentation/modules/image for more information about working https://drupal.org/documentation/modules/image for more information about
with images. working with images.
A note about the name of this module A note about the name of this module
@ -90,7 +106,7 @@ porting to D7, that name has not been changed (yet).
Which toolkit to use? Which toolkit to use?
--------------------- ---------------------
Personally, I (fieterwin) prefer the imagemagick toolkit: Personally, I (fietserwin) prefer the imagemagick toolkit:
- It is better in anti-aliasing. Try to rotate an image using both toolkits and - It is better in anti-aliasing. Try to rotate an image using both toolkits and
you will see what I mean. you will see what I mean.
- It does not execute in the PHP memory space, so is not restricted by the - It does not execute in the PHP memory space, so is not restricted by the
@ -102,27 +118,36 @@ 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 whereas imagemagick is not always present on shared hosting or may be present in
an antique version that might give problems. an antique version that might give problems.
Please also note that effects may give different results depending on the Please note that effects may give different results depending on the
toolkit used. toolkit used.
Please also note that a 3rd image toolkit exists:
Imagick (https://www.drupal.org/project/imagick)
This toolkit uses the Imagick extension and thus does not call the ImageMagick
binaries directly. However our module does not implement the toolkit specific
parts for the effects we provide for the Imagick toolkit, but the Imagick
toolkit comes with its own set of effects that partly covers our effects. So,
depending on the effects you require it may be a replacement for both our module
and the Imagemagick module or the core GD toolkit.
Hard Dependencies Hard Dependencies
----------------- -----------------
- Drupal 7.x
- Image module from Drupal core - Image module from Drupal core
At least 1 of the available image toolkits: At least 1 of the available image toolkits:
- GD toolkit from Drupal core - GD toolkit from Drupal core.
- Imagemagick toolkit: http://drupal.org/project/imagemagick - Imagemagick toolkit: https://drupal.org/project/imagemagick.
Soft Dependencies Soft Dependencies
----------------- -----------------
- System stream wrapper (http://drupal.org/project/system_stream_wrapper) - System stream wrapper (https://drupal.org/project/system_stream_wrapper)
- Remote stream wrapper (http://drupal.org/project/remote_stream_wrapper) - Remote stream wrapper (https://drupal.org/project/remote_stream_wrapper)
These modules provide additional stream wrappers. Especially the system stream These modules provide additional stream wrappers. Especially the system stream
wrapper is very handy as it provides, among others, a module:// and theme:// wrapper is very handy as it provides, among others, a module:// and theme://
wrapper. wrapper.
Installing Installing
---------- ----------
As usual. As usual.
@ -175,7 +200,6 @@ be defined using either:
- A relative (to the current directory, probably Drupal root) or absolute path. - A relative (to the current directory, probably Drupal root) or absolute path.
Support Support
------- -------
Via the issue queue of this project at Drupal.org. Via the issue queue of this project at Drupal.org.
@ -194,3 +218,8 @@ well).
- Brightness values outside the -250 .. 250 range are accepted. - Brightness values outside the -250 .. 250 range are accepted.
- Check color fields that allow a transparency component or allow to be empty to - Check color fields that allow a transparency component or allow to be empty to
specify fully transparent. specify fully transparent.
Known problems: Imagemagick
---------------------------
- Define canvas using offsets may bot work on older versions. We have an error
report for version 6.5.4.7 (2009-07) (https://drupal.org/node/888644).

View File

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

View File

@ -1,16 +1,13 @@
name = Imagecache Autorotate name = Imagecache Autorotate
description = Autorotate image based on EXIF Orientation. description = Provides an image effect to autorotate an image based on EXIF data.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = image dependencies[] = image
files[] = imagecache_autorotate.install ; Information added by Drupal.org packaging script on 2018-03-20
files[] = imagecache_autorotate.module version = "7.x-1.9"
; Information added by drupal.org packaging script on 2012-12-04
version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,13 +1,25 @@
<?php <?php
/** /**
* @file (un)install and (dis/en)able hooks for imagecache autorotate module. * Implements hook_requirements().
*/ */
function imagecache_autorotate_requirements($phase) {
/** $result = array();
* Implements hook_enable(). $t = get_t();
*/ if (!extension_loaded('exif')) {
function imagecache_autorotate_enable() { $result['imagecache_autorotate_exif_extension'] = array(
if (!function_exists('exif_read_data')) { 'title'=> 'EXIF extension',
drupal_set_message(t('The exif_read_data() function is not available in this PHP installation. You probably will have to enable the exif extension.'), 'warning'); 'value'=> $phase === 'runtime' ? $t('Disabled') : '',
'description'=> $t('The autorotate image effect requires the exif extension to be enabled.'),
'severity' => REQUIREMENT_ERROR,
);
} }
else {
$result['imagecache_autorotate_exif_extension'] = array(
'title'=> 'EXIF extension',
'value'=> $t('Enabled'),
'severity' => REQUIREMENT_OK,
);
}
return $result;
} }

View File

@ -1,61 +1,71 @@
<?php <?php
/** /**
* @file * @file Autorotate image based on EXIF Orientation tag.
* 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 * EXIF: https://en.wikipedia.org/wiki/Exchangeable_image_file_format
* EXIF orientation tag: http://sylvana.net/jpegcrop/exif_orientation.html
*
* Originally contributed by jonathan_hunt https://drupal.org/user/28976,
* September 1, 2009 * September 1, 2009
*/
/**
* Implements hook_image_effect_info().
* *
* Tweaked by dman to add documentation * Defines information about the supported effects.
*/ */
/* 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() { function imagecache_autorotate_image_effect_info() {
$effects = array(); $effects = array();
$effects['imagecache_autorotate'] = array( $effects['imagecache_autorotate'] = array(
'label' => t('Autorotate'), 'label' => t('Autorotate'),
'help' => t('Add autorotate image based on EXIF Orientation.'), 'help' => t('Autorotate image based on EXIF orientation and reset that tag.'),
'effect callback' => 'imagecache_autorotate_image', 'effect callback' => 'imagecache_autorotate_effect',
'dimensions callback' => 'imagecache_autorotate_dimensions', 'dimensions callback' => 'imagecache_autorotate_dimensions',
'form callback' => 'imagecache_autorotate_form',
'summary theme' => 'imagecache_autorotate_summary',
); );
return $effects; return $effects;
} }
/** /**
* @todo: This form is no longer needed nor defined in the hook above. If this * Implements hook_theme().
* information still needs to be displayed it should probably be moved to help. *
* Registers theme functions for the effect summaries.
*/
function imagecache_autorotate_theme() {
return array(
'imagecache_autorotate_summary' => array(
'variables' => array('data' => NULL),
),
);
}
/**
* Builds the auto-rotate form.
*
* This effect has no options, only some help text, so the form is displayed
* anyway.
*/ */
function imagecache_autorotate_form() { function imagecache_autorotate_form() {
$form = array(); $form = array();
$form['help'] = array( $form['help'] = array(
'#type' => 'markup', '#markup' => "<p><strong>There are no user-configurable options for this process.</strong></p>
'#value' => "<p> <p>Certain cameras can embed <em>orientation</em> information into image
<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 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. 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> <em>Not all cameras or images contain this information.</em>
This process is only useful for those that do. This process is only useful for images that contain this information,
</p><p> whereas for other images it is harmless.
The expected/supported values are </p>
<br/><strong>Tag</strong>: <code>0x0112 Orientation</code> <p>Although most modern browsers do support the orientation tag, the
information may get lost or become incorrect by other operations.
So, to support all browsers and prevent rotation errors, it is better to
start each image style with this effect.
</p>
<p>The expected/supported values are:<br/>
<strong>Tag</strong>: <code>0x0112 Orientation</code>
</p> </p>
<ul> <ul>
<li>1 = Horizontal (normal)</li> <li>1 = Horizontal (normal)</li>
@ -63,83 +73,172 @@ function imagecache_autorotate_form() {
<li>6 = Rotate 90 CW</li> <li>6 = Rotate 90 CW</li>
<li>8 = Rotate 270 CW</li> <li>8 = Rotate 270 CW</li>
</ul> </ul>
<p> <p>Wikipedia: <a href='https://en.wikipedia.org/wiki/Exchangeable_image_file_format'>Exchangeable image file format</a></p>
<a href='http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html'>
EXIF Reference</a>
</p>
", ",
); );
return $form; return $form;
} }
/** /**
* Autorotate image based on EXIF Orientation tag. * Implements theme_hook() for the autorotate effect summary.
* *
* See code at * param array $variables
* http://sylvana.net/jpegcrop/exif_orientation.html * An associative array containing:
* - data: The current configuration for this image effect.
* *
* and reference at * @return string
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html * The HTML for the summary of this image effect.
* * @ingroup themeable
* @todo:
* Add horizontal and vertical flips etc.
* Need to create sample set for tests.
*/ */
function imagecache_autorotate_image($image, $data) { function theme_imagecache_autorotate_summary(/*array $variables*/) {
// Test to see if EXIF is supported for image type (e.g. not PNG). return 'image based on its EXIF data.';
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; * Autorotate image based on EXIF Orientation tag.
} */
$exif = exif_read_data(drupal_realpath($image->source)); function imagecache_autorotate_effect(stdClass $image /*, $data*/) {
if (isset($exif['Orientation'])) { // Test to see if EXIF is supported by the current image type.
switch ($exif['Orientation']) { if (in_array($image->info['mime_type'], array('image/jpeg', 'image/tiff'))) {
case 3: // Hand over to toolkit.
$degrees = 180; return image_toolkit_invoke('imagecache_autorotate', $image);
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;
}
}
}
} }
else if ($image->source === 'modules/image/sample.png' && !function_exists('exif_read_data')) { else if ($image->source === 'modules/image/sample.png' && user_access('administer image styles')) {
// Issue a warning if we are in the admin screen and the exif extension is if (!extension_loaded('exif')) {
// not enabled. // Issue a warning if we are in the admin screen and the exif extension is
drupal_set_message(t('The exif_read_data() function is not available in this PHP installation. You probably will have to enable the exif extension.'), 'warning'); // not enabled.
drupal_set_message(t('The autorotate image effect requires the exif extension to be enabled.'), 'warning');
if ($image->toolkit === 'imagemagick') {
drupal_set_message(t('Though imagemagick will work without the exif extension, subsequent effects may fail as the image dimensions cannot be updated.'), 'warning');
}
}
} }
return TRUE; return TRUE;
} }
/** /**
* Image dimensions callback; Auto-rotate. * GD toolkit specific implementation of this image effect.
*
* @param stdClass $image
*
* @return bool
* true on success, false otherwise.
*/
function image_gd_imagecache_autorotate(stdClass $image) {
if (!function_exists('exif_read_data')) {
watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function is not available in this PHP installation. You probably have to enable the exif extension.')));
return FALSE;
}
// Read and check result.
$exif = @exif_read_data(drupal_realpath($image->source));
if ($exif === FALSE && $image->extension === 'jpg') {
watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.')));
return FALSE;
}
if (isset($exif['Orientation'])) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html:
// 1 = Horizontal (normal)
// 2 = Mirror horizontal
// 3 = Rotate 180
// 4 = Mirror vertical
// 5 = Mirror horizontal and rotate 270 CW
// 6 = Rotate 90 CW
// 7 = Mirror horizontal and rotate 90 CW
// 8 = Rotate 270 CW
// @todo: Add horizontal and vertical flips etc.
// imagecopy seems to be able to mirror, see conmments on
// http://php.net/manual/en/function.imagecopy.php
// @todo: Create sample set for tests.
switch ($exif['Orientation']) {
case 3:
$degrees = 180;
break;
case 6:
$degrees = 90;
break;
case 8:
$degrees = 270;
break;
default:
$degrees = 0;
}
if ($degrees != 0) {
return image_rotate($image, $degrees);
}
}
return TRUE;
}
/**
* Imagemagick toolkit specific implementation of this image effect.
*
* @param stdClass $image
* An image object.
*
* @return bool
* true on success, false otherwise.
*
* @see http://www.imagemagick.org/script/command-line-options.php#auto-orient
*/
function image_imagemagick_imagecache_autorotate(stdClass $image) {
// Use the exif extension, if enabled, to figure out the new dimensions.
// Moreover (see [#2366163]): to prevent a bug in IM to incorrectly rotate the
// image when it should not, we only pass the auto-orient argument when the
// exif extension could detect the 'Orientation' tag.
if (function_exists('exif_read_data')) {
$exif = @exif_read_data(drupal_realpath($image->source));
if (isset($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 1:
// Normal orientation: no need to rotate or to change the dimensions.
break;
case 5:
case 6:
case 7:
case 8:
// 90 or 270 degrees rotation (+ optional mirror): swap dimensions.
$image->ops[] = '-auto-orient';
$tmp = $image->info['width'];
$image->info['width'] = $image->info['height'];
$image->info['height'] = $tmp;
break;
default:
// All other orientations: pass the arguments, but the dimensions
// remain the same.
$image->ops[] = '-auto-orient';
break;
}
}
elseif ($exif === FALSE && $image->extension === 'jpg') {
watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.')));
}
}
else {
// We do add the auto-orient argument to IM. IM will determine itself
// whether to rotate or not.
$image->ops[] = '-auto-orient';
// However we cannot keep track of the dimensions anymore.
if ($image->info['width'] !== $image->info['height']) {
$image->info['width'] = $image->info['height'] = NULL;;
}
}
return TRUE;
}
/**
* Image dimensions callback for this image effect.
* *
* @param array $dimensions * @param array $dimensions
* An array with the dimensions (in pixels) to be modified. * An array with the dimensions (in pixels) to be modified.
* @param array $data * param array $data
* An array of parameters for the autorotate effect (empty for this effect). * An associative array containing the effect data.
*/ */
function imagecache_autorotate_dimensions(array &$dimensions, array $data) { function imagecache_autorotate_dimensions(array &$dimensions/*, array $data*/) {
// We can only know the resulting dimensions if both dimensions are equal. // We can only know the resulting dimensions if both dimensions are equal.
// Otherwise we need to inspect the image itself, which is not passed in here. // Otherwise we need to inspect the image itself, which is not passed in here.
// (this callback was introduced to enhance performance by not accessing the // (this callback was introduced to enhance performance by NOT accessing the
// image file when rendering the width and height attributes of the html img // image file when rendering the width and height attributes of the html img
// tag). // tag).
if ($dimensions['width'] !== $dimensions['height']) { if ($dimensions['width'] !== $dimensions['height']) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@ -1,31 +1,14 @@
name = Imagecache Canvas Actions name = Imagecache Canvas Actions
description = Actions for manipulating image canvases layers, including watermark and background effect. Also an aspect switcher (portrait/landscape) description = Provides image effects for manipulating image canvases: define canvas, image mask, watermark, underlay background image, rounded corners, composite source to image and resize by percent effect. Also provides an aspect switcher (portrait/landscape).
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = imagecache_actions dependencies[] = imagecache_actions
dependencies[] = image dependencies[] = image
files[] = canvasactions.inc ; Information added by Drupal.org packaging script on 2018-03-20
files[] = imagecache_canvasactions.install version = "7.x-1.9"
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-12-04
version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,27 +0,0 @@
<?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

@ -5,7 +5,7 @@
* including "Watermark" * including "Watermark"
* *
* Based on first draft of the code by Dimm (imagecache.module 5--1) * Based on first draft of the code by Dimm (imagecache.module 5--1)
* http://drupal.org/node/184816 * https://drupal.org/node/184816
* *
* Rewritten and ported to Imagecache actions API (imagecache.module 5--2) by * Rewritten and ported to Imagecache actions API (imagecache.module 5--2) by
* dman http://coders.co.nz/ * dman http://coders.co.nz/
@ -39,21 +39,20 @@
* *
*/ */
// During devel, caching is pointless. Flush it
// imagecache_action_definitions(TRUE);
if (! function_exists('imagecache_actions_calculate_relative_position') ) { if (! function_exists('imagecache_actions_calculate_relative_position') ) {
module_load_include('inc', 'imagecache_canvasactions', 'utility'); module_load_include('inc', 'imagecache_actions', 'utility');
} }
// @todo There doesn't seem to be a way to specify a file in hook_image_effect_info // @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. // so placing this here for the time being.
module_load_include('inc', 'imagecache_canvasactions', 'canvasactions'); module_load_include('inc', 'imagecache_canvasactions', 'canvasactions');
module_load_include('inc', 'imagecache_canvasactions', 'rounded_corners'); module_load_include('inc', 'imagecache_canvasactions', 'rounded_corners');
// imageapi extensions
module_load_include('inc', 'imagcache_actions', 'image_overlay.inc');
/**
* Implements hook_image_effect_info().
*
* Defines information about the supported effects.
*/
function imagecache_canvasactions_image_effect_info() { function imagecache_canvasactions_image_effect_info() {
$effects = array(); $effects = array();
@ -69,8 +68,8 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_imagemask'] = array( $effects['canvasactions_imagemask'] = array(
'label' => t('Image mask'), 'label' => t('Image mask'),
'help' => t(' Choose the file image you wish to use as a mask, and apply it to the canvas.'), '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, 'dimensions passthrough' => TRUE,
'effect callback' => 'canvasactions_imagemask_effect',
'form callback' => 'canvasactions_imagemask_form', 'form callback' => 'canvasactions_imagemask_form',
'summary theme' => 'canvasactions_imagemask_summary', 'summary theme' => 'canvasactions_imagemask_summary',
); );
@ -78,8 +77,8 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_file2canvas'] = array( $effects['canvasactions_file2canvas'] = array(
'label' => t('Overlay (watermark)'), '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.'), '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, 'dimensions passthrough' => TRUE,
'effect callback' => 'canvasactions_file2canvas_effect',
'form callback' => 'canvasactions_file2canvas_form', 'form callback' => 'canvasactions_file2canvas_form',
'summary theme' => 'canvasactions_file2canvas_summary', 'summary theme' => 'canvasactions_file2canvas_summary',
); );
@ -87,7 +86,7 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_canvas2file'] = array( $effects['canvasactions_canvas2file'] = array(
'label' => t('Underlay (background)'), 'label' => t('Underlay (background)'),
'help' => t('Choose the file image you wish to use as an background, and position the processed image on it.'), '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', 'effect callback' => 'canvasactions_canvas2file_effect',
'dimensions callback' => 'canvasactions_canvas2file_dimensions', 'dimensions callback' => 'canvasactions_canvas2file_dimensions',
'form callback' => 'canvasactions_canvas2file_form', 'form callback' => 'canvasactions_canvas2file_form',
'summary theme' => 'canvasactions_canvas2file_summary', 'summary theme' => 'canvasactions_canvas2file_summary',
@ -96,8 +95,8 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_source2canvas'] = array( $effects['canvasactions_source2canvas'] = array(
'label' => t('Overlay: source image to canvas'), 'label' => t('Overlay: source image to canvas'),
'help' => t('Places the source image onto the canvas for compositing.'), 'help' => t('Places the source image onto the canvas for compositing.'),
'effect callback' => 'canvasactions_source2canvas_image',
'dimensions passthrough' => TRUE, 'dimensions passthrough' => TRUE,
'effect callback' => 'canvasactions_source2canvas_effect',
'form callback' => 'canvasactions_source2canvas_form', 'form callback' => 'canvasactions_source2canvas_form',
'summary theme' => 'canvasactions_source2canvas_summary', 'summary theme' => 'canvasactions_source2canvas_summary',
); );
@ -105,8 +104,8 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_roundedcorners'] = array( $effects['canvasactions_roundedcorners'] = array(
'label' => t('Rounded Corners'), 'label' => t('Rounded Corners'),
'help' => t('This is true cropping, not overlays, so the result <em>can</em> be transparent.'), 'help' => t('This is true cropping, not overlays, so the result <em>can</em> be transparent.'),
'effect callback' => 'canvasactions_roundedcorners_image',
'dimensions passthrough' => TRUE, 'dimensions passthrough' => TRUE,
'effect callback' => 'canvasactions_roundedcorners_effect',
'form callback' => 'canvasactions_roundedcorners_form', 'form callback' => 'canvasactions_roundedcorners_form',
'summary theme' => 'canvasactions_roundedcorners_summary', 'summary theme' => 'canvasactions_roundedcorners_summary',
); );
@ -114,55 +113,105 @@ function imagecache_canvasactions_image_effect_info() {
$effects['canvasactions_aspect'] = array( $effects['canvasactions_aspect'] = array(
'label' => t('Aspect switcher'), '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.'), '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', 'effect callback' => 'canvasactions_aspect_effect',
'dimensions callback' => 'canvasactions_aspect_dimensions', 'dimensions callback' => 'canvasactions_aspect_dimensions',
'form callback' => 'canvasactions_aspect_form', 'form callback' => 'canvasactions_aspect_form',
'summary theme' => 'canvasactions_aspect_summary', 'summary theme' => 'canvasactions_aspect_summary',
); );
$effects['canvasactions_resizepercent'] = array(
'label' => t('Resize (percent)'),
'help' => t('Resize the image based on percent. If only a single dimension is specified, the other dimension will be calculated.'),
'effect callback' => 'canvasactions_resizepercent_effect',
'dimensions callback' => 'canvasactions_resizepercent_dimensions',
'form callback' => 'canvasactions_resizepercent_form',
'summary theme' => 'canvasactions_resizepercent_summary',
);
$effects['canvasactions_blur'] = array(
'label' => t('Blur'),
'help' => t('Make the image become unclear.'),
'effect callback' => 'canvasactions_blur_effect',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_blur_form',
'summary theme' => 'canvasactions_blur_summary',
);
$effects['canvasactions_interlace'] = array(
'label' => t('Interlace / Progressive'),
'help' => t('Create interlaced PNG/GIF or progressive JPG.'),
'effect callback' => 'canvasactions_interlace_effect',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_interlace_form',
);
$effects['canvasactions_perspective'] = array(
'label' => t('Perspective transform'),
'help' => t('Adds a perspective transformation to the image.'),
'effect callback' => 'canvasactions_perspective_effect',
'dimensions passthrough' => TRUE,
'form callback' => 'canvasactions_perspective_form',
'summary theme' => 'canvasactions_perspective_summary',
);
return $effects; return $effects;
} }
/** /**
* Need to register the theme functions we expect to use * Implements hook_theme().
*
* Registers theme functions for the effect summaries.
*/ */
function imagecache_canvasactions_theme() { function imagecache_canvasactions_theme() {
$util_dir = drupal_get_path('module', 'imagecache_actions');
return array( return array(
'canvasactions_definecanvas_summary' => array( 'canvasactions_definecanvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_imagemask_summary' => array( 'canvasactions_imagemask_summary' => array(
'file' => 'canvasactions.inc',
'arguments' => array('element' => NULL), 'arguments' => array('element' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_file2canvas_summary' => array( 'canvasactions_file2canvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_source2canvas_summary' => array( 'canvasactions_source2canvas_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_canvas2file_summary' => array( 'canvasactions_canvas2file_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_roundedcorners_summary' => array( 'canvasactions_roundedcorners_summary' => array(
'file' => 'rounded_corners.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
), ),
'canvasactions_aspect_summary' => array( 'canvasactions_aspect_summary' => array(
'file' => 'canvasactions.inc',
'variables' => array('data' => NULL), 'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
),
'canvasactions_resizepercent_summary' => array(
'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
),
'canvasactions_blur_summary' => array(
'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
),
'canvasactions_perspective_summary' => array(
'variables' => array('data' => NULL),
'file' => 'canvasactions.inc',
),
'canvasactions_perspective_anchor' => array(
'render element' => 'element',
), ),
); );
} }
/** /**
* Implements hook_image_style_flush. * Implements hook_image_style_flush().
* *
* This hook checks if the image style that is being flushed is used in an * This hook checks if the image style that is being flushed is used in an
* aspect switcher effect. If so, the style that contains the aspect switcher * aspect switcher effect. If so, the style that contains the aspect switcher
@ -171,7 +220,7 @@ function imagecache_canvasactions_theme() {
* @param array $flushed_style * @param array $flushed_style
* The image style that is being flushed. * The image style that is being flushed.
*/ */
function imagecache_canvasactions_image_style_flush($flushed_style) { function imagecache_canvasactions_image_style_flush(/*array*/ $flushed_style) {
$styles = image_styles(); $styles = image_styles();
foreach ($styles as $style) { foreach ($styles as $style) {
if ($style['name'] !== $flushed_style['name']) { if ($style['name'] !== $flushed_style['name']) {

View File

@ -60,6 +60,11 @@ function canvasactions_roundedcorners_form($action) {
$form['independent_corners_set']['radii'] = array( $form['independent_corners_set']['radii'] = array(
'#type' => 'item', '#type' => 'item',
'#id' => 'independent-corners-set', '#id' => 'independent-corners-set',
'#states' => array(
'visible' => array(
':input[name="data[independent_corners_set][independent_corners]"]' => array('checked' => TRUE),
),
),
); );
foreach ($corners as $attribute => $label) { foreach ($corners as $attribute => $label) {
$form['independent_corners_set']['radii'][$attribute] = array( $form['independent_corners_set']['radii'][$attribute] = array(
@ -79,8 +84,7 @@ function canvasactions_roundedcorners_form($action) {
); );
*/ */
$form['notes'] = array( $form['notes'] = array(
'#type' => 'markup', '#markup' => t('
'#value' => t('
Note: the rounded corners effect uses true alpha transparency masking. Note: the rounded corners effect uses true alpha transparency masking.
This means that this effect <b>will fail to be saved</b> on jpegs This means that this effect <b>will fail to be saved</b> on jpegs
<em>unless</em> you either <ul> <em>unless</em> you either <ul>
@ -97,7 +101,7 @@ function canvasactions_roundedcorners_form($action) {
return $form; return $form;
} }
function canvasactions_roundedcorners_image($image, $action = array()) { function canvasactions_roundedcorners_effect($image, $action) {
$independent_corners = !empty($action['independent_corners_set']['independent_corners']); $independent_corners = !empty($action['independent_corners_set']['independent_corners']);
if (!$independent_corners) { if (!$independent_corners) {
// set the independant corners to all be the same. // set the independant corners to all be the same.
@ -125,7 +129,7 @@ function canvasactions_roundedcorners_image($image, $action = array()) {
* @param $image * @param $image
* @param $action * @param $action
*/ */
function image_gd_roundedcorners($image, $action = array()) { function image_gd_roundedcorners($image, $action) {
// Read settings. // Read settings.
$width = $image->info['width']; $width = $image->info['width'];
$height = $image->info['height']; $height = $image->info['height'];
@ -266,7 +270,7 @@ function _canvasactions_roundedcorners_pixel_opacity($x, $y, $r) {
/** /**
* imageapi_roundedcorners * imageapi_roundedcorners
*/ */
function image_imagemagick_roundedcorners($image, $action = array()) { function image_imagemagick_roundedcorners($image, $action) {
// Based on the imagemagick documentation. // Based on the imagemagick documentation.
// http://www.imagemagick.org/Usage/thumbnails/#rounded // http://www.imagemagick.org/Usage/thumbnails/#rounded
// Create arc cut-outs, then mask them. // Create arc cut-outs, then mask them.

View File

@ -1,9 +1,8 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
* *
* Created on Dec 29, 2009 * Created on Dec 29, 2009
* *
* @author 'dman' Dan Morrison http://coders.co.nz/ * @author 'dman' Dan Morrison http://coders.co.nz/
@ -12,7 +11,7 @@
$presets['cheap_dropshadow'] = array ( $presets['cheap_dropshadow'] = array (
'name' => 'cheap_dropshadow', 'name' => 'cheap_dropshadow',
'#weight' => '3.3', '#weight' => '3.3',
'effects' => 'effects' =>
array ( array (
-1 => array ( -1 => array (
'weight' => '-1', 'weight' => '-1',
@ -20,28 +19,29 @@ $presets['cheap_dropshadow'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' => array ( 'data' => array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
0 => 0 =>
array ( array (
'weight' => '0', 'weight' => '0',
'module' => 'imagecache_canvasactions', 'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_definecanvas', 'name' => 'canvasactions_definecanvas',
'data' => 'data' =>
array ( array (
'RGB' => 'RGB' =>
array ( array (
'HEX' => '999999', 'HEX' => '999999',
), ),
'under' => 0, 'under' => 0,
'exact' => 'exact' =>
array ( array (
'width' => '', 'width' => '',
'height' => '', 'height' => '',
'xpos' => 'center', 'xpos' => 'center',
'ypos' => 'center', 'ypos' => 'center',
), ),
'relative' => 'relative' =>
array ( array (
'leftdiff' => '0', 'leftdiff' => '0',
'rightdiff' => '0', 'rightdiff' => '0',
@ -50,26 +50,26 @@ $presets['cheap_dropshadow'] = array (
), ),
), ),
), ),
1 => 1 =>
array ( array (
'weight' => '1', 'weight' => '1',
'module' => 'imagecache_canvasactions', 'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_definecanvas', 'name' => 'canvasactions_definecanvas',
'data' => 'data' =>
array ( array (
'RGB' => 'RGB' =>
array ( array (
'HEX' => '', 'HEX' => '',
), ),
'under' => 1, 'under' => 1,
'exact' => 'exact' =>
array ( array (
'width' => '', 'width' => '',
'height' => '', 'height' => '',
'xpos' => 'center', 'xpos' => 'center',
'ypos' => 'center', 'ypos' => 'center',
), ),
'relative' => 'relative' =>
array ( array (
'leftdiff' => '20', 'leftdiff' => '20',
'rightdiff' => '0', 'rightdiff' => '0',
@ -78,27 +78,27 @@ $presets['cheap_dropshadow'] = array (
), ),
), ),
), ),
2 => 2 =>
array ( array (
'weight' => '2', 'weight' => '2',
'module' => 'imagecache_canvasactions', 'module' => 'imagecache_canvasactions',
'name' => 'canvasactions_source2canvas', 'name' => 'canvasactions_source2canvas',
'data' => 'data' =>
array ( array (
'xpos' => 0, 'xpos' => 0,
'ypos' => 0, 'ypos' => 0,
'alpha' => '100', 'alpha' => '100',
), ),
), ),
3 => 3 =>
array ( array (
'weight' => '3', 'weight' => '3',
'module' => 'image', 'module' => 'image',
'name' => 'image_scale', 'name' => 'image_scale',
'data' => 'data' =>
array ( array (
'width' => '200', 'width' => '200',
'height' => '100%', 'height' => '',
'upscale' => 0, 'upscale' => 0,
), ),
), ),
@ -108,7 +108,8 @@ $presets['cheap_dropshadow'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' => array ( 'data' => array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
), ),
); );

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,9 +1,8 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
* *
* Created on Dec 29, 2009 * Created on Dec 29, 2009
* *
* @author 'dman' Dan Morrison http://coders.co.nz/ * @author 'dman' Dan Morrison http://coders.co.nz/
@ -41,7 +40,8 @@ $presets['positioned_underlay'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' => array ( 'data' => array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
), ),
); );

View File

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

View File

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

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,9 +1,8 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
* *
* Created on Dec 29, 2009 * Created on Dec 29, 2009
* *
* @author 'dman' Dan Morrison http://coders.co.nz/ * @author 'dman' Dan Morrison http://coders.co.nz/
@ -18,7 +17,7 @@ $presets['rotate_scale_alpha'] = array (
'weight' => '1', 'weight' => '1',
'module' => 'image', 'module' => 'image',
'name' => 'image_rotate', 'name' => 'image_rotate',
'data' => array ( 'data' => array (
'degrees' => '65', 'degrees' => '65',
'random' => 0, 'random' => 0,
'bgcolor' => '', 'bgcolor' => '',
@ -33,6 +32,7 @@ $presets['rotate_scale_alpha'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' => array ( 'data' => array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
*/ */
@ -52,8 +52,9 @@ $presets['rotate_scale_alpha'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' => array ( 'data' => array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
), ),
); );

View File

@ -1,9 +1,8 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
* *
* Created on Dec 29, 2009 * Created on Dec 29, 2009
* *
* @author 'dman' Dan Morrison http://coders.co.nz/ * @author 'dman' Dan Morrison http://coders.co.nz/
@ -28,7 +27,8 @@ $presets['rounded'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' =>array ( 'data' =>array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
), ),
); );

View File

@ -1,9 +1,8 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
* *
* Created on Dec 29, 2009 * Created on Dec 29, 2009
* *
* @author 'dman' Dan Morrison http://coders.co.nz/ * @author 'dman' Dan Morrison http://coders.co.nz/
@ -37,7 +36,8 @@ $presets['rounded_bl'] = array (
'name' => 'coloractions_convert', 'name' => 'coloractions_convert',
'data' =>array ( 'data' =>array (
'format' => 'image/png', 'format' => 'image/png',
'quality' => '95',
), ),
), ),
), ),
); );

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -0,0 +1,66 @@
<?php
/**
* @file Functions to create .htaccess files in public://styles/{image-style}
* directories that force the correct Content-Type header.
*/
/**
* Checks for all image style if a .htaccess should be created to force a
* correct Content-Type header.
*/
function imagecache_coloractions_create_htaccess_all_styles() {
$styles = image_styles();
foreach ($styles as $style) {
imagecache_coloractions_create_htaccess_for_style($style);
}
}
/**
* Checks if an image style has a convert format image effect and if so, creates
* an .htaccess in the folder where the derivatives for this style are stored
* to force the correct Content-Type header.
*
* @param array $style
*
* @return bool|null
* True if .htaccess created successfully, false om error, null if no
* .htaccess needed to be created.
*/
function imagecache_coloractions_create_htaccess_for_style(array $style) {
// If we have multiple convert effects in the same style (I found one during
// testing) the last determines the mime type.
$format = NULL;
foreach ($style['effects'] as $effect) {
if ($effect['name'] === 'coloractions_convert') {
if (!empty($effect['data']['format'])) {
$format = $effect['data']['format'];
}
}
}
return $format ? imagecache_coloractions_create_htaccess($style['name'], $format) : NULL;
}
/**
* Creates an .htaccess file in the folder public://styles/%style_name.
*
* The folder itself will also be created if necessary.
*
* @param string $style_name
* @param string $mimeType
*
* @return bool
* True on success, false otherwise.
*/
function imagecache_coloractions_create_htaccess($style_name, $mimeType) {
$forceType = "ForceType $mimeType\n";
$directory = "public://styles/$style_name";
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
watchdog('imagecache_actions', 'Failed to create style directory: %directory', array('%directory' => $directory), WATCHDOG_ERROR);
return FALSE;
}
if (!file_put_contents("$directory/.htaccess", $forceType)) {
watchdog('imagecache_actions', 'Failed to create .htaccess in style directory: %directory', array('%directory' => $directory), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
}

View File

@ -1,19 +1,18 @@
name = Imagecache Color Actions name = Imagecache Color Actions
description = Additional ImageCache actions, providing color-shifting, brightness and alpha transparency effects. description = Provides image effects color-shifting, invert colors, brightness, posterize and alpha transparency effects. Also provides a change image format effect.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = imagecache_actions dependencies[] = imagecache_actions
dependencies[] = image dependencies[] = image
files[] = imagecache_coloractions.install
files[] = imagecache_coloractions.module files[] = imagecache_coloractions.module
files[] = transparency.inc files[] = transparency.inc
files[] = tests/green.imagecache_preset.inc files[] = tests/green.imagecache_preset.inc
; Information added by drupal.org packaging script on 2012-12-04 ; Information added by Drupal.org packaging script on 2018-03-20
version = "7.x-1.1" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,27 +1,12 @@
<?php <?php
/** /**
* @file Set up new color actions. Tell imagecache.module about them * Create an .htaccess file in image style directories that have a change format
* image effect to enforce the correct Content-Type header for the derivative
* images.
*/ */
function imagecache_coloractions_update_7101(&$sandbox) {
/** module_load_include('module', 'image');
* Need to flush the cache when this module is enabled or disabled include_once dirname(__FILE__) . '/imagecache_coloractions.htaccess_creator.inc';
*/ imagecache_coloractions_create_htaccess_all_styles();
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

@ -1,26 +1,26 @@
<?php <?php
/** /**
* @file Helper functions for the alpha action for imagecache * @file Helper functions for the alpha effect.
* *
* @author dan http://coders.co.nz * @author dan http://coders.co.nz
*/ */
/** /**
* Implementation of imagecache_hook_form() * Image effect form callback for the alpha effect.
* *
* Settings for alpha actions. * @param array $data
* The current configuration for this image effect.
* *
* @param $action array of settings for this action * @return array
* @return a form definition * The form definition for this effect.
*/ */
function imagecache_alpha_form($action) { function coloractions_alpha_form(array $data) {
$defaults = array( $defaults = array(
'flatten' => FALSE, 'flatten' => FALSE,
'RGB' => array('HEX' => '#000000'), 'RGB' => array('HEX' => '#000000'),
'opacity' => 0.5, 'opacity' => 0.5,
); );
$action = array_merge($defaults, (array) $action); $data = array_merge($defaults, (array) $data);
$form = array(); $form = array();
$form['help'] = array( $form['help'] = array(
@ -36,7 +36,7 @@ function imagecache_alpha_form($action) {
$form['opacity'] = array( $form['opacity'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Opacity'), '#title' => t('Opacity'),
'#default_value' => $action['opacity'], '#default_value' => $data['opacity'],
'#size' => 3, '#size' => 3,
'#description' => t(" '#description' => t("
A decimal between 0 and 1. A decimal between 0 and 1.
@ -48,8 +48,9 @@ function imagecache_alpha_form($action) {
"), "),
); );
$form['description'] = array('#value' => t( $form['description'] = array(
"<p>Alpha toning is an advanced method of greyscaling or colorizing. '#value' => t(
"<p>Alpha toning is an advanced method of greyscaling or colorizing.
It works using transparency, not colour matching. It works using transparency, not colour matching.
The results of this filter are excellent for using as watermarks, The results of this filter are excellent for using as watermarks,
and for 'sepia' type imprints on coloured or textured backgrounds. and for 'sepia' type imprints on coloured or textured backgrounds.
@ -58,9 +59,10 @@ function imagecache_alpha_form($action) {
<em>unless</em> you either <strong>flatten</strong> this image against a background color <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 or image in a later process or <strong>convert</strong> it to a PNG before saving
using available imagecache actions.</p>" using available imagecache actions.</p>"
)); )
);
$form['RGB'] = imagecache_rgb_form($action['RGB']); $form['RGB'] = imagecache_rgb_form($data['RGB']);
$form['RGB']['#type'] = 'fieldset'; $form['RGB']['#type'] = 'fieldset';
$form['RGB']['#title'] = t('Fill Color'); $form['RGB']['#title'] = t('Fill Color');
$form['RGB']['HEX']['#description'] = t(" $form['RGB']['HEX']['#description'] = t("
@ -70,114 +72,121 @@ function imagecache_alpha_form($action) {
Set it to nothing to not perform any color shift. Set it to nothing to not perform any color shift.
"); ");
$form['flatten'] = array( $form['flatten'] = array(
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => t('Flatten Transparency'), '#title' => t('Flatten Transparency'),
'#default_value' => $action['flatten'], '#default_value' => $data['flatten'],
'#return_value' => TRUE, '#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."), '#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; return $form;
} }
/** /**
* Implementation of theme_hook() for imagecache_ui.module * Implements theme_hook() for the alpha effect summary.
*
* @param array $variables
* An associative array containing:
* - data: The current configuration for this image effect.
*
* @return string
* The HTML for the summary of this image effect.
*
* @ingroup themeable
*/ */
function theme_imagecache_alpha($variables) { function theme_coloractions_alpha_summary(array $variables) {
$element = $variables['element']; $data = $variables['data'];
return ($element['#value']['flatten'] ? t("Flatten") : t("Transparent")) return ($data['flatten'] ? t("Flatten") : t("Transparent"))
. ($element['#value']['opacity'] ? " : " . ($element['#value']['opacity'] * 100) . '%' : '') . ($data['opacity'] ? " : " . ($data['opacity'] * 100) . '%' : '')
. " : " . theme_imagecacheactions_rgb($element['#value']['RGB']); . " : " . theme_imagecacheactions_rgb($data['RGB']);
} }
/** /**
* Given an image, manipulate the transparancy behaviour. * Image effect callback for the alpha effect.
*
* implementation of hook_image()
* *
* Either convert light parts of an image to see-through, or place a solid * Either convert light parts of an image to see-through, or place a solid
* colour behind areas that would otherwise be see-though * 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.
* *
* To save a partially transparent image, the image resource must be switched to PNG. * @param stdClass $image
* REMEMBER TO SWITCH IT BACK if needed * @param array $data
* *
* @param $image handle on the image definition, including a gd image resource * @return bool
* to act upon * true on success, false otherwise.
* @param $data settings for this process.
* @return bool success
*/ */
function imagecache_alpha_image($image, $data = array()) { function coloractions_alpha_effect(stdClass $image, array $data) {
if (! $data['flatten']) { // @todo: Extract to GD specific function.
// given an image, convert dark areas to opaque, if (!$data['flatten']) {
// light to transparent, // Given an image, convert dark areas to opaque, light to transparent.
return png_color2alpha($image, $data['RGB']['HEX'], $data['opacity']); return png_color2alpha($image, $data['RGB']['HEX'], $data['opacity']);
} }
else { else {
// Do the opposite, flatten the transparency ONTO the given colour // Do the opposite, flatten the transparency ONTO the given color.
$info = $image->info; $info = $image->info;
if (!$info) { if (!$info) {
watchdog("imagecache", "Problem converting image to fill behind. Source image returned no info"); watchdog('imagecache_actions', "Problem converting image to fill behind. Source image returned no info");
#dsm($source); return FALSE;
return; // error
} }
$base_image = imagecreatetruecolor($info['width'], $info['height']); $base_image = imagecreatetruecolor($info['width'], $info['height']);
imagesavealpha($base_image, TRUE); imagesavealpha($base_image, TRUE);
imagealphablending($base_image, FALSE); imagealphablending($base_image, FALSE);
// Start with a solid colour // Start with a solid color.
$background_rgb = imagecache_actions_hex2rgba($data['RGB']['HEX']); $background_rgb = imagecache_actions_hex2rgba($data['RGB']['HEX']);
// Setting the background colour here solid is what flattens the image // Setting the background color 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 colored rgb AND 100% transparent,
// in the hope that a failure to render transparency would instead render
// 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. // THAT colour.
$background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0); $background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0);
// But that still doesn't work. // But that still doesn't work. Yet somehow I've seen transparent images
// Yet somehow I've seen transparent images that fallback to white, not silver. // that fallback to white, not silver.
imagefill( $base_image, 0, 0, $background_color ); imagefill($base_image, 0, 0, $background_color);
// And set the overlay behaviour back again // And set the overlay behavior back again.
imagealphablending($base_image, TRUE); imagealphablending($base_image, TRUE);
// Place the current image over it // Place the current image over it.
$foreground = $image->resource; if ($result = imagecopy($base_image, $image->resource, 0, 0, 0, 0, $info['width'], $info['height'])) {
$success = imagecopy($base_image, $image->resource, 0, 0, 0, 0, $info['width'], $info['height']); imagedestroy($image->resource);
$image->resource = $base_image;
}
$image->resource = $base_image; return $result;
return TRUE;
} }
} }
/** /**
* This achives a tonal effect by converting the images combined tone and * This achieves a tonal effect by converting the images combined tone and
* existing transparency into one shade value. This is then used as the ALPHA * 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 * transparency for that pixel, while the whole thing is coloured the same
* shade. Images 'greytoned' in this manner should sit smoothly on any * shade. Images 'grey toned' in this manner should sit smoothly on any
* background. * background.
* *
* With no color set, use the existing hue. * With no color set, use the existing hue.
* *
* To save a partially transparent image, the image resource must be switched to PNG. * To save a partially transparent image, the image resource must be switched to
* ... or maybe not. Just flatten it yourself, or switch the format yourself. * PNG. ... or maybe not. Just flatten it yourself, or switch the format
* This hack would produce side effects otherwise. * yourself. This hack would produce side effects otherwise.
* *
* This algorithm runs maths per-pixel, and therefore is incredibly much more * This algorithm runs maths per-pixel, and therefore is incredibly much more
* inefficient than any native routine. Will kill the server on large images. * inefficient than any native routine. Will kill the server on large images.
* *
* @param $opacity between 0 transparent and 1 solid. * @param stdClass $image
* @param string $color
* @param float $opacity
* between 0 transparent and 1 solid.
*
* @return bool
* true on success, false otherwise.
*/ */
function png_color2alpha($image, $color, $opacity = NULL) { function png_color2alpha(stdClass $image, $color, $opacity = NULL) {
$info = $image->info; $info = $image->info;
if (!$info) { if (!$info) {
return FALSE; return FALSE;
@ -195,11 +204,11 @@ function png_color2alpha($image, $color, $opacity = NULL) {
if (($width * $height) > (1200 * 1200)) { if (($width * $height) > (1200 * 1200)) {
watchdog('imagecache_actions', __FUNCTION__ . " on {$image->source}. Image is TOO BIG to run the per-pixel algorithm. Aborting."); watchdog('imagecache_actions', __FUNCTION__ . " on {$image->source}. Image is TOO BIG to run the per-pixel algorithm. Aborting.");
return TRUE; return FALSE;
} }
for ($i = 0; $i < $height; $i++) { for ($i = 0; $i < $height; $i++) {
//this loop traverses each row in the image //this loop traverses each row in the image
for ($j = 0; $j < $width; $j++) { for ($j = 0; $j < $width; $j++) {
//this loop traverses each pixel of each row //this loop traverses each pixel of each row
// Get the color & alpha info of the current pixel. // Get the color & alpha info of the current pixel.
$retrieved_color = imagecolorat($im1, $j, $i); // an index $retrieved_color = imagecolorat($im1, $j, $i); // an index
@ -207,33 +216,33 @@ function png_color2alpha($image, $color, $opacity = NULL) {
$alpha = 127; $alpha = 127;
// Calculate the total shade value of this pixel. // Calculate the total shade value of this pixel.
// If the rule sets a color, then the darkness of the existing // If the rule sets a color, then the darkness of the existing
// pixel will define the desired alpha value. // pixel will define the desired alpha value.
if ($color) { if ($color) {
$lightness = ( $rgba_array['red'] + $rgba_array['green'] + $rgba_array['blue'] ) / 3; $lightness = ($rgba_array['red'] + $rgba_array['green'] + $rgba_array['blue']) / 3;
// Need to flip the numbers around before doing maths. // Need to flip the numbers around before doing maths.
#$opacity = 1-($rgba_array['alpha']/127); //$opacity = 1-($rgba_array['alpha']/127);
#$darkness = 1-($lightness/256); // 0 is white, 1 is black //$darkness = 1-($lightness/256); // 0 is white, 1 is black
#$visibility = $darkness * $opacity; //$visibility = $darkness * $opacity;
#$alpha = (1-$visibility) * 127; //$alpha = (1-$visibility) * 127;
$alpha = (1 - ((1 -($lightness / 256)) * (1 -($rgba_array['alpha'] / 127)))) * 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 // If color is NOT set, then the existing color is passed though, only
// made somewhat transparent. // made somewhat transparent.
if (!$color) { if (!$color) {
$background = $rgba_array; $background = $rgba_array;
} }
if ($opacity) { if ($opacity) {
// It's a user-defined alpha value // It's a user-defined alpha value.
$alpha = $alpha * $opacity; $alpha = $alpha * $opacity;
} }
// Paint the pixel. // Paint the pixel.
/** @noinspection PhpUndefinedVariableInspection */
$color_to_paint = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $alpha); $color_to_paint = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $alpha);
imagesetpixel($image->resource, $j, $i, $color_to_paint); imagesetpixel($image->resource, $j, $i, $color_to_paint);
} }
} }
return TRUE; return TRUE;
} }

View File

@ -21,9 +21,8 @@ Personally, I prefer the imagemagick toolkit:
you will see what I mean. you will see what I mean.
- It does not execute in the PHP memory space, so is not restricted by the - It does not execute in the PHP memory space, so is not restricted by the
memory_limit PHP setting. memory_limit PHP setting.
- The GD toolkit will, at least on my Windows configuration, keep the font file - The GD toolkit will, at least on my Windows configuration, keep font files
open after a text operation, so you cannot delete, move or rename it anymore. 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 Installing
@ -59,10 +58,11 @@ record so by changing $image->info['width'] and/or $image->info['height'].
General General
------- -------
To ease your task, this effect makes some information regarding the image being To ease your task, this effect makes some information regarding the image being
processed available in 2 variables: $image and $image_context. These variables processed available in a number of variables: $image, $image_context,
are readily available in your snippet. $image_style, and $image_effect_id. These variables are readily available in
your snippet.
$image is an associative array containing: $image is an object containing the following properties:
- source: string, the source of the image, e.g. public://photo.jpg - source: string, the source of the image, e.g. public://photo.jpg
- info: array, example data: - info: array, example data:
- width (int) 180 - width (int) 180
@ -113,6 +113,18 @@ $image_context is an associative array containing:
- title (string) ... - title (string) ...
- ... - ...
$image_style is an associative array containing the current image style being
processed. It ocntians a.o.:
- isid: the unique image style id
- name: machine name.
- label: Human readable name.
- effects: An array with the effects of this image style, ordered in the way
they should be applied.
$image_effect_id is an int containng the unique id of the current image effect
being applied. This can be used to look the current image effect up in the
$image_style array.
Of course there are many other possible useful globals. Think of: Of course there are many other possible useful globals. Think of:
- base_url - base_url
- base_path - base_path
@ -144,3 +156,41 @@ foreach ($referring_entities as $field_name => $field_referring_entities) {
} }
} }
?> ?>
"Dynamic" parameters
--------------------
Thee are many requests for adding token support or allowing for dynamic
parameters in another way. However, the current image style processing does not
easily allow for this. But for these cases we have the custom action to our
rescue. It is quite easy to:
- Create you own array of parameters.
- Call the effect callback yourself
Exanple, calling the watermark/canvas overlay effect:
<?php
$data = array(
'xpos' => 'center',
'ypos' => 'center',
'alpha' => '100',
'scale' => '',
'path' => 'module://imagecache_actions/tests/black-ribbon.gif',
);
return canvasactions_file2canvas_effect($image, $data);
?>
Or, to be on the safe side with effect info altering:
<?php
$definition = image_effect_definition_load('canvasactions_file2canvas');
$callback = $definition['effect callback'];
if (function_exists($callback)) {
$data = array(
'xpos' => 'center',
'ypos' => 'center',
'alpha' => '100',
'scale' => '',
'path' => 'module://imagecache_actions/tests/black-ribbon.gif',
);
return $callback($image, $data);
}
return FALSE;
?>

View File

@ -1,17 +1,14 @@
name = Imagecache Custom Actions name = Imagecache Custom Actions
description = Allow direct PHP code manipulation of imagecache images. description = Provides the custom and subroutine image effects.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = imagecache_actions dependencies[] = imagecache_actions
dependencies[] = image dependencies[] = image
files[] = imagecache_customactions.install ; Information added by Drupal.org packaging script on 2018-03-20
files[] = imagecache_customactions.module version = "7.x-1.9"
; Information added by drupal.org packaging script on 2012-12-04
version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,8 +1,8 @@
<?php <?php
/** /**
* Rename 'text' to 'php' in custom action effect data * Rename 'text' to 'php' in custom action effect data.
*/ */
function imagecache_customactions_update_7100(&$sandbox) { function imagecache_customactions_update_7100(/*&$sandbox*/) {
$effects = db_select('image_effects') $effects = db_select('image_effects')
->fields('image_effects') ->fields('image_effects')
->condition('name', 'imagecache_customactions', '=') ->condition('name', 'imagecache_customactions', '=')

View File

@ -1,57 +1,82 @@
<?php <?php
/** /**
* @file Allow advanced users to code their own PHP image manipulation routines * @file Allows advanced users to code their own PHP image manipulation routines
* as part of imagecache processing. * as part of image style processing.
* *
* @author Originally contributed by crea http://drupal.org/node/325103#comment- * @author Originally contributed by crea https://drupal.org/node/325103#comment-
* 1076011 * 1076011
* *
* @author merged into imagecache_actions by dman http://coders.co.nz * @author merged into imagecache_actions by dman http://coders.co.nz
* *
* Needs review - currently a security risk etc * custom action effect:
* @todo: add description field that editors can use to define their own summary?
* @todo: add form field asking if dimensions stay the same (or if the new dimensions are known).
* subroutine effect:
* @todo: use isid to allow for image style renaming, but also use name to allow export and import (features)
*/ */
/** /**
* Implements hook_image_effect_info. * Implements hook_image_effect_info().
* *
* @return array * Defines information about the supported effects.
*/ */
function imagecache_customactions_image_effect_info() { function imagecache_customactions_image_effect_info() {
$effects = array(); $effects = array();
// @todo: implement summary theme callback
$effects['imagecache_customactions'] = array( $effects['imagecache_customactions'] = array(
'label' => t('Custom action'), 'label' => t('Custom action'),
'help' => t('Runs custom PHP code.'), 'help' => t('Runs custom PHP code.'),
'effect callback' => 'imagecache_customactions_image', 'effect callback' => 'imagecache_customactions_effect',
'dimensions callback' => 'imagecache_customactions_dimensions', 'dimensions callback' => 'imagecache_customactions_dimensions',
'form callback' => 'imagecache_customactions_form', 'form callback' => 'imagecache_customactions_form',
'summary theme' => 'imagecache_customactions_summary',
); );
$effects['imagecache_subroutine'] = array( $effects['imagecache_subroutine'] = array(
'label' => t('Subroutine'), 'label' => t('Subroutine'),
'help' => t('Runs another defined preset on the image.'), 'help' => t('Runs another defined preset on the image.'),
'effect callback' => 'imagecache_subroutine_image', 'effect callback' => 'imagecache_subroutine_effect',
'dimensions callback' => 'imagecache_subroutine_dimensions', 'dimensions callback' => 'imagecache_subroutine_dimensions',
'form callback' => 'imagecache_subroutine_form', 'form callback' => 'imagecache_subroutine_form',
'summary theme' => 'imagecache_subroutine_summary',
); );
return $effects; return $effects;
} }
/** /**
* Implements hook_image_style_flush. * Implements hook_theme().
* *
* This hook checks if the image style that is being flushed is used in an * Registers theme functions for the effect summaries.
* subroutine effect. If so, the style that contains the subroutine effect, */
* should be flushed as well as the flushed style was probably changed. function imagecache_customactions_theme() {
return array(
'imagecache_customactions_summary' => array(
'variables' => array('data' => NULL),
),
'imagecache_subroutine_summary' => array(
'variables' => array('data' => NULL),
),
);
}
/**
* Implements hook_image_style_flush().
*
* This hook checks if the image style that is being flushed is used in a
* subroutine effect. If so, the style that contains the subroutine effect
* should be flushed as well.
*
* This may lead to recursive calls to image_style_flush() and thus to this
* hook. Without loops in styles that call each other as subroutine, this
* recursion will always end.
* *
* @param array $flushed_style * @param array $flushed_style
* The image style that is being flushed. * The image style that is being flushed.
*/ */
function imagecache_customactions_image_style_flush($flushed_style) { function imagecache_customactions_image_style_flush(/*array*/ $flushed_style) {
$styles = image_styles(); $styles = image_styles();
foreach ($styles as $style) { foreach ($styles as $style) {
if ($style['name'] !== $flushed_style['name']) { if ($style['name'] !== $flushed_style['name']) {
@ -67,44 +92,34 @@ function imagecache_customactions_image_style_flush($flushed_style) {
} }
/** /**
* @deprecated replaced by summary theme callback * Image effect form callback for the custom action effect.
* Implementation of theme_hook() for imagecache_customactions.module *
*/ * Note that this is not a complete form, it only contains the portion of the
function imagecache_customactions_theme() { * form for configuring the effect options. Therefore it does not not need to
return array( * include metadata about the effect, nor a submit button.
'imagecache_subroutine' => array(
'render element' => 'element',
),
);
}
/**
* Implements hook_form().
* *
* @param array $data * @param array $data
* Array of settings for this action. * The current configuration for this image effect.
*
* @return array * @return array
* A form definition. * The form definition for this effect.
*/ */
function imagecache_customactions_form($data) { function imagecache_customactions_form(array $data) {
// Add defaults. // Add defaults.
$data += array('php' => 'return TRUE;'); $data += array('php' => 'return TRUE;');
// Note: we also need to check for the existence of the module: admin has // 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 // all rights, so user_acccess(...) returns TRUE even if the module is not
// enabled and the permission does not exist. // enabled and the permission does not exist.
$allow_dynamic = user_access('use PHP for settings') && module_exists('php'); $allow_php = module_exists('php') && user_access('use PHP for settings');
// @todo: for imagemagick, the PHP code should add a set of commands to the
// ops aray of $image. Document this in description.
$form = array( $form = array(
'php' => array( 'php' => array(
'#type' => 'textarea', '#type' => 'textarea',
'#rows' => 10, '#rows' => 12,
'#title' => t('PHP code'), '#title' => t('PHP code'),
'#default_value' => $data['php'], '#default_value' => $data['php'],
'#disabled' => !$allow_dynamic, '#disabled' => !$allow_php,
'#description' => t("<p>A piece of PHP code that modifies the image. '#description' => t("<p>A piece of PHP code that modifies the image.
It should return a boolean indicating success or failure. It should return a boolean indicating success or failure.
You will need the '%use_php' permission, defined by the 'PHP filter' module. You will need the '%use_php' permission, defined by the 'PHP filter' module.
@ -117,15 +132,30 @@ See the help for an extensive explanation of the possibilities.</p>",
} }
/** /**
* Implements hook_image(). * Implements theme_hook() for the custom action effect summary.
* *
* @param object $image * param array $variables
* An associative array containing:
* - data: The current configuration for this image effect.
*
* @return string
* The HTML for the summary of this image effect.
* @ingroup themeable
*/
function theme_imagecache_customactions_summary(/*array $variables*/) {
return 'Custom PHP code';
}
/**
* Image effect callback for the custom action effect.
*
* @param stdClass $image
* @param array $data * @param array $data
* *
* @return bool * @return boolean
* true on success, false otherwise.
*/ */
function imagecache_customactions_image($image, $data) { function imagecache_customactions_effect(stdClass $image, array $data) {
// Check that the PHP filter module is enabled.
$result = module_exists('php'); $result = module_exists('php');
if ($result) { if ($result) {
// Get context about the image. // Get context about the image.
@ -133,10 +163,17 @@ function imagecache_customactions_image($image, $data) {
$GLOBALS['image_context'] = imagecache_actions_get_image_context($image, $data); $GLOBALS['image_context'] = imagecache_actions_get_image_context($image, $data);
$GLOBALS['image'] = $image; $GLOBALS['image'] = $image;
// Get (non-alterable) context about the image style and image effect.
$execution_info = imagecache_actions_get_image_effect_context();
$GLOBALS['image_style'] = $execution_info['image_style'];
$GLOBALS['image_effect_id'] = $execution_info['image_effect_id'];
$result = php_eval('<' . '?php global $image, $image_context; ' . $data['php'] . ' ?' . '>'); $result = php_eval('<' . '?php global $image, $image_context; ' . $data['php'] . ' ?' . '>');
// php_eval returns '1' if the snippet returns true. // php_eval returns '1' if the snippet returns true.
$result = $result === '1'; $result = $result === '1';
unset($GLOBALS['image_effect_id']);
unset($GLOBALS['image_style']);
unset($GLOBALS['image']); unset($GLOBALS['image']);
unset($GLOBALS['image_context']); unset($GLOBALS['image_context']);
} }
@ -150,94 +187,102 @@ function imagecache_customactions_image($image, $data) {
} }
/** /**
* Image dimensions callback; Custom action. * Image dimensions callback for the custom action effect.
* *
* @param array $dimensions * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in * Dimensions to be modified - an associative array containing the items
* pixels. * 'width' and 'height' (in pixels).
* @param array $data * param array $data
* An array with the effect options. * An associative array containing the effect data.
*/ */
function imagecache_customactions_dimensions(array &$dimensions, array $data) { 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['width'] = NULL;
$dimensions['height'] = NULL; $dimensions['height'] = NULL;
} }
/**
* @todo change into summary theme callback
* 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. * Subroutine - an imagecache action that just calls another one.
* *
* Contributed by Alan D * Contributed by Alan D
* http://drupal.org/node/618784 * https://drupal.org/node/618784
* *
* Reworked into customactions by dman 2010-07 * Reworked into customactions by dman 2010-07
*/ */
/** /**
* Config form for this preset. * Image effect form callback for the subroutine effect.
*
* Implementation of imagecache_hook_form()
* *
* @param array $data * @param array $data
* The effect data for this effect. * The current configuration for this image effect.
*
* @return array * @return array
* The form definition. * The form definition for this effect.
*/ */
function imagecache_subroutine_form($data) { function imagecache_subroutine_form(array $data) {
$data = (array) $data; // Add defaults.
$form = array(); $data += array('subroutine_presetname' => '');
// List available presets // List available image styles.
// @todo: use image_style_options and remove current style? // The PASS_THROUGH parameter is new as of D7.23, and is added here to prevent
$presets = array(); // image_style_options() from double-encoding the human-readable image style
foreach (image_styles(TRUE) as $preset) { // name, since the form API will already sanitize options in a select list.
$presets[$preset['name']] = $preset['name']; $styles = image_style_options(TRUE, PASS_THROUGH);
} //@todo: unset the current style to prevent obvious recursion.
$form = array();
$form['subroutine_presetname'] = array( $form['subroutine_presetname'] = array(
'#type' => 'select', '#type' => 'select',
'#title' => t('Preset to call'), '#title' => t('Image style to call'),
'#default_value' => $data['subroutine_presetname'], '#default_value' => $data['subroutine_presetname'],
'#options' => $presets, '#options' => $styles,
'#required' => TRUE,
); );
return $form; return $form;
} }
/** /**
* Actually invoke the action - which means just handing off to the named real * Implements theme_hook() for the subroutine effect summary.
* preset to do the job.
* *
* Implementation of hook_image() * @param array $variables
* An associative array containing:
* - data: The current configuration for this image effect.
* *
* @param object $image * @return string
* @param array $data * The HTML for the summary of this image effect.
* * @ingroup themeable
* @return bool
*/ */
function imagecache_subroutine_image($image, $data) { function theme_imagecache_subroutine_summary(array $variables) {
if ($preset = image_style_load($data['subroutine_presetname'])) { $data = $variables['data'];
foreach ($preset['effects'] as $effect) {
image_effect_apply($image, $effect); module_load_include('inc', 'imagecache_actions', 'utility');
} $label = imagecache_actions_get_style_label($data['subroutine_presetname']);
} return t('Apply image style %label', array('%label' => $label));
return TRUE;
} }
/** /**
* Image dimensions callback; Subroutine. * Image effect callback for the subroutine effect.
*
* @param stdClass $image
* @param array $data
*
* @return boolean
* true on success, false otherwise.
*/
function imagecache_subroutine_effect(stdClass $image, array $data) {
$result = FALSE;
if ($style = image_style_load($data['subroutine_presetname'])) {
$result = TRUE;
foreach ($style['effects'] as $effect) {
$result = $result && image_effect_apply($image, $effect);
}
}
return $result;
}
/**
* Image dimensions callback for the subroutine effect.
* *
* @param array $dimensions * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in * Dimensions to be modified - an array with components width and height, in
@ -246,25 +291,6 @@ function imagecache_subroutine_image($image, $data) {
* An array with the effect options. * An array with the effect options.
*/ */
function imagecache_subroutine_dimensions(array &$dimensions, array $data) { function imagecache_subroutine_dimensions(array &$dimensions, array $data) {
// @todo: dimensions // Let the subroutine transform the dimensions.
// @todo: call dimensions callback on subroutine style. image_style_transform_dimensions($data['subroutine_presetname'], $dimensions);
$dimensions['width'] = NULL;
$dimensions['height'] = NULL;
}
/**
* @todo change into summary theme callback
* This lets the user see what parameters were selected for the action
*/
function theme_imagecache_subroutine($variables) {
// @todo: better decsription, do not use internal id's, imagecache_preset_by_name does not exist: fatal error?
$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>');
} }

View File

@ -0,0 +1,36 @@
Komika font by APOSTROPHIC LABS:
http://moorstation.org/typoasis/designers/lab/index.htm.
Below the license as was packaged with the font download:
Here is the "Read Me" from the original http://www.apostrophiclab.com website:
Archived here: http://web.archive.org/web/20030408055445/www.hardcovermedia.com/lab/Pages/info.html
and here: http://apostrophiclab.pedroreina.net/info.html
-----------
Apostrophic Laboratories does not collect or relay any personal information about its visitors.
Apostrophic Laboratories is not affiliated with the company that provides its web hosting service.
Apostrophic Laboratories is not affiliated with any third party advertising service. This lab will always be advertisement-free to maintain the spirit and integrity of freedom of information that the world wide web was intended to represent.
The fonts on this site are freeware and can be used as they are in any context without permission from Apostrophic Laboratories, except to produce material that is racist, criminal and/or illegal in nature. It is prohibited to modify any Apostrophic Laboratories font(s) for repackaging and/or re-release without an express written authorization by the designer(s) of the font(s) or Apostrophic Laboratories. Under no circumstance shall any Apostrophic Laboratories design or font design be sold or purchased. Email info@apostrophiclab.com if you want more information.
The Apostrophic Laboratories site and its contents are the property of Apostrophic Laboratories and the contents' creators. It is prohibited to use the graphic designs shown on this site or any of the site's elements without obtaining written authorization from the designer(s) and/or developer(s) of the content in question. Email info@apostrophiclab.com if you want more information.
Read everything written by Kurt Vonnegut.
Hear everything written by Mozart.
Live every day like it is your first.
Smell the flowers.
Touch the faces of babies.
Run in the rain.
Eat pop corn.
The following constitutes the different methods of contacting Apostrophic Laboratories.
Web: http://www.apostrophiclab.com
Email: info@apostrophiclab.com
For individual designer email addresses, click here.
Address: Apostrophic Laboratories, 343 Kingswood Road, Toronto, Ontario, Canada M4E 3N8

View File

@ -0,0 +1,24 @@
Blokletters fonts by Lefly Fonts: http://lefly.vepar.nl/ under the following
license:
You are free:
to Share — to copy, distribute and transmit the work
to Remix — to adapt the work
Under the following conditions:
Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.
With the understanding that:
Waiver — Any of the above conditions can be waived if you get permission from the copyright holder.
Other Rights — In no way are any of the following rights affected by the license:
- Your fair dealing or fair use rights;
- The author's moral rights;
- Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights.
Notice — For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.

View File

@ -0,0 +1,93 @@
Copyright (c) 2011, Vernon Adams (vern@newtypography.co.uk),
with Reserved Font Name Pacifico.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -2,7 +2,7 @@ README
------ ------
README for the Image effect text module. README for the Image effect text module.
Author Erwin Derksen (fietserwin: http://drupal.org/user/750928) Author Erwin Derksen (fietserwin: https://drupal.org/user/750928)
Dependencies Dependencies
@ -12,10 +12,10 @@ Hard dependencies:
- Image (Drupal core). - Image (Drupal core).
Soft dependencies/recommended modules: Soft dependencies/recommended modules:
- Imagemagick (preferred toolkit, http://drupal.org/project/imagemagick). - Imagemagick (preferred toolkit, https://drupal.org/project/imagemagick).
- PHP filter (Drupal core, if yuo want to use PHP to create the text to render). - PHP filter (Drupal core, if yuo want to use PHP to create the text to render).
- System stream wrapper (http://drupal.org/project/system_stream_wrapper) - System stream wrapper (https://drupal.org/project/system_stream_wrapper)
- Remote stream wrapper (http://drupal.org/project/remote_stream_wrapper) - Remote stream wrapper (https://drupal.org/project/remote_stream_wrapper)
The latter 2 provide additional stream wrappers. Especially the system stream The latter 2 provide additional stream wrappers. Especially the system stream
wrapper is very handy as it provides, among others, a module:// and theme:// wrapper is very handy as it provides, among others, a module:// and theme://
wrapper. wrapper.
@ -45,9 +45,12 @@ More information about the effect data options
Font Font
---- ----
You have to supply the font file to use. The font type supported depend on the This module comes with some free fonts so you can easily test this effect.
toolkit in use, but at least ttf files will always work. This option accepts Please read their respective licences.
either:
For real use, you normally want to use your own font as dictated by the website
design. The font types 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: - 1 of the (enabled) scheme's:
* public:// * public://
* private:// Preferred for site specific masks, overlays, etc, that do not * private:// Preferred for site specific masks, overlays, etc, that do not
@ -59,13 +62,17 @@ either:
* theme:// idem. * theme:// idem.
* profile:// idem. * profile:// idem.
* library:// idem. * library:// idem.
- A relative (to the current directory, probably Drupal root) or absolute path. - A relative path (relative to the current directory, probably Drupal root).
- An absolute path.
- A system or toolkit font specification. E.g. on my Windows system 'arial.ttf'
worked with both GD and Imagemagick. A warning will be issued but that may be
ignored when it works as expected.
Text position Text position
------------- -------------
The text position defines the point in the image where you want to place (align) 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 your text. It starts at the top left corner of the image with position 0,0 and
the positive directions are to the right and down. the positive directions are to the right and down.
The definition of the vertical position differs per toolkit. For GD it is the The definition of the vertical position differs per toolkit. For GD it is the
@ -89,49 +96,65 @@ for this yourself.
Rotation Rotation
-------- --------
The text can be rotated before being overlaid on the image. The vlaue is in The text can be rotated before being overlaid on the image. The value is in
degrees, so 90 degrees is straight down. Positive values are rotated clockwise, degrees. Positive values are rotated clockwise, So 90 degrees is straight down.
negative values counter clockwise. negative values counter clockwise.
In Imagemagick the text is rotated around the text position. Thus centered text 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 is rotated around its own center. GD, on the other hand, always rotates around
the left bottom (baseline) position, regardless the text alignment. Using the left bottom (baseline) position, regardless the text alignment. So using
rotation with a non default alignment (left bottom) will give surprising rotation with a non default alignment (left bottom) will give surprising
results. results.
Text source 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: 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 - Text (with token replacement): the text to place on the image has to be
effect data. Use this e.g. for a fixed copyright notice. entered on the image effect form. Use this e.g. for a copyright notice.
notes:
* Token replacement: you can use all global tokens, the file tokens, and
tokens from entities referring to the image via an image field. Example: if
you know that the image style is only used for article nodes, you can use
[node:field-image:alt] to get the alt text of the image. Note: this specific
example requires the entity_token module.
* New lines: you can add a new line by adding \n to your text. To get a
literal \n, use \\n.
- PHP: the text to place on the image comes from a piece of PHP code that should - PHP: the text to place on the image comes from a piece of PHP code that should
return the text to place on the image. Only users with the 'use PHP for 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 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 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. 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 To add new lines to your text add them literally to the string you return,
for dynamic texts are directly available without any coding, namely the alt normally by using "\n" in your PHP code.
and title properties of the image field linked to the image at hand. Note that - Image Alt or Title: to alleviate the need to enable the PHP filter module, 2
multiple image fields, possibly in different languages, may be referring to commonly used sources for dynamic texts are directly available without any
the image that is being processed. This module will take the first image field coding: the alt and title properties of an image field linked to the image at
it finds to extract the alt and title. If the field in itself is multi hand.
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. Notes:
- When using token replacement or the image alt or title, 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 image derivative first.
- This module will not automatically break text based on available space.
- Due to the way that GD text box positioning works it is quite difficult to
correctly position multiple lines of text with GD. If you have a working
solution please post a patch. (Probably involves exploding the text in
separate lines and then positioning each line separately.)
PHP snippets to determine the text PHP snippets to determine the text
---------------------------------- ----------------------------------
Given the correct permission, you can write your own PHP snippet to compute the 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 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. the image being processed available in a number of variables: $image,
These variables are readily available in your snippet. $image_context, $image_style, and $image_effect_id. These variables are readily
available in your snippet.
$image is an associative array containing: $image is an object containing the following properties:
- source: string, the source of the image, e.g. public://photo.jpg - source: string, the source of the image, e.g. public://photo.jpg
- info: array, example data: - info: array, example data:
- width (int) 180 - width (int) 180
@ -153,7 +176,7 @@ $image_context is an associative array containing:
- HEX (string) 000000 - HEX (string) 000000
- alpha (string) 100 - alpha (string) 100
- angle (string) 0 - angle (string) 0
- fontfile (string:10) lhandw.ttf - fontfile (string:46) module://image_effects_text/Komika_display.ttf
- text_source (string) text - text_source (string) text
- text (string) Hello World! - text (string) Hello World!
- php (string) return 'Hello World!' - php (string) return 'Hello World!'
@ -192,6 +215,18 @@ $image_context is an associative array containing:
- title (string) ... - title (string) ...
- ... - ...
$image_style is an associative array containing the current image style being
processed. It ocntians a.o.:
- isid: the unique image style id
- name: machine name.
- label: Human readable name.
- effects: An array with the effects of this image style, ordered in the way
they should be applied.
$image_effect_id is an int containng the unique id of the current image effect
being applied. This can be used to look the current image effect up in the
$image_style array.
Of course there are many other possible useful globals. Think of: Of course there are many other possible useful globals. Think of:
- base_url - base_url
- base_path - base_path
@ -205,20 +240,30 @@ Using these information you can access entity data as follows:
Specific case (1 entity, of known entity_type, referring to the image): Specific case (1 entity, of known entity_type, referring to the image):
<?php <?php
if (!$image_context['entity']) {
return 'No referring entity';
}
$entity_type = 'node'; $entity_type = 'node';
$field_name = 'my_field'; $field_name = 'my_field';
$entity = $image_context['entity']; $entity = $image_context['entity'];
$field = field_get_items($entity_type, $entity, $field_name); $field = field_get_items($entity_type, $entity, $field_name);
if ($field) {
return isset($field[0]['value']) ? $field[0]['value'] : 'No field value';
}
?> ?>
Or the more general case (not knowing the referring type, or multiple entities Or the more general case (not knowing the referring type, or multiple entities
that may be referring to the image): that may be referring to the image):
<?php <?php
if (!$image_context['referring_entities']) {
return 'No referring entities';
}
$referring_entities = $image_context['referring_entities']; $referring_entities = $image_context['referring_entities'];
foreach ($referring_entities as $field_name => $field_referring_entities) { foreach ($referring_entities as $field_name => $field_referring_entities) {
foreach ($field_referring_entities as $entity_type => $entities) { foreach ($field_referring_entities as $entity_type => $entities) {
foreach ($entities as $entity_id => $entity) { foreach ($entities as $entity_id => $entity) {
$field = field_get_items($entity_type, $entity, $field_name); $field = field_get_items($entity_type, $entity, $field_name);
// ...
} }
} }
} }
@ -232,7 +277,7 @@ TODO
do the same? do the same?
- Language and alt/title: what if the first user to pass by and that generates - 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? the image is in a language that has no alt/title?
- Newlines: seem to work in GD, not in Imagemagick. - Check for existence of imagettftext() and fail properly.
To quote http://www.imagemagick.org/Usage/text/#draw: 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 As of IM version 6.2.4, the "-draw text" operation no longer understands the use

View File

@ -10,7 +10,7 @@
* Only tuned for Ubuntu so far. I've been unable do find ubiquitous tools that * Only tuned for Ubuntu so far. I've been unable do find ubiquitous tools that
* provide useful font listings.' * provide useful font listings.'
*/ */
function image_effects_text_help_inc($path, $arg) { function image_effects_text_help_inc(/*$path, $arg*/) {
$output = "<p> $output = "<p>
For text rendering to work on a server, we <em>must</em> 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. know the system path to the font <em>file</em>, not just the name.
@ -39,7 +39,7 @@ function image_effects_text_help_inc($path, $arg) {
} }
/** /**
* Builds the form structure for the overlay text image effect. * Image effect form callback for the text effect.
* *
* Note that this is not a complete form, it only contains the portion of the * 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 * form for configuring the effect options. Therefore it does not not need to
@ -51,36 +51,132 @@ function image_effects_text_help_inc($path, $arg) {
* @return array * @return array
* The form definition for this effect. * The form definition for this effect.
*/ */
function image_effects_text_form_inc($data) { function image_effects_text_form_inc(array $data) {
// Use of functions imagecache_file_...() creates a dependency on file utility.inc. // Use of functions imagecache_file_...() creates a dependency on file utility.inc.
module_load_include('inc', 'imagecache_actions', 'utility'); module_load_include('inc', 'imagecache_actions', 'utility');
// Use of function imagecache_rgb_form() creates a dependency on file utility-color.inc. // Use of function imagecache_rgb_form() creates a dependency on file utility-color.inc.
module_load_include('inc', 'imagecache_actions', 'utility-color'); module_load_include('inc', 'imagecache_actions', 'utility-color');
// Note: we also need to check for the existence of the module: admin has // 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 // all rights, so user_access(...) returns TRUE even if the module is not
// enabled and the permission does not exist. // enabled and the permission does not exist.
// A user without the 'use PHP for settings' permission (defined by the core // A user without the 'use PHP for settings' permission (defined by the core
// PHP filter module) may not: // PHP filter module) may not:
// - Select the 'PHP code' text source option if it is currently not selected. // - Select the 'PHP code' text source option if it is currently not selected.
// - Change the 'PHP code' textarea. // - Change the 'PHP code' textarea.
$allow_dynamic = user_access('use PHP for settings') && module_exists('php'); $allow_php = module_exists('php') && user_access('use PHP for settings');
$defaults = array( $defaults = array(
'size' => 12, 'text_source' => 'text',
'angle' => 0, 'text' => t('Hello World!'),
'php' => 'if (!$image_context[\'entity\']) {
return \'' . t('No referring entity') . '\';
}
$entity_type = \'node\';
$field_name = \'my_field\';
$entity = $image_context[\'entity\'];
$field = field_get_items($entity_type, $entity, $field_name);
if ($field) {
return isset($field[0][\'value\']) ? $field[0][\'value\'] : \'' . t('No field value') . '\';
}
',
'text_case' => 'none',
'fontfile' => drupal_get_path('module', 'image_effects_text') . '/Komika_display.ttf',
'size' => 50,
'RGB' => array('HEX' => '#000000'),
'alpha' => 100,
'xpos' => '0', 'xpos' => '0',
'ypos' => '0', 'ypos' => '0',
'halign' => 'left', 'halign' => 'left',
'valign' => 'bottom', 'valign' => 'top',
'RGB' => array('HEX' => '#000000'), 'angle' => 0,
'alpha' => 100,
'fontfile' => 'lhandw.ttf',
'text_source' => 'text',
'text' => 'Hello World!',
'php' => 'return \'Hello World!\'',
); );
$data += $defaults; $data += $defaults;
$tokens = token_info();
$tokens = array_keys($tokens['types']);
$form = array( $form = array(
'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>Text (with token replacement)</strong>: A text with optional token replacement. Line breaks can be inserted with \n (\\\\n for a literal \n). 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 in README.txt 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('Text (with token replacement)'),
'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')),
),
),
'token_help' => array(
'#type' => 'fieldset',
'#title' => t('Token replacement patterns'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'tree' => array(
'#theme' => 'token_tree',
'#token_types' => $tokens,
),
'token_module_notice' => array(
'#markup' => !module_exists('token') ? t('You might want to enable the token module to view token replacement patterns.') : '',
),
'entity_token_module_notice' => array(
'#markup' => !module_exists('entity_token') ? t('You might want to enable the entity_token module to get token options for all entities and field properties.') : '',
),
'#states' => array(
'visible' => array(':input[name="data[text_source]"]' => array('value' => 'text')),
),
),
'php' => array(
'#type' => 'textarea',
'#rows' => 12,
'#title' => t('PHP code'),
'#default_value' => $data['php'],
'#disabled' => !$allow_php,
'#states' => array(
'visible' => array(':input[name="data[text_source]"]' => array('value' => 'php')),
),
'#wysiwyg' => FALSE,
),
'text_case' => array(
'#title' => t('Capitalization'),
'#type' => 'select',
'#options' => array(
'none' => t('No transform'),
'upper' => t('Upper case'),
'lower' => t('Lower case'),
'ucfirst' => t('Capitalize first letter'),
'ucwords' => t('Capitalize each word'),
),
'#default_value' => $data['text_case'],
'#description' => t('Defines if and how to transform the case of the text to render.'),
),
'fontfile' => array(
'#type' => 'textfield',
'#title' => t('Font file name'),
'#default_value' => $data['fontfile'],
'#description' => imagecache_actions_file_field_description(),
'#element_validate' => array('image_effects_text_validate_font'),
'#size' => 80,
),
'size' => array( 'size' => array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Font size'), '#title' => t('Font size'),
@ -88,6 +184,14 @@ function image_effects_text_form_inc($data) {
'#description' => t('The font size in points. Only in GD1 this is in pixels.'), '#description' => t('The font size in points. Only in GD1 this is in pixels.'),
'#size' => 3, '#size' => 3,
), ),
'RGB' => imagecache_rgb_form($data['RGB']),
'alpha' => array(
'#type' => 'textfield',
'#title' => t('Opacity'),
'#default_value' => $data['alpha'],
'#size' => 3,
'#description' => t('Opacity: 1-100.'),
),
'xpos' => array( 'xpos' => array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('X offset'), '#title' => t('X offset'),
@ -107,22 +211,22 @@ function image_effects_text_form_inc($data) {
'#title' => t('Horizontal alignment'), '#title' => t('Horizontal alignment'),
'#default_value' => $data['halign'], '#default_value' => $data['halign'],
'#description' => t('The horizontal alignment of the text around the given %xpos.', array('%xpos' => t('X offset'))), '#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')), '#options' => array(
'left' => t('Left'),
'center' => t('Center'),
'right' => t('Right')
),
), ),
'valign' => array( 'valign' => array(
'#type' => 'select', '#type' => 'select',
'#title' => t('Vertical alignment'), '#title' => t('Vertical alignment'),
'#default_value' => $data['valign'], '#default_value' => $data['valign'],
'#description' => t('The vertical alignment of the text around the given %ypos.', array('%ypos' => t('Y offset'))), '#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')), '#options' => array(
), 'top' => t('Top'),
'RGB' => imagecache_rgb_form($data['RGB']), 'center' => t('Center'),
'alpha' => array( 'bottom' => t('Bottom')
'#type' => 'textfield', ),
'#title' => t('Opacity'),
'#default_value' => $data['alpha'] ? $data['alpha'] : 100,
'#size' => 3,
'#description' => t('Opacity: 1-100.'),
), ),
'angle' => array( 'angle' => array(
'#type' => 'textfield', '#type' => 'textfield',
@ -131,52 +235,8 @@ function image_effects_text_form_inc($data) {
'#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.'), '#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, '#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') { if (!$allow_php && $data['text_source'] !== 'php') {
unset($form['text_fieldset']['text_source']['#options']['php']); unset($form['text_fieldset']['text_source']['#options']['php']);
} }
@ -188,7 +248,7 @@ function image_effects_text_form_inc($data) {
* Element validation callback for the effect form. * Element validation callback for the effect form.
* @see http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#element_validate * @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) { function image_effects_text_form_validate(array $element/*, &$form_state, $form*/) {
if (!is_numeric($element['size']['#value']) || $element['size']['#value'] <= 0) { 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')))); form_error($element['size'], t('%field must be a positive number.', array('%field' => t('Font size'))));
} }
@ -201,17 +261,30 @@ function image_effects_text_form_validate($element, &$form_state, $form) {
} }
/** /**
* Implementation of theme_hook() for text image effect. * Validates that the file as specified in the element exists and is readable.
*
* This is a Form API #element_validate callback.
*
* @param array $element
*/
function image_effects_text_validate_font(array &$element/*, &$form_status*/) {
if (!imagecache_actions_find_file($element['#value'])) {
drupal_set_message(t("Unable to find the font file '%file'. Please check the path. You can ignore this waning, if the font refers to a system or toolkit font, and the text shows correctly.", array('%file' => $element['#value'])), 'warning');
}
}
/**
* Implements theme_hook() for the text effect summary.
* *
* @param array $variables * @param array $variables
* An associative array containing: * An associative array containing:
* - data: The current configuration for this resize effect. * - data: The current configuration for this image effect.
* *
* @return string * @return string
* The HTML for the summary of a text image effect. * The HTML for the summary of this image effect.
* @ingroup themeable * @ingroup themeable
*/ */
function theme_image_effects_text_summary($variables) { function theme_image_effects_text_summary(array $variables) {
$data = $variables['data']; $data = $variables['data'];
switch ($data['text_source']) { switch ($data['text_source']) {
case 'alt': case 'alt':
@ -226,27 +299,23 @@ function theme_image_effects_text_summary($variables) {
case 'php': case 'php':
$text = 'PHP code'; $text = 'PHP code';
break; break;
default:
$text = '';
break;
} }
return 'Text: ' . $text . '; Position: ' . $data['xpos'] . ',' . $data['ypos'] . '; Alignment: ' . $data['halign'] . ',' . $data['valign']; return 'Text: ' . $text . '; Position: ' . $data['xpos'] . ',' . $data['ypos'] . '; Alignment: ' . $data['halign'] . ',' . $data['valign'];
} }
/** /**
* (Real implementation of) Image effect callback; Overlay text on an image * Image effect callback for the text effect.
* resource.
*
* @param object $image
* An image object returned by image_load().
* *
* @param stdClass $image
* @param array $data * @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 * @return boolean
* true on success, false on failure to apply the effect. * true on success, false otherwise.
*/ */
function image_effects_text_effect_inc($image, $data) { function image_effects_text_effect_inc(stdClass $image, array $data) {
// Use of imagecache_actions_hex2rgba() ,the imagecache_file_...() functions, // Use of imagecache_actions_hex2rgba() ,the imagecache_file_...() functions,
// and imagecache_actions_get_image_context() create a dependency on // and imagecache_actions_get_image_context() create a dependency on
// file utility.inc. // file utility.inc.
@ -256,79 +325,82 @@ function image_effects_text_effect_inc($image, $data) {
// Start with a straight copy. // Start with a straight copy.
$params = $data; $params = $data;
// Get the text to overlay.
$params['text'] = image_effects_text_get_text($image, $params);
if (empty($params['text'])) {
// No text to overlay. This is a situation that can be expected with token
// replacement, or custom PHP code. Return immediately with success.
return TRUE;
}
// Find out where the font file is located and if it is readable. // Find out where the font file is located and if it is readable.
$params['fontpath'] = imagecache_actions_find_file($data['fontfile']); $params['fontpath'] = imagecache_actions_find_file($data['fontfile']);
if ($params['fontpath'] === FALSE) { 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'); // Pass the font on and let's hope that the toolkit knows where to find it.
return FALSE; // We did warn on the effect form.
$params['fontpath'] = $data['fontfile'];
} }
// Get the text to overlay. // Parse offsets.
$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['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']); $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). // Convert color from hex (as it is stored in the UI).
$params['RGB'] = $data['RGB']; $params['RGB'] = $data['RGB'];
if($params['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($params['RGB']['HEX'])) { if ($params['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($params['RGB']['HEX'])) {
$params['RGB'] += $deduced; $params['RGB'] += $deduced;
} }
// Make int's of various parameters // Make integers of various parameters.
$params['size'] = (int) $params['size']; $params['size'] = (int) $params['size'];
$params['xpos'] = (int) $params['xpos']; $params['xpos'] = (int) $params['xpos'];
$params['ypos'] = (int) $params['ypos']; $params['ypos'] = (int) $params['ypos'];
// Hand over to toolkit
// Hand over to toolkit.
return image_toolkit_invoke('image_effects_text', $image, array($params)); return image_toolkit_invoke('image_effects_text', $image, array($params));
} }
/** /**
* GD toolkit specific implementation of this image effect. * GD toolkit specific implementation of the text effect.
* *
* @param object $image * @param stdClass $image
* @param array $params * @param array $data
* An array containing the parameters for this effect. * The parameters for this effect.
* *
* @return bool * @return bool
* true on success, false otherwise. * true on success, false otherwise.
*/ */
function image_gd_image_effects_text($image, $params) { function image_gd_image_effects_text(stdClass $image, array $data) {
// Convert color and alpha to GD alpha and color value. // Convert color and alpha to GD alpha and color value.
// GD alpha value: 0 = opaque, 127 = transparent. // GD alpha value: 0 = opaque, 127 = transparent.
$params['alpha'] = (int) ((1 - ($params['alpha'] / 100)) * 127); $data['alpha'] = (int) ((1 - ($data['alpha'] / 100)) * 127);
$color = imagecolorallocatealpha($image->resource, $params['RGB']['red'], $params['RGB']['green'], $params['RGB']['blue'], $params['alpha']); $color = imagecolorallocatealpha($image->resource, $data['RGB']['red'], $data['RGB']['green'], $data['RGB']['blue'], $data['alpha']);
if ($color !== FALSE) { if ($color !== FALSE) {
$bounds = NULL; $bounds = NULL;
// Adjust Y position for vertical alignment (if different from bottom). // Adjust Y position for vertical alignment (if different from bottom).
if ($params['valign'] !== 'bottom') { if ($data['valign'] !== 'bottom') {
// Get bounding box. // Get bounding box.
// PHP Manual: "This function requires both the GD library and the » FreeType library." // 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. // So it is not more demanding than imagettftext, which we need anyway.
$bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']); $bounds = imagettfbbox($data['size'], 0, $data['fontpath'], $data['text']);
if ($bounds === FALSE) { if (!$bounds) {
drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment settings.'), 'warning'); drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment settings.'), 'warning');
} }
else { else {
// Get height of bounding box. // Get height of bounding box.
$height = $bounds[1] - $bounds[7]; $height = $bounds[1] - $bounds[7];
// Shift ypos down (full height on bottom, half the height on center). // Shift ypos down (full height on bottom, half the height on center).
$params['ypos'] += $params['valign'] === 'center' ? (int) ($height/2) : $height; $data['ypos'] += $data['valign'] === 'center' ? (int) ($height / 2) : $height;
} }
} }
// Adjust X position for horizontal alignment (if different from left). // Adjust X position for horizontal alignment (if different from left).
if ($params['halign'] !== 'left') { if ($data['halign'] !== 'left') {
// Get bounding box. // Get bounding box. PHP Manual: "This function requires both the GD
// PHP Manual: "This function requires both the GD library and the » FreeType library." // library and the » FreeType library.", so it is not more demanding than
// So it is not more demanding than imagettftext, which we need anyway. // imagettftext(), which we need anyway.
if ($bounds === NULL) { if ($bounds === NULL) {
$bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']); $bounds = imagettfbbox($data['size'], 0, $data['fontpath'], $data['text']);
if ($bounds === FALSE) { if (!$bounds) {
drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment.'), 'warning'); drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment.'), 'warning');
} }
} }
@ -336,39 +408,40 @@ function image_gd_image_effects_text($image, $params) {
// Get width of bounding box. // Get width of bounding box.
$width = $bounds[2] - $bounds[0]; $width = $bounds[2] - $bounds[0];
// Shift xpos to the left (full width on right, half the width on center). // Shift xpos to the left (full width on right, half the width on center).
$params['xpos'] -= $params['halign'] === 'center' ? (int) ($width/2) : $width; $data['xpos'] -= $data['halign'] === 'center' ? (int) ($width / 2) : $width;
} }
} }
// PHP Manual: "This function requires both the GD library and the » FreeType library." // 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']); $bounds = imagettftext($image->resource, $data['size'], $data['angle'], $data['xpos'], $data['ypos'], $color, $data['fontpath'], $data['text']);
return $bounds !== FALSE; return $bounds !== FALSE;
} }
return FALSE; return FALSE;
} }
/** /**
* Imagemagick toolkit specific implementation of this image effect. * Imagemagick toolkit specific implementation of the text effect.
* *
* Text in Imagemagick: * Text in Imagemagick:
* @link http://www.imagemagick.org/script/command-line-options.php?#draw * - http://www.imagemagick.org/script/command-line-options.php?#draw
* @link http://www.imagemagick.org/script/command-line-options.php?#annotate * - http://www.imagemagick.org/script/command-line-options.php?#annotate
* *
* UTF-8/non-ascii characters * UTF-8/non-ascii characters:
* Though the online imagemagick manual mentions some problems with accented * To prevent problems with non-ASCII characters, the online manual suggests to
* characters, it worked fine for me in a Windows Vista shell. TBC on other * put the text in a file and use the @{file} syntax. This does not work with
* OS'es (including linux) * the text primitive of the -draw command, so we use -annotate.
* (@see: http://www.imagemagick.org/Usage/windows/#character_encoding) * We put the text in a temporary file which will be deleted by our hook_exit().
* http://www.imagemagick.org/Usage/windows/#character_encoding
* *
* Alignment in Imagemagick: * Alignment in Imagemagick:
* This is not directly supported, though a justicifcation option has been * This is not directly supported, though a justification option has been
* proposed: @link http://www.imagemagick.org/Usage/bugs/future/#justification. * proposed: http://www.imagemagick.org/Usage/bugs/future/#justification.
* *
* What we do have is the gravity option: * 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 * 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, * justification based on that placement. So we use gravity here for alignment,
* but will thus have to rebase our positioning. * but will thus have to rebase our positioning.
* - http://www.imagemagick.org/Usage/annotating/#gravity
* *
* Gravity |halign|valign |hpos change|vpos change * Gravity |halign|valign |hpos change|vpos change
* ------------------------------------------------ * ------------------------------------------------
@ -381,8 +454,16 @@ function image_gd_image_effects_text($image, $params) {
* SouthWest left bottom 0 -height * SouthWest left bottom 0 -height
* South center bottom -width/2 -height * South center bottom -width/2 -height
* SouthEast right bottom -width -height * SouthEast right bottom -width -height
*
* @param stdClass $image
* @param array $data
* The parameters for this effect.
*
* @return bool
* true on success, false otherwise.
*/ */
function image_imagemagick_image_effects_text($image, $params) { function image_imagemagick_image_effects_text(stdClass $image, array $data) {
static $alignments2gravity = array( static $alignments2gravity = array(
'left' => array( 'left' => array(
'top' => array( 'top' => array(
@ -438,39 +519,65 @@ function image_imagemagick_image_effects_text($image, $params) {
); );
// Convert color and alpha to Imagemagick rgba color argument. // Convert color and alpha to Imagemagick rgba color argument.
$alpha = $params['alpha'] / 100; $alpha = $data['alpha'] / 100;
$color = 'rgba(' . $params['RGB']['red']. ',' . $params['RGB']['green'] . ',' . $params['RGB']['blue'] . ','. $alpha . ')'; $color = 'rgba(' . $data['RGB']['red'] . ',' . $data['RGB']['green'] . ',' . $data['RGB']['blue'] . ',' . $alpha . ')';
// Alignment // Set gravity for the alignment and calculate the translation to the
$alignment_corrections = $alignments2gravity[$params['halign']][$params['valign']]; // requested x and y offset as starting from the gravity point.
$alignment_corrections = $alignments2gravity[$data['halign']][$data['valign']];
$gravity = $alignment_corrections['gravity']; $gravity = $alignment_corrections['gravity'];
if ($alignment_corrections['tx'] > 0) { if ($alignment_corrections['tx'] > 0) {
$params['xpos'] = (int) ($alignment_corrections['tx'] * $image->info['width'] - $params['xpos']); $data['xpos'] = (int) ($alignment_corrections['tx'] * $image->info['width'] - $data['xpos']);
} }
else { else {
$params['xpos'] += (int) ($alignment_corrections['tx'] * $image->info['width']); $data['xpos'] += (int) ($alignment_corrections['tx'] * $image->info['width']);
} }
if ($alignment_corrections['ty'] > 0) { if ($alignment_corrections['ty'] > 0) {
$params['ypos'] = (int) ($alignment_corrections['ty'] * $image->info['height'] - $params['ypos']); $data['ypos'] = (int) ($alignment_corrections['ty'] * $image->info['height'] - $data['ypos']);
} }
else { else {
$params['ypos'] += (int) ($alignment_corrections['ty'] * $image->info['height']); $data['ypos'] += (int) ($alignment_corrections['ty'] * $image->info['height']);
} }
// Define the quote to use around the text. This is part of the argument of // Add signs to translation, also when positive or 0.
// the -draw command and thus should NOT be the shell argument enclosing if ($data['xpos'] >= 0) {
// character. $data['xpos'] = '+' . $data['xpos'];
$quote = strstr($_SERVER['SERVER_SOFTWARE'], 'Win32') || strstr($_SERVER['SERVER_SOFTWARE'], 'IIS') ? "'" : '"'; }
// and subsequently escape the use of that quote within the text. if ($data['ypos'] >= 0) {
$text = $params['text']; $data['ypos'] = '+' . $data['ypos'];
$text = str_replace($quote, "\\$quote", $text); }
$image->ops[] = '-font ' . escapeshellarg($params['fontpath']); // Angle must be positive.
$image->ops[] = "-pointsize {$params['size']}"; if ($data['angle'] < 0) {
$data['angle'] = $data['angle'] % 360 + 360;
}
// Set font file, size and color. fontpath is the real path, not a wrapper.
$image->ops[] = '-font ' . escapeshellarg($data['fontpath']);
$image->ops[] = "-pointsize {$data['size']}";
$image->ops[] = '-fill ' . escapeshellarg($color); $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'); // Add text to a temporary file,
$image->ops[] = '-draw ' . escapeshellarg("gravity $gravity translate {$params['xpos']},{$params['ypos']} rotate {$params['angle']} text 0,0 $quote$text$quote"); $tmp_file_name = drupal_tempnam('temporary://', 'image_effects_text');
$tmp_file = fopen($tmp_file_name, 'w');
if ($tmp_file) {
fwrite($tmp_file, $data['text']);
fclose($tmp_file);
// and inform our hook_exit about it.
image_effects_text_exit($tmp_file_name);
$tmp_file_name = drupal_realpath($tmp_file_name);
$text = "@$tmp_file_name";
}
else {
// Fallback to pass the text via the command line, let's hope there are no
// non-ASCII characters or that it works anyway (OS and locale dependent).
$text = $data['text'];
}
// Add gravity.
$image->ops[] = "-gravity $gravity";
// Add text angle, position and text (file) itself.
$image->ops[] = "-annotate {$data['angle']}x{$data['angle']}{$data['xpos']}{$data['ypos']} " . escapeshellarg($text);
return TRUE; return TRUE;
} }
@ -499,14 +606,14 @@ function image_imagemagick_image_effects_text($image, $params) {
* The algorithm will accept many more situations, though the result may be hard * The algorithm will accept many more situations, though the result may be hard
* to predict. * to predict.
* *
* @param string $position
* The string defining the position.
* @param int $width * @param int $width
* The length of the horizontal dimension. * The length of the horizontal dimension.
* @param int $height * @param int $height
* The length of the vertical dimension. * The length of the vertical dimension.
* @param int $length * @param int $length
* The length of the current dimension (should be either width or height). * The length of the current dimension (should be either width or height).
* @param string $position
* The string defining the position.
* *
* @return number * @return number
* The computed offset in pixels. * The computed offset in pixels.
@ -543,7 +650,7 @@ function image_effects_text_get_offset($position, $width, $height, $length) {
break; break;
case 'center': case 'center':
// half the current dimension as provided by $length. // half the current dimension as provided by $length.
$value += $sign * $length/2; $value += $sign * $length / 2;
$sign = 1; $sign = 1;
break; break;
default: default:
@ -565,38 +672,123 @@ function image_effects_text_get_offset($position, $width, $height, $length) {
/** /**
* Get the text to use for this image. * Get the text to use for this image.
* *
* @param object $image * @param stdClass $image
* The image the current effect is to be applied to. * The image the current effect is to be applied to.
* @param array $data * @param array $data
* An array containing the effect data. * An array containing the effect data.
* *
* @return string * @return string
* Plain string to be placed on the image. * Plain text to be placed on the image.
*/ */
function image_effects_text_get_text($image, $data) { function image_effects_text_get_text(stdClass $image, array $data) {
// Get context about the image.
$image_context = imagecache_actions_get_image_context($image, $data);
if ($data['text_source'] === 'text') { if ($data['text_source'] === 'text') {
$text = $data['text']; // Replace \n with a newline character, except when preceded by a \.
} $text = preg_replace('/([^\\\\])\\\\n/', "$1\n", $data['text']);
else { // Replace \\n by \n.
// Get context about the image. $text = preg_replace('/\\\\\\\\n/', '\n', $text);
$image_context = imagecache_actions_get_image_context($image, $data);
if ($data['text_source'] === 'alt' || $data['text_source'] === 'title') { // Replace tokens.
// Existence of an image field is not guaranteed, so check for that first. $token_data = array();
$text = isset($image_context['image_field'][$data['text_source']]) ? $image_context['image_field'][$data['text_source']] : ''; foreach ($image_context['referring_entities'] as /*$field_name =>*/ $field_referring_entities) {
foreach ($field_referring_entities as $entity_type => $entities) {
// We can pass only 1 entity per given type to token_replace(), we take
// the first.
$token_data[$entity_type] = reset($entities);
}
} }
else { // $data['text_source'] === 'php' if ($image_context['managed_file']) {
// Process the php using php_eval (rather than eval), but with GLOBAL $token_data['file'] = $image_context['managed_file'];
// variables, so they can be passed successfully. }
$GLOBALS['image_context'] = $image_context; // We should not sanitize the text as it will not be rendered in the browser
$GLOBALS['image'] = $image; // but is rendered on the image canvas on the server.
// We don't need to check_plain() the resulting text, as the text is not $text = token_replace($text, $token_data, array('clear' => TRUE, 'sanitize' => FALSE));
// rendered in a browser but processed on the server. }
$text = module_exists('php') ? php_eval('<'.'?php global $image, $image_context; ' . $data['php'] . ' ?'.'>') : ''; else if ($data['text_source'] === 'alt' || $data['text_source'] === 'title') {
$text = '';
unset($GLOBALS['image']); // We have 2 possible sources for the alt or title text:
unset($GLOBALS['image_context']); // - Image field.
// - Media module (7.x-2.x) with file_entity: the alt and title come as
// fields of the file entity, stored in 'managed_file'. The names of the
// fields are field_file_image_alt_text resp. field_file_image_title_text.
// BTW: these fields are also available in the 'image_field' entry, but as
// a managed file may be existing without any image field referring to it,
// we do the lookup in the managed_file entry.
if (!empty($image_context['image_field'][$data['text_source']])) {
$text = $image_context['image_field'][$data['text_source']];
}
else if (!empty($image_context['managed_file'])) {
$field = field_get_items('file', $image_context['managed_file'], "field_file_image_{$data['text_source']}_text");
if ($field) {
$text = $field[0]['value'];
}
} }
} }
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;
// Get (non-alterable) context about the image style and image effect.
$execution_info = imagecache_actions_get_image_effect_context();
$GLOBALS['image_style'] = $execution_info['image_style'];
$GLOBALS['image_effect_id'] = $execution_info['image_effect_id'];
// 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_effect_id']);
unset($GLOBALS['image_style']);
unset($GLOBALS['image']);
unset($GLOBALS['image_context']);
}
// Convert case.
$text = image_effect_text_case_transform($text, isset($data['text_case']) ? $data['text_case'] : 'none');
return $text; return $text;
} }
/**
* Transform a string by a certain method.
*
* Proudly copied from module://views/includes/handlers.inc.
*
* @param $string
* The input you want to transform.
* @param $option
* How do you want to transform it, possible values:
* - upper: Uppercase the string.
* - lower: lowercase the string.
* - ucfirst: Make the first char uppercase.
* - ucwords: Make each word in the string uppercase.
*
* @return string
* The transformed string.
*/
function image_effect_text_case_transform($string, $option) {
global $multibyte;
switch ($option) {
default:
return $string;
case 'upper':
return drupal_strtoupper($string);
case 'lower':
return drupal_strtolower($string);
case 'ucfirst':
return drupal_strtoupper(drupal_substr($string, 0, 1)) . drupal_substr($string, 1);
case 'ucwords':
if ($multibyte == UNICODE_MULTIBYTE) {
return mb_convert_case($string, MB_CASE_TITLE);
}
else {
return ucwords($string);
}
}
}

View File

@ -1,14 +1,14 @@
name = Image Text Effects name = Image Effects Text
description = Display simple or dynamic captions on images. description = Provides an image effect to overlay text captions on images.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = image dependencies[] = image
dependencies[] = imagecache_actions dependencies[] = imagecache_actions
; Information added by drupal.org packaging script on 2012-12-04 ; Information added by Drupal.org packaging script on 2018-03-20
version = "7.x-1.1" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,25 +0,0 @@
<?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

@ -3,7 +3,7 @@
* @file Provide text manipulation effects for image styles. * @file Provide text manipulation effects for image styles.
* *
* Ported by dman * Ported by dman
* from http://drupal.org/node/264862#comment-865490 by patrickharris * from https://drupal.org/node/264862#comment-865490 by patrickharris
* *
* Ported to D7 by fietserwin * Ported to D7 by fietserwin
* from imagecache_textactions 6.x-1.8. * from imagecache_textactions 6.x-1.8.
@ -29,8 +29,8 @@ function image_effects_text_image_effect_info() {
'label' => t('Text'), 'label' => t('Text'),
'help' => t('Add static or dynamic (coded) text to an image.'), 'help' => t('Add static or dynamic (coded) text to an image.'),
'dimensions passthrough' => TRUE, 'dimensions passthrough' => TRUE,
'form callback' => 'image_effects_text_form',
'effect callback' => 'image_effects_text_effect', 'effect callback' => 'image_effects_text_effect',
'form callback' => 'image_effects_text_form',
'summary theme' => 'image_effects_text_summary', 'summary theme' => 'image_effects_text_summary',
); );
@ -51,12 +51,13 @@ function image_effects_text_help($path, $arg) {
// link to it. // link to it.
return ' '; return ' ';
} }
return '';
} }
/** /**
* Implements hook_theme(). * Implements hook_theme().
* *
* We register theme functions for the effect summaries. * Registers theme functions for the effect summaries.
*/ */
function image_effects_text_theme() { function image_effects_text_theme() {
return array( return array(
@ -67,6 +68,36 @@ function image_effects_text_theme() {
); );
} }
/**
* Implements hook_exit().
*
* For imagemagick we place the text in a temporary file as this prevents
* problems with non-ASCII characters. This hook deletes files created during
* execution of the text effect. As a style may contain multiple text effects,
* there may be multiple files to delete.
*
* To keep track of temporary files created by this effect, the image effect
* function itself also calls this hook, but passes the filename as a parameter.
*
* @param string|null $destination
*/
function image_effects_text_exit($destination = NULL) {
static $tmp_file_names = array();
if (isset($destination)) {
if (substr($destination, 0 , strlen('temporary://')) === 'temporary://') {
// Called by the effect function. Add to our static list of files ot delete.
$tmp_file_names[] = $destination;
}
}
else {
// Normal invocation as Drupal hook: delete any files we have collected.
foreach ($tmp_file_names as $tmp_file_name) {
unlink($tmp_file_name);
}
}
}
/** /**
* Builds the form structure for the overlay text image effect. * Builds the form structure for the overlay text image effect.
*/ */

View File

@ -2,17 +2,18 @@ README
------ ------
README for the Image effect text test module. README for the Image effect text test module.
This module contains 2 image styles to test text effects. It uses an image This module contains several image styles to test text effects. It uses an image
containing a grid and a font which are included in the install package as well. containing a grid and a font which are included in the install package as well.
The image styles defined by this module start with 'text-test-'.
Hard Dependencies Hard Dependencies
----------------- -----------------
Hard dependencies: Hard dependencies:
- Imagecache actions. - Imagecache actions (canvas_actions and image_effects_text).
- Image (Drupal core). - Image (Drupal core).
- Features - System stream wrapper (https://drupal.org/project/system_stream_wrapper)
- System stream wrapper (http://drupal.org/project/system_stream_wrapper)
Soft Dependencies Soft Dependencies
----------------- -----------------
- Imagemagick (preferred toolkit, http://drupal.org/project/imagemagick). - Imagemagick (preferred toolkit, https://drupal.org/project/imagemagick).
- GD

View File

@ -1,483 +0,0 @@
<?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' => 'Echelle',
'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',
'dimensions passthrough' => TRUE,
'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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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' => 'module://image_effects_text_test/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(
30 => 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',
'dimensions passthrough' => TRUE,
'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' => 'module://image_effects_text_test/grid800x600.png',
),
'weight' => '-10',
),
31 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '100,200 left,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '-8',
),
32 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '300,200 left,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '3',
),
33 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '500,200 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '4',
),
34 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '100,400 center,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '5',
),
35 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '300,400 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '6',
),
36 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '500,400 center, bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '7',
),
37 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '100,500 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '8',
),
38 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '300,500 right,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '9',
),
39 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text_test/lhandw.ttf',
'text_source' => 'text',
'text' => '500,500 right,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '10',
),
),
);
return $styles;
}

View File

@ -1,19 +1,16 @@
name = Image Effects Text test name = Image Effects Text test
description = Image effects that test the text effect description = Defines image styles that test the text effect.
core = 7.x core = 7.x
package = Features package = Media
php = 5.2.4
project = image_effects_text_test
dependencies[] = image dependencies[] = image
dependencies[] = image_effects_text dependencies[] = image_effects_text
dependencies[] = imagecache_canvasactions
dependencies[] = system_stream_wrapper dependencies[] = system_stream_wrapper
features[features_api][] = api:1
features[image][] = text-rotate-test
features[image][] = text-test
; Information added by drupal.org packaging script on 2012-12-04 ; Information added by Drupal.org packaging script on 2018-03-20
version = "7.x-1.1" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -4,4 +4,606 @@
* Code for the Image Effects Text test feature. * Code for the Image Effects Text test feature.
*/ */
include_once('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-test-position-orientation.
$styles['text-test-position-orientation'] = array(
'name' => 'text-test-position-orientation',
'effects' => array(
115 => 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',
'dimensions passthrough' => TRUE,
'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' => 'module://image_effects_text_test/grid800x600.png',
),
'weight' => '-10',
),
116 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '100,200 left,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '-8',
),
117 => array(
'label' => 'Tekst',
'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' => 'left+300',
'ypos' => 'top+200',
'halign' => 'left',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'left+300,top+200 left,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '3',
),
118 => array(
'label' => 'Tekst',
'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' => 'right-300',
'ypos' => '200+top',
'halign' => 'left',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'right-300,200+top left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '4',
),
119 => array(
'label' => 'Tekst',
'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' => 'center-300',
'ypos' => '100+center',
'halign' => 'center',
'valign' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'center-300,100+center center,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '5',
),
120 => array(
'label' => 'Tekst',
'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+center',
'ypos' => 'center+100',
'halign' => 'center',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '-100+center,center+100 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '6',
),
121 => array(
'label' => 'Tekst',
'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' => '-200+right',
'ypos' => 'bottom-200',
'halign' => 'center',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '-200+right,bottom-200 center, bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '7',
),
122 => array(
'label' => 'Tekst',
'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' => 'top',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '300,500 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '8',
),
123 => array(
'label' => 'Tekst',
'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' => 'right-center+100',
'ypos' => 'bottom-100',
'halign' => 'right',
'valign' => 'center',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'right-center+100,bottom-100 right,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '9',
),
124 => array(
'label' => 'Tekst',
'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' => 'right-100',
'ypos' => 'bottom-100',
'halign' => 'right',
'valign' => 'bottom',
'RGB' => array(
'HEX' => '000000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'right-100,bottom-100 right,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '10',
),
),
);
// Exported image style: text-test-rotate-wrt-alignment.
$styles['text-test-rotate-wrt-alignment'] = array(
'name' => 'text-test-rotate-wrt-alignment',
'effects' => array(
125 => array(
'label' => 'Schalen',
'help' => 'Door te schalen worden de originele verhoudingen behouden. Als één van de dimensies wordt ingevuld zal de andere worden berekend.',
'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',
),
126 => 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',
'dimensions passthrough' => TRUE,
'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' => 'module://image_effects_text_test/grid800x600.png',
),
'weight' => '2',
),
127 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '100,200 45 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '3',
),
128 => array(
'label' => 'Tekst',
'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' => '66.67',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '300,200 66.67 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '4',
),
129 => array(
'label' => 'Tekst',
'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' => '90',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '500,200 45 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '5',
),
130 => array(
'label' => 'Tekst',
'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' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '100,500 -30 left,bottom',
'php' => 'return \'Hello World!\'',
),
'weight' => '7',
),
131 => array(
'label' => 'Tekst',
'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' => '-45',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '300,500 -45 center,center',
'php' => 'return \'Hello World!\'',
),
'weight' => '8',
),
132 => array(
'label' => 'Tekst',
'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' => '-90',
'fontfile' => 'module://image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => '600,400 -90 right,top',
'php' => 'return \'Hello World!\'',
),
'weight' => '9',
),
),
);
// Exported image style: text-test-utf8-multi-line-color-transparency.
$styles['text-test-utf8-multi-line-color-transparency'] = array(
'name' => 'text-test-utf8-multi-line-color-transparency',
'effects' => array(
133 => 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',
'dimensions passthrough' => TRUE,
'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' => 'module://image_effects_text_test/grid800x600.png',
),
'weight' => '-10',
),
114 => array(
'label' => 'Tekst',
'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' => '20',
'xpos' => '100',
'ypos' => '100',
'halign' => 'left',
'valign' => 'top',
'RGB' => array(
'HEX' => 'FF0000',
),
'alpha' => '100',
'angle' => '0',
'fontfile' => 'sites/all/modules/imagecache_actions/image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'top+100,left+100: é è à ù ç Ö ÿ “ ” ° ² ³ € x ÷',
'php' => 'if (!$image_context[\'entity\']) {
return \'No referring entity\';
}
$entity_type = \'node\';
$field_name = \'my_field\';
$entity = $image_context[\'entity\'];
$field = field_get_items($entity_type, $entity, $field_name);
if ($field) {
return isset($field[0][\'value\']) ? $field[0][\'value\'] : \'No field value\';
}
',
),
'weight' => '-9',
),
135 => array(
'label' => 'Tekst',
'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' => '50',
'xpos' => '100',
'ypos' => '200',
'halign' => 'left',
'valign' => 'top',
'RGB' => array(
'HEX' => '0000FF',
),
'alpha' => '90',
'angle' => '0',
'fontfile' => 'sites/all/modules/imagecache_actions/image_effects_text/Komika_display.ttf',
'text_source' => 'text',
'text' => 'Hello World!\\nOlá, mundo\\\\nHej världen',
'php' => 'if (!$image_context[\'entity\']) {
return \'No referring entity\';
}
$entity_type = \'node\';
$field_name = \'my_field\';
$entity = $image_context[\'entity\'];
$field = field_get_items($entity_type, $entity, $field_name);
if ($field) {
return isset($field[0][\'value\']) ? $field[0][\'value\'] : \'No field value\';
}
',
),
'weight' => '-8',
),
134 => array(
'label' => 'Tekst',
'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' => '50',
'xpos' => '400',
'ypos' => 'bottom-100',
'halign' => 'center',
'valign' => 'bottom',
'RGB' => array(
'HEX' => 'FFFFFF',
),
'alpha' => '50',
'angle' => '0',
'fontfile' => 'arial.ttf',
'text_source' => 'php',
'text' => 'Hello World!',
'php' => 'return "Hello World!\\nOlá, mundo\\\\nHej världen";
',
),
'weight' => '-7',
),
),
);
return $styles;
}

View File

@ -5,178 +5,201 @@
* layers, preserving transparency. * layers, preserving transparency.
*/ */
// Not sure where this library will live between upgrade versions. /**
// Be careful to not conflict with another copy of myself. * Places one image over another.
if (! function_exists('image_overlay')) { *
* @param stdClass $image
/** * A valid image object.
* Place one image over another * @param stdClass $layer
* * A valid image object to be placed over or under the $image image.
* @param object $image * @param int $x
* An image object. * Position of the overlay.
* @param object $overlay * @param int $y
* An image object. * Position of the overlay.
* @param $x * @param int $alpha
* Position of the overlay * Transparency of the overlay from 0-100. 0 is totally transparent. 100
* @param $y * (default) is totally opaque.
* Position of the overlay * @param boolean $reverse
* @param $alpha * Flag to indicate the 'overlay' actually goes under the image.
* Transparency of the overlay from 0-100. 0 is totally transparent. 100 *
* (default) is totally opaque. * @return boolean
* @param $reverse * true on success, false otherwise.
* BOOL flag to indicate the 'overlay' actually goes under the image. As */
* the imageapi callbacks modify the $image object by reference, this is needed function image_overlay(stdClass $image, stdClass $layer, $x, $y, $alpha = 100, $reverse = FALSE) {
* to replace the old image resource with the new one. if ($reverse) {
* @return bool success $x = imagecache_actions_keyword_filter($x, $layer->info['width'], $image->info['width']);
* $y = imagecache_actions_keyword_filter($y, $layer->info['height'], $image->info['height']);
* @ingroup imageapi
*/
function image_overlay($image, &$layer, $x, $y, $alpha = 100, $reverse = FALSE) {
if ($reverse) {
$x = imagecache_actions_keyword_filter($x, $layer->info['width'], $image->info['width']);
$y = imagecache_actions_keyword_filter($y, $layer->info['height'], $image->info['height']);
}
else {
$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));
} }
else {
/** $x = imagecache_actions_keyword_filter($x, $image->info['width'], $layer->info['width']);
* Place one image over another $y = imagecache_actions_keyword_filter($y, $image->info['height'], $layer->info['height']);
* 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;
} }
return image_toolkit_invoke('overlay', $image, array($layer, $x, $y, $alpha, $reverse));
/** }
* Improvements on this are welcomed!
* /**
* Please be aware of the limitations of imagemagick libraries out there - the * GD toolkit specific implementation of the image overlay effect.
* versions distributed on hosted servers (if any) are often several years *
* behind. Using the latest imagemagick release features will make this function * NOTE that the PHP libraries are not great at merging images SO we include a
* unusable in real deployments. * 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!
* @param $image *
* Base imageapi object. * A watermark is layer onto image, return the image. An underlay is image onto
* @param $layer * layer, return the layer. Almost identical, but seeing as we work with
* May be a filename or an imageAPI object, gets placed on top * resource handles, the handle needs to be swapped before returning.
* If using reverse, this is going to be the result we carry on working with. *
*/ * @param stdClass $image
function image_imagemagick_overlay($image, $layer, $x = 0, $y = 0, $alpha = 100, $reverse = FALSE) { * An image object.
// In imagemagick terms: * @param stdClass $layer
// - $image is the destination (the image being constructed) * Image object to be placed over or under the $image image.
// - $layer is the source (the source of the current operation) * @param int $x
// Add the layer image to the imagemagick command line. * Position of the overlay.
$image->ops[] = escapeshellarg($layer->source) . ' '; * @param int $y
* Position of the overlay.
// Set its offset. Offset arguments require a sign in front. * @param int $alpha
if ($x >= 0) { * Transparency of the overlay from 0-100. 0 is totally transparent. 100
$x = "+$x"; * (default) is totally opaque.
} * @param boolean $reverse
if ($y >= 0) { * Flag to indicate that the 'overlay' actually goes under the image.
$y = "+$y"; *
} * @return boolean
$image->ops[] = " -geometry $x$y"; * true on success, false otherwise.
*/
// And compose it with the destination. function image_gd_overlay(stdClass $image, stdClass $layer, $x, $y, $alpha = 100, $reverse = FALSE) {
if ($alpha == 100) { // If the given alpha is 100%, we can use imagecopy - which actually works,
// Lay one image over the other. The transparency channel of the upper // is more efficient, and seems to retain the overlays partial transparency.
// image and/or its dimensions (being smaller than the lower image) will // Still does not work great for indexed gifs though?
// determine what remains visible of the lower image). if ($reverse) {
// $upper = &$image;
// Note: In explicitly setting a -compose operator we reset/overwrite any $lower = &$layer;
// previously set one (former versions could produce erroneous results }
// in combination with other effects before this one). else {
if ($reverse) { $upper = &$layer;
$compose_operator = 'dst-over'; $lower = &$image;
} }
else { if ($alpha == 100 && ($upper->info['mime_type'] != 'image/gif')) {
$compose_operator = 'src-over'; imagealphablending($lower->resource, TRUE);
} imagesavealpha($lower->resource, TRUE);
$image->ops[] = "-compose $compose_operator -composite "; imagealphablending($upper->resource, TRUE);
} imagesavealpha($upper->resource, TRUE);
else { imagecopy($lower->resource, $upper->resource, $x, $y, 0, 0, $upper->info['width'], $upper->info['height']);
// Alpha is not 100, so this image effect turns into a blend operation. imagedestroy($upper->resource);
// The alpha determines what percentage of the upper image pixel will be $image->resource = $lower->resource;
// taken. From the lower image pixel, 100 - alpha percent will be taken. $image->info = $lower->info;
// }
// Note 1: I'm not sure if and how transparency of one or both images is else {
// used in or after the blend operation. // imagecopy() cannot be used and we have to use the slow library.
// Note 2: As of IM v6.5.3-4 (around june 2009) we can use: module_load_include('inc', 'imagecache_actions', 'watermark');
// -compose blend -define compose:args=30[,70] $watermark = new watermark();
$image->ops[] = "-compose blend -define compose:args=$alpha -composite "; $result_img = $watermark->create_watermark($lower->resource, $upper->resource, $x, $y, $alpha);
} // Watermark creates a new image resource, so clean up both old images.
return TRUE; imagedestroy($lower->resource);
} imagedestroy($upper->resource);
$image->resource = $result_img;
$image->info = $lower->info;
}
return TRUE;
}
/**
* Imagemagick toolkit specific implementation of the image overlay effect.
*
* An underlay should can be created like:
* -background None -extent 300x250-50-50 under.jpg -compose dst-over -composite
*
* Explanation:
* - first enlarge the canvas, making any new part fully transparent.
* - placing the "original" image on its requested position
* - define the "source" image to IM
* - compose the images placing the original over the source
*
* An overlay can be created like:
* overlay.png -geometry 50x50+100+75 -compose src-over -composite
*
* Explanation:
* - define the overlay image to IM
* - and define its size and position on the current image
* - compose the images placing the original over the source
*
* 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 stdClass $image
* An image object.
* @param stdClass $layer
* Image object to be placed over or under the $image image.
* @param int $x
* Position of the overlay.
* @param int $y
* Position of the overlay.
* @param int $alpha
* Transparency of the overlay from 0-100. 0 is totally transparent. 100
* (default) is totally opaque.
* @param boolean $reverse
* Flag to indicate that the 'overlay' actually goes under the image.
*
* @return boolean
* true on success, false otherwise.
*/
function image_imagemagick_overlay(stdClass $image, stdClass $layer, $x = 0, $y = 0, $alpha = 100, $reverse = FALSE) {
$realPath = imagecache_actions_find_file($layer->source);
if (!$realPath) {
return FALSE;
}
// Reset any gravity settings from earlier effects.
$image->ops[] = '-gravity None';
// 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.
// Set the dimensions of the overlay. Use of the scale option means that we
// need to change the dimensions: always set them, they don't harm when the
// scale option is not used.
$sign = $reverse ? -1 : 1;
$geometry = sprintf('%ux%u%+d%+d', $layer->info['width'], $layer->info['height'], $sign * $x, $sign * $y);
$compose_operator = $reverse ? 'dst-over' : 'src-over';
// 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) {
// Underlay.
$image->ops[] = '-background';
$image->ops[] = escapeshellarg('rgba(0,0,0,0)');
$image->ops[] = "-extent $geometry";
$image->ops[] = escapeshellarg($realPath);
}
else {
$image->ops[] = escapeshellarg($realPath);
$image->ops[] = "-geometry $geometry";
}
$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[] = escapeshellarg($realPath);
$image->ops[] = "-geometry $geometry";
$image->ops[] = "-compose blend -define compose:args=$alpha -composite";
}
return TRUE;
} }

View File

@ -1,10 +1,10 @@
README for the Image styleds admin Drupal module README for the Image styleds admin Drupal module
------------------------------------------------ ------------------------------------------------
Project page: http://drupal.org/project/imagecache_actions Project page: https://drupal.org/project/imagecache_actions
Current and past maintainers for Image styles admin: Current and past maintainers for Image styles admin:
- fietserwin (http://drupal.org/user/750928) - fietserwin (https://drupal.org/user/750928)
Release notes for 7.x-1.x-dev Release notes for 7.x-1.x-dev
@ -24,12 +24,12 @@ a test/showcase sute of styles. Finally, it allows everybody to test D8 image
module features in real life. module features in real life.
This module is not a replacement for the features module This module is not a replacement for the features module
(http://drupal.org/project/features). If you are serious about configuration (https://drupal.org/project/features). If you are serious about configuration
management and want to distribute styles to other systems, use features. management and want to distribute styles to other systems, use features.
Use this module for 1 time export/imports between different sites, "copy & Use this module for 1 time export/imports between different sites, "copy &
paste" reuse within a site, and when reporting issues to the imagecache_actions paste" reuse within a site, and when reporting issues to the imagecache_actions
issue queue. issue queue.
TODO TODO

View File

@ -0,0 +1,52 @@
table#image-styles {
table-layout: fixed;
}
#image-styles th.style-name {
width: 20%;
}
#image-styles th.effects {
width: 30%;
}
#image-styles th.settings {
width: 15%;
}
#image-styles td {
vertical-align: top;
}
#image-styles .expand.inner {
background: transparent url(/misc/menu-collapsed.png) left 0.6em no-repeat;
margin-left: -12px;
padding-left: 12px;
}
#image-styles .expanded.expand.inner {
background: transparent url(/misc/menu-expanded.png) left 0.6em no-repeat;
}
#image-styles .description {
cursor: pointer;
}
#image-styles .description .inner {
overflow: hidden; /* truncates descriptions if too long */
text-overflow: ellipsis;
white-space: nowrap;
}
#image-styles .description .expanded.inner {
overflow: visible;
white-space: normal;
}
#image-styles .description .expanded .details {
display: block;
}
#image-styles .description .expanded .separator {
display: none;
}

View File

@ -16,7 +16,9 @@
*/ */
function image_styles_admin_duplicate_page_callback($style) { function image_styles_admin_duplicate_page_callback($style) {
$duplicate_style = image_styles_admin_duplicate($style); $duplicate_style = image_styles_admin_duplicate($style);
drupal_set_message(t('Style %name has been duplicated to %new_name.', array('%name' => $style['name'], '%new_name' => $duplicate_style['name']))); drupal_set_message(t('Style %name has been duplicated to %new_name.', array(
'%name' => isset($style['label']) ? $style['label'] : $style['name'],
'%new_name' => isset($duplicate_style['label']) ? $duplicate_style['label'] : $duplicate_style['name'])));
drupal_goto('admin/config/media/image-styles'); drupal_goto('admin/config/media/image-styles');
} }
@ -29,14 +31,19 @@ function image_styles_admin_duplicate_page_callback($style) {
* The preferred name for the new style. If left empty, the new name will be * The preferred name for the new style. If left empty, the new name will be
* based on the name of the style to duplicate. In both cases and when * based on the name of the style to duplicate. In both cases and when
* necessary, the new name will be made unique by adding some suffix to it. * necessary, the new name will be made unique by adding some suffix to it.
* @param string|null $new_style_label
* The preferred label for the new style. If left empty, the new label will be
* based on the label of the style to duplicate. If that one is also empty,
* no label will be defined for the new style, so Drupal (>=7.23) will create
* one.
* *
* @return array * @return array
* An image style array with the newly created copy of the given style. * An image style array with the newly created copy of the given style.
* *
* @see image_style_name_validate() * @see image_style_name_validate()
*/ */
function image_styles_admin_duplicate($style, $new_style_name = NULL) { function image_styles_admin_duplicate($style, $new_style_name = NULL, $new_style_label = NULL) {
// Find a unique name for copy. // Find a unique name for the copy.
// Step 1: Find the base: name without things like '-copy' or '-copy-1' // Step 1: Find the base: name without things like '-copy' or '-copy-1'
$style_name_base = empty($new_style_name) ? $style['name'] : $new_style_name; $style_name_base = empty($new_style_name) ? $style['name'] : $new_style_name;
if (preg_match('/-copy(-\d+)?$/', $style_name_base)) { if (preg_match('/-copy(-\d+)?$/', $style_name_base)) {
@ -58,6 +65,25 @@ function image_styles_admin_duplicate($style, $new_style_name = NULL) {
} }
$style['name'] = $style_name; $style['name'] = $style_name;
// Step 4: Find a new label for the copy.
if (isset($new_style_label) || isset($style['label'])) {
$style_label = empty($new_style_label) ? $style['label'] : $new_style_label;
$copy = t('copy');
if (preg_match("/ $copy( \d+)?$/", $style_label)) {
$style_label = substr($style_label, 0, strpos($style_label, " $copy"));
}
// Step 4a: Add " copy" to it (if the name comes from the current style).
if (empty($new_style_label)) {
$style_label .= " $copy";
}
// Step 4b: Make "unique" (based on the number added to the name)
if ($i > 0) {
$style['label'] .= " $i";
}
}
// Unset isid to save it as a new style. // Unset isid to save it as a new style.
unset($style['isid']); unset($style['isid']);
$style = image_style_save($style); $style = image_style_save($style);
@ -74,31 +100,35 @@ function image_styles_admin_duplicate($style, $new_style_name = NULL) {
} }
/** /**
* drupal_get_form callback: form to export an image style. * drupal_get_form callback: form to export an image style.
* *
* @param array $style * @param array $form
* An image style array. * @param array $form_state
*/ * @param array $style
* An image style array.
*
* @return array
*/
function image_styles_admin_export_form($form, $form_state, $style) { function image_styles_admin_export_form($form, $form_state, $style) {
drupal_set_title(format_string('%page_name @style_name', array('%page_name' => t('Export image style'), '@style_name' => $style['name'])), PASS_THROUGH); drupal_set_title(format_string('%page_name @style_name',
array('%page_name' => t('Export image style'), '@style_name' => isset($style['label']) ? $style['label'] : $style['name'])),
PASS_THROUGH);
$form['serialized_style'] = array( $form['serialized_style'] = array(
'#type' => 'textarea', '#type' => 'textarea',
'#rows' => 5, '#rows' => 5,
'#title' => t('Image style export data'), '#title' => t('Image style export data'),
'#default_value' => serialize($style), '#default_value' => image_styles_admin_export_serialize($style),
'#attributes' => array('readonly' =>'readonly'), '#attributes' => array('readonly' =>'readonly'),
'#description' => t('Copy the contents of this field to the clipboard and, on another site, paste it in the textarea of an %page_title page.', array('%page_title' => t('Import image style'))), '#description' => t('Copy the contents of this field to the clipboard and, on another site, paste it in the textarea of an %page_title page.',
array('%page_title' => t('Import image style'))),
); );
return $form; return $form;
} }
/** /**
* drupal_get_form callback: form to import an image style. * drupal_get_form callback: form to import an image style.
*
* @param array $style
* An image style array.
*/ */
function image_styles_admin_import_form($form, $form_state) { function image_styles_admin_import_form($form/*, $form_state*/) {
$form['serialized_style'] = array( $form['serialized_style'] = array(
'#type' => 'textarea', '#type' => 'textarea',
'#rows' => 5, '#rows' => 5,
@ -121,7 +151,8 @@ function image_styles_admin_import_form($form, $form_state) {
* Callback to validate the import style form. * Callback to validate the import style form.
*/ */
function image_styles_admin_import_form_validate($form, &$form_state) { function image_styles_admin_import_form_validate($form, &$form_state) {
if (image_styles_admin_import_extract_style($form_state['values']['serialized_style']) === FALSE) { $import = image_styles_admin_unify_newlines($form_state['values']['serialized_style']);
if (image_styles_admin_import_extract_style($import) === FALSE) {
form_set_error('serialized_style', t('The %field cannot be imported as an image style.', array('%field' => t('Image style import data')))); form_set_error('serialized_style', t('The %field cannot be imported as an image style.', array('%field' => t('Image style import data'))));
} }
} }
@ -130,19 +161,64 @@ function image_styles_admin_import_form_validate($form, &$form_state) {
* Callback to process form submission of the import style form. * Callback to process form submission of the import style form.
*/ */
function image_styles_admin_import_form_submit($form, &$form_state) { function image_styles_admin_import_form_submit($form, &$form_state) {
$style = image_styles_admin_import_extract_style($form_state['values']['serialized_style']); $import = image_styles_admin_unify_newlines($form_state['values']['serialized_style']);
$style = image_styles_admin_import_extract_style($import);
// Import the style by "duplicating" it, but prevent adding the -copy suffix // Import the style by "duplicating" it, but prevent adding the -copy suffix
// by passing the requested name as 2nd parameter. // by passing the requested name and label as 2nd and 3rd parameter.
$new_style = image_styles_admin_duplicate($style, $style['name']); $new_style = image_styles_admin_duplicate($style, $style['name'], isset($style['label']) ? $style['label'] : NULL);
if ($new_style['name'] === $style['name']) { if ($new_style['name'] === $style['name']) {
drupal_set_message(t('Style %name has been imported.', array('%name' => $style['name']))); drupal_set_message(t('Style %name has been imported.', array('%name' => $style['name'])));
} }
else { else {
drupal_set_message(t('Style %name has been imported as %new_name.', array('%name' => $style['name'], '%new_name' => $new_style['name']))); drupal_set_message(t('Style %name has been imported as %new_name.', array(
'%name' => isset($style['label']) ? $style['label'] : $style['name'],
'%new_name' => isset($new_style['label']) ? $new_style['label'] : $new_style['name'])));
} }
drupal_goto('admin/config/media/image-styles'); drupal_goto('admin/config/media/image-styles');
} }
/**
* Serializes image style data so it can be exported.
*
* @param array $style
* An image style array.
*
* @return string
* The serialized image style. Keys that are not needed for import are not
* serialized.
*/
function image_styles_admin_export_serialize($style) {
$style = array_intersect_key($style, array('name' => 0, 'label' => 0, 'effects' => 0));
foreach ($style['effects'] as &$effect) {
$effect = array_intersect_key($effect, array('weight' => 0, 'name' => 0, 'data' => 0));
}
array_walk_recursive($style, function(&$value) {
if (is_string($value)) {
$value = image_styles_admin_unify_newlines($value);
}
});
return serialize($style);
}
/**
* Unifies newlines in the string to the Unix newline standard.
*
* #2636314: textareas may convert newlines to the underlying OS style: convert
* all new lines to Unix style before unserializing. As string length is in the
* serialized data, we must ensure that we also do this on each array value
* before serializing.
*
* @param string $str
*
* @return string
*/
function image_styles_admin_unify_newlines($str) {
$str = str_replace("\r\n", "\n", $str);
$str = str_replace("\r", "\n", $str);
return $str;
}
/** /**
* Unserializes and validates a string into image style data. * Unserializes and validates a string into image style data.
* *
@ -154,25 +230,31 @@ function image_styles_admin_import_form_submit($form, &$form_state) {
* image style data. * image style data.
*/ */
function image_styles_admin_import_extract_style($import) { function image_styles_admin_import_extract_style($import) {
$style = @unserialize($import); $style = unserialize($import);
// Check if the contents of the textarea could be unserialized into an array. // Check if the contents of the textarea could be unserialized into an array.
if (!is_array($style)) { if (!is_array($style)) {
return FALSE; return FALSE;
} }
// Check if the required keys are available, we will ignore the other.
$style = array_intersect_key($style, array('name' => 0, 'effects' => 0)); // Filter out keys that we do not process.
if (count($style) !== 2) { $style = array_intersect_key($style, array('name' => 0, 'label' => 0, 'effects' => 0));
// 'name' is required and must be "machine name" string.
if (!isset($style['name']) || !is_string($style['name']) || preg_match('/[0-9a-z_\-]+/', $style['name']) !== 1) {
return FALSE; return FALSE;
} }
// 'name' must be "machine name" string
if (!is_string($style['name']) || preg_match('/[0-9a-z_\-]+/', $style['name']) !== 1) { // Optional 'label' must be a string.
if (isset($style['label']) && !is_string($style['label'])) {
return FALSE; return FALSE;
} }
// 'effects' must be an array
if (!is_array($style['effects'])) { // 'effects' is required and must be an array.
if (!isset($style['effects']) || !is_array($style['effects'])) {
return FALSE; return FALSE;
} }
// Check effects elements // Check effects elements
foreach ($style['effects'] as &$effect) { foreach ($style['effects'] as &$effect) {
// an effect must be an array. // an effect must be an array.
@ -197,6 +279,7 @@ function image_styles_admin_import_extract_style($import) {
return FALSE; return FALSE;
} }
} }
// @todo: are there any security implications for creating styles like this? // @todo: are there any security implications for creating styles like this?
// - Unserialize() is save in itself: it only creates data (except possibly // - Unserialize() is save in itself: it only creates data (except possibly
// for__wakeup(), but that can only be in already existing code: safe // for__wakeup(), but that can only be in already existing code: safe
@ -209,14 +292,14 @@ function image_styles_admin_import_extract_style($import) {
// contain invalid values. This is acceptable as it can also be done by // contain invalid values. This is acceptable as it can also be done by
// operating directly on the database. In Drupal this is not normally // operating directly on the database. In Drupal this is not normally
// checked for during processing: error messages will make clear that the // checked for during processing: error messages will make clear that the
// data has been played with. Can incorrect dat be abused? It may contain: // data has been played with. Can incorrect data be abused? It may contain:
// - incorrect types: we do not know the data structure of the various // - incorrect types: we do not know the data structure of the various
// effects, so we cannot check that and have to accept it as it comes. // effects, so we cannot check that and have to accept it as it comes.
// Effects should check_plain in summary theme and convert to int/float // Effects should check_plain in summary theme and convert to int/float
// whenever possible befoire using it in commands. // whenever possible before using it in commands.
// - PHP code, but that may be valid content for the text or custom effects: // - PHP code, but that may be valid content for the text or custom effects:
// Effects should check_plain in summary theme and convert to int/float // Effects should check_plain in summary theme and convert to int/float
// whenever possible befoire using it in commands. // whenever possible before using it in commands.
// @todo: if the style contains an effect that contains PHP code, the user // @todo: if the style contains an effect that contains PHP code, the user
// should need the 'use PHP for settings' permission. // should need the 'use PHP for settings' permission.
// - HTML and or JS code: when used as parameter, this normally won't hurt. // - HTML and or JS code: when used as parameter, this normally won't hurt.

View File

@ -1,13 +1,13 @@
name = Image styles admin name = Image styles admin
description = Provides additional administrative image style functionality. description = Provides additional administrative functionality to duplicate, export or import image styles.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = image dependencies[] = image
; Information added by drupal.org packaging script on 2012-12-04 ; Information added by Drupal.org packaging script on 2018-03-20
version = "7.x-1.1" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -0,0 +1,19 @@
(function($) {
"use strict";
Drupal.behaviors.imageStylesAdmin = {
attach: function(context) {
$('#image-styles').find('th.expand-all').once('expand-all', function() {
$(this).click(function() {
$('#image-styles').find('td.description .inner.expand').addClass('expanded');
});
});
$('#image-styles').find('td.description').once('description', function() {
$('.inner.expand', $(this)).click(function() {
$(this).toggleClass('expanded');
});
});
}
};
})(jQuery);

View File

@ -41,36 +41,122 @@ function image_styles_admin_menu() {
* Implements hook_preprocess_HOOK for theme image_style_list. * Implements hook_preprocess_HOOK for theme image_style_list.
*/ */
function image_styles_admin_preprocess_image_style_list(&$variables) { function image_styles_admin_preprocess_image_style_list(&$variables) {
// Tell imagecache_actions_preprocess_image_style_list to preprocess the next // Sort the image styles by name.
// call to theme_table() uasort($variables['styles'], function ($a, $b) {
$flag = TRUE; return strcasecmp($a['label'], $b['label']);
image_styles_admin_preprocess_table($flag); });
// Tell imagecache_actions_preprocess_table to preprocess the next call to
// theme_table().
$image_styles = array_values($variables['styles']);
image_styles_admin_preprocess_table($image_styles);
// Add CSS and JS files.
drupal_add_css(drupal_get_path('module', 'image_styles_admin') . '/image_styles_admin.css');
if (base_path() !== '/') {
$base_path = base_path();
drupal_add_css("
#image-styles .expand.inner { background-image: url($base_path/misc/menu-collapsed.png) }
#image-styles .expanded.expand.inner { background-image: url($base_path/misc/menu-expanded.png) }",
array('type' => 'inline'));
}
drupal_add_js(drupal_get_path('module', 'image_styles_admin') . '/image_styles_admin.js');
} }
/** /**
* Implements hook_preprocess_HOOK for theme table. * Implements hook_preprocess_HOOK for theme table.
*/ */
function image_styles_admin_preprocess_table(&$variables) { function image_styles_admin_preprocess_table(&$variables) {
static $is_in_image_style_list = FALSE; static $image_styles = NULL;
if (is_bool($variables)) { // If called from image_styles_admin_preprocess_image_style_list(), the
// Called from imagecache_actions_style_duplicate(): set flag // parameter will be a sequential array.
$is_in_image_style_list = $variables; if (key($variables) === 0) {
$image_styles = $variables;
} }
else if ($is_in_image_style_list) { else if (!empty($image_styles)) {
// Normal preprocess hook call: only process if theme('table', ...) has been // Normal preprocess hook call: we only process if theme('table', ...) has
// called by theme_image_style_list() // been called via theme_image_style_list() and we have a non empty list of
$variables['header'][2]['colspan'] = 4; // styles;
foreach ($variables['rows'] as &$row) {
array_splice($row, 2, 0, array($row[2], $row[2])); // Set an ID on the table so it can be targeted by our CSS.
// Replace edit with duplicate in text and href $variables['attributes']['id'] = 'image-styles';
$row[3] = str_replace('>' . t('edit') . '<', '>' . t('duplicate') . '<', $row[3]);
$row[3] = preg_replace('/\/edit\/([-a-z0-9_]+)"/', '/duplicate/\1"', $row[3]); // Add a class to the Style name and Settings columns for styling.
// Replace edit with export in text and href foreach ($variables['header'] as &$cell) {
$row[4] = str_replace('>' . t('edit') . '<', '>' . t('export') . '<', $row[4]); $temp_cell = is_string($cell) ? array('data' => $cell) : $cell;
$row[4] = preg_replace('/\/edit\/([-a-z0-9_]+)"/', '/export/\1"', $row[4]); $class_names = array(
'style-name' => t('Style name'),
'settings' => t('Settings'),
);
foreach ($class_names as $class => $name) {
if ($temp_cell['data'] == $name) {
$temp_cell['class'][] = $class;
$cell = $temp_cell;
}
}
} }
// Add the effects column header.
array_splice($variables['header'], 1, 0, array(array(
'data' => t('Effects') . ' <span class="description expand" role="button">(' . t('expand all') . ')</span>',
'class' => array('effects expand-all')
)));
// Add a column with a summary of all effects to each row.
foreach ($variables['rows'] as $i => &$row) {
$style = $image_styles[$i];
$effects_list = array();
foreach ($style['effects'] as $key => $effect) {
$definition = image_effect_definition_load($effect['name']);
$effect = array_merge($definition, $effect);
$style['effects'][$key] = $effect;
$effect_details = isset($effect['summary theme']) ? theme($effect['summary theme'], array('data' => $effect['data'])) : '';
$effects_list[] = '<span class="details">' . $effect['label'] . ' ' . $effect_details . '</span>';
}
// Add the effects summary column to the row.
$effects_summary = array(
'data' => '<div class="inner expand" role="button">' . implode('<span class="separator">, </span>', $effects_list) . '</div>',
'class' => 'description'
);
array_splice($row, 1, 0, array($effects_summary));
}
// Find the column with the edit link in it.
$i = 0;
$first_row = reset($variables['rows']);
foreach ($first_row as $i => &$cell) {
$cell_data = is_array($cell) ? $cell['data'] : $cell;
if (strpos($cell_data, '>' . t('edit') . '<') !== FALSE) {
break;
}
}
// Increase the colspan for the column with the edit link to include the
// duplicate and export links as well. This *should* be 2, but Drupal core
// specifies 1 more than should be there.
$variables['header'][$i]['colspan'] += 1;
// Add the 2 links to each row by duplicating the edit link and then
// changing the text and the link.
$edit_column = $i;
foreach ($variables['rows'] as &$row) {
$i = $edit_column;
// Duplicate the edit link twice.
array_splice($row, $i + 1, 0, array($row[$i], $row[$i]));
// Replace edit with duplicate in text and href
$i++;
$row[$i] = str_replace('>' . t('edit') . '<', '>' . t('duplicate') . '<', $row[$i]);
$row[$i] = preg_replace('#/admin/config/media/image-styles/edit/#', '/admin/config/media/image-styles/duplicate/', $row[$i]);
// Replace edit with export in text and href
$i++;
$row[$i] = str_replace('>' . t('edit') . '<', '>' . t('export') . '<', $row[$i]);
$row[$i] = preg_replace('#/admin/config/media/image-styles/edit/#', '/admin/config/media/image-styles/export/', $row[$i]);
}
// Don't preprocess subsequent calls to theme_table(). // Don't preprocess subsequent calls to theme_table().
$is_in_image_style_list = FALSE; $image_styles = NULL;
} }
} }

View File

@ -1,9 +0,0 @@
<?php
/**
* Warn about the soft dependency on system steam wrapper module
*/
function imagecache_actions_update_7001(&$sandbox) {
//$t = get_t();
drupal_set_message(t("Imagecache Actions: If you use the module:// notation anywhere in an image effect, you must now install the !module module.",
array('!module' => l('System Stream Wrapper', 'http://drupal.org/project/system_stream_wrapper', array('external' => TRUE)))), 'warning');
}

View File

@ -1,16 +1,13 @@
name = Imagecache Actions name = Imagecache Actions
description = Provides a number of additional image effects. description = Provides utility code for a number of additional image effects that can be found in the sub modules.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = image dependencies[] = image
; files with classes ; Information added by Drupal.org packaging script on 2018-03-20
files[] = ImageCacheActionsModuleStreamWrapper.php version = "7.x-1.9"
; Information added by drupal.org packaging script on 2012-12-04
version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -0,0 +1,16 @@
<?php
/**
* Warn about the soft dependency on system stream wrapper module.
*/
function imagecache_actions_update_7001(&$sandbox) {
drupal_set_message(t("Imagecache Actions: If you use the module:// notation anywhere in an image effect, you must now install the !module module.",
array('!module' => l('System Stream Wrapper', 'https://drupal.org/project/system_stream_wrapper', array('external' => TRUE)))), 'warning');
}
/**
* Clear image styles cache.
*/
function imagecache_actions_update_7002(&$sandbox) {
// We need to clear this cache as many effect callbacks have been renamed.
cache_clear_all('image_styles', 'cache');
}

View File

@ -4,37 +4,9 @@
(function($){ (function($){
/** /**
* Check if independent corners are enabled and disable other fields in the UI * None here at the moment :-)
* Conditional form logic was moved to drupal FAPI #states instead.
*/ */
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); })(jQuery);

View File

@ -9,10 +9,6 @@
*/ */
function imagecache_actions_theme() { function imagecache_actions_theme() {
return array( return array(
'imagecacheactions_rgb_form' => array(
'file' => 'utility-color.inc',
'render element' => 'form',
),
'imagecacheactions_rgb' => array( 'imagecacheactions_rgb' => array(
'file' => 'utility-color.inc', 'file' => 'utility-color.inc',
'variables' => array('rgb' => NULL), 'variables' => array('rgb' => NULL),

View File

@ -1,73 +1,99 @@
<?php <?php
//include this file whenever you have to use imageconvolution… /**
//you can use in your project, but keep the comment below * @file Include this file whenever you have to use imageconvolution.
//great for any image manipulation library *
//Made by Chao Xu(Mgccl) 3/1/07 * You can use in your project, but keep the comment below:
//www.webdevlogs.com * Great for any image manipulation library
//V 1.0 * Made by Chao Xu(Mgccl) 3/1/07
* www.webdevlogs.com
* V 1.0
*/
if (!function_exists('imagefilter')) { if (!function_exists('imagefilter')) {
function imagefilter($source, $var, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { /**
#define('IMAGE_FILTER_NEGATE', 0); * Applies a filter to an image.
#define('IMAGE_FILTER_GRAYSCALE', 1); *
#define('IMAGE_FILTER_BRIGHTNESS', 2); * @link http://php.net/manual/en/function.imagefilter.php
#define('IMAGE_FILTER_CONTRAST', 3); *
#define('IMAGE_FILTER_COLORIZE', 4); * @param resource $image
#define('IMAGE_FILTER_EDGEDETECT', 5); * @param int $filtertype
#define('IMAGE_FILTER_EMBOSS', 6); * One of the IMG_FILTER_ constants
#define('IMAGE_FILTER_GAUSSIAN_BLUR', 7); * @param int $arg1
#define('IMAGE_FILTER_SELECTIVE_BLUR', 8); * [optional] IMG_FILTER_BRIGHTNESS: Brightness level.
#define('IMAGE_FILTER_MEAN_REMOVAL', 9); * @param int $arg2
#define('IMAGE_FILTER_SMOOTH', 10); * [optional] IMG_FILTER_COLORIZE: Value of green component.
$max_y = imagesy($source); * @param int $arg3
$max_x = imagesx($source); * [optional] IMG_FILTER_COLORIZE: Value of blue component.
switch ($var) { * param int $arg4
* [optional] IMG_FILTER_COLORIZE: Alpha channel, A value between 0 and 127.
* 0 indicates completely opaque while 127 indicates completely transparent.
* This implementation does not use this parameter.
*
* @return bool
* true on success or false on failure.
*/
function imagefilter($image, $filtertype, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) {
// define('IMAGE_FILTER_NEGATE', 0);
// define('IMAGE_FILTER_GRAYSCALE', 1);
// define('IMAGE_FILTER_BRIGHTNESS', 2);
// define('IMAGE_FILTER_CONTRAST', 3);
// define('IMAGE_FILTER_COLORIZE', 4);
// define('IMAGE_FILTER_EDGEDETECT', 5);
// define('IMAGE_FILTER_EMBOSS', 6);
// define('IMAGE_FILTER_GAUSSIAN_BLUR', 7);
// define('IMAGE_FILTER_SELECTIVE_BLUR', 8);
// define('IMAGE_FILTER_MEAN_REMOVAL', 9);
// define('IMAGE_FILTER_SMOOTH', 10);
// define('IMG_FILTER_PIXELATE', 11);
$max_y = imagesy($image);
$max_x = imagesx($image);
switch ($filtertype) {
case 0: case 0:
$y = 0; $y = 0;
while ($y < $max_y) { while ($y < $max_y) {
$x = 0; $x = 0;
while ($x < $max_x) { while ($x < $max_x) {
$rgb = imagecolorat($source, $x, $y); $rgb = imagecolorat($image, $x, $y);
$r = 255 - (($rgb >> 16) & 0xFF); $r = 255 - (($rgb >> 16) & 0xFF);
$g = 255 - (($rgb >> 8) & 0xFF); $g = 255 - (($rgb >> 8) & 0xFF);
$b = 255 - ($rgb & 0xFF); $b = 255 - ($rgb & 0xFF);
$a = $rgb >> 24; $a = $rgb >> 24;
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a); $new_pxl = imagecolorallocatealpha($image, $r, $g, $b, $a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a); $new_pxl = imagecolorclosestalpha($image, $r, $g, $b, $a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
++$x; ++$x;
} }
++$y; ++$y;
} }
return TRUE; return TRUE;
break;
case 1: case 1:
$y = 0; $y = 0;
while ($y < $max_y) { while ($y < $max_y) {
$x = 0; $x = 0;
while ($x < $max_x) { while ($x < $max_x) {
$rgb = imagecolorat($source, $x, $y); $rgb = imagecolorat($image, $x, $y);
$a = $rgb >> 24; $a = $rgb >> 24;
$r = ((($rgb >> 16) & 0xFF) * 0.299) + ((($rgb >> 8) & 0xFF) * 0.587) + (($rgb & 0xFF) * 0.114); $r = ((($rgb >> 16) & 0xFF) * 0.299) + ((($rgb >> 8) & 0xFF) * 0.587) + (($rgb & 0xFF) * 0.114);
$new_pxl = imagecolorallocatealpha($source, $r, $r, $r, $a); $new_pxl = imagecolorallocatealpha($image, $r, $r, $r, $a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, $r, $r, $r, $a); $new_pxl = imagecolorclosestalpha($image, $r, $r, $r, $a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
++$x; ++$x;
} }
++$y; ++$y;
} }
return TRUE; return TRUE;
break;
case 2: case 2:
$y = 0; $y = 0;
while ($y < $max_y) { while ($y < $max_y) {
$x = 0; $x = 0;
while ($x < $max_x) { while ($x < $max_x) {
$rgb = imagecolorat($source, $x, $y); $rgb = imagecolorat($image, $x, $y);
$r = (($rgb >> 16) & 0xFF) + $arg1; $r = (($rgb >> 16) & 0xFF) + $arg1;
$g = (($rgb >> 8) & 0xFF) + $arg1; $g = (($rgb >> 8) & 0xFF) + $arg1;
$b = ($rgb & 0xFF) + $arg1; $b = ($rgb & 0xFF) + $arg1;
@ -75,24 +101,24 @@ if (!function_exists('imagefilter')) {
$r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r); $r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r);
$g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g); $g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g);
$b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b); $b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a); $new_pxl = imagecolorallocatealpha($image, $r, $g, $b, $a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a); $new_pxl = imagecolorclosestalpha($image, $r, $g, $b, $a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
++$x; ++$x;
} }
++$y; ++$y;
} }
return TRUE; return TRUE;
break;
case 3: case 3:
$contrast = pow((100 - $arg1) / 100, 2); $contrast = pow((100 - $arg1) / 100, 2);
$y = 0; $y = 0;
while ($y < $max_y) { while ($y < $max_y) {
$x = 0; $x = 0;
while ($x < $max_x) { while ($x < $max_x) {
$rgb = imagecolorat($source, $x, $y); $rgb = imagecolorat($image, $x, $y);
$a = $rgb >> 24; $a = $rgb >> 24;
$r = (((((($rgb >> 16) & 0xFF) / 255) - 0.5) * $contrast) + 0.5) * 255; $r = (((((($rgb >> 16) & 0xFF) / 255) - 0.5) * $contrast) + 0.5) * 255;
$g = (((((($rgb >> 8) & 0xFF) / 255) - 0.5) * $contrast) + 0.5) * 255; $g = (((((($rgb >> 8) & 0xFF) / 255) - 0.5) * $contrast) + 0.5) * 255;
@ -100,23 +126,23 @@ if (!function_exists('imagefilter')) {
$r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r); $r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r);
$g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g); $g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g);
$b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b); $b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a); $new_pxl = imagecolorallocatealpha($image, $r, $g, $b, $a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a); $new_pxl = imagecolorclosestalpha($image, $r, $g, $b, $a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
++$x; ++$x;
} }
++$y; ++$y;
} }
return TRUE; return TRUE;
break;
case 4: case 4:
$x = 0; $x = 0;
while ($x < $max_x) { while ($x < $max_x) {
$y = 0; $y = 0;
while ($y < $max_y) { while ($y < $max_y) {
$rgb = imagecolorat($source, $x, $y); $rgb = imagecolorat($image, $x, $y);
$r = (($rgb >> 16) & 0xFF) + $arg1; $r = (($rgb >> 16) & 0xFF) + $arg1;
$g = (($rgb >> 8) & 0xFF) + $arg2; $g = (($rgb >> 8) & 0xFF) + $arg2;
$b = ($rgb & 0xFF) + $arg3; $b = ($rgb & 0xFF) + $arg3;
@ -124,55 +150,55 @@ if (!function_exists('imagefilter')) {
$r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r); $r = ($r > 255) ? 255 : (($r < 0) ? 0 : $r);
$g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g); $g = ($g > 255) ? 255 : (($g < 0) ? 0 : $g);
$b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b); $b = ($b > 255) ? 255 : (($b < 0) ? 0 : $b);
$new_pxl = imagecolorallocatealpha($source, $r, $g, $b, $a); $new_pxl = imagecolorallocatealpha($image, $r, $g, $b, $a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, $r, $g, $b, $a); $new_pxl = imagecolorclosestalpha($image, $r, $g, $b, $a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
++$y; ++$y;
} }
++$x; ++$x;
} }
return TRUE; return TRUE;
break;
case 5: case 5:
return imageconvolution($source, array( return imageconvolution($image, array(
array(-1, 0, -1), array(-1, 0, -1),
array(0, 4, 0), array(0, 4, 0),
array(-1, 0, -1) array(-1, 0, -1),
), 1, 127); ), 1, 127);
break;
case 6: case 6:
return imageconvolution($source, array( return imageconvolution($image, array(
array(1.5, 0, 0), array(1.5, 0, 0),
array(0, 0, 0), array(0, 0, 0),
array(0, 0, -1.5) array(0, 0, -1.5),
), 1, 127); ), 1, 127);
break;
case 7: case 7:
return imageconvolution($source, array( return imageconvolution($image, array(
array(1, 2, 1), array(1, 2, 1),
array(2, 4, 2), array(2, 4, 2),
array(1, 2, 1) array(1, 2, 1),
), 16, 0); ), 16, 0);
break;
case 8: case 8:
for ($y = 0; $y < $max_y; $y++) { for ($y = 0; $y < $max_y; $y++) {
for ($x = 0; $x < $max_x; $x++) { for ($x = 0; $x < $max_x; $x++) {
$flt_r_sum = $flt_g_sum = $flt_b_sum = 0; $flt_r_sum = $flt_g_sum = $flt_b_sum = 0;
$cpxl = imagecolorat($source, $x, $y); $cpxl = imagecolorat($image, $x, $y);
for ($j = 0; $j < 3; $j++) { for ($j = 0; $j < 3; $j++) {
for ($i = 0; $i < 3; $i++) { for ($i = 0; $i < 3; $i++) {
if (($j == 1) && ($i == 1)) { if (($j == 1) && ($i == 1)) {
$flt_r[1][1] = $flt_g[1][1] = $flt_b[1][1] = 0.5; $flt_r[1][1] = $flt_g[1][1] = $flt_b[1][1] = 0.5;
} }
else { else {
$pxl = imagecolorat($source, $x - (3 >> 1) + $i, $y - (3 >> 1) + $j); $pxl = imagecolorat($image, $x - (3 >> 1) + $i, $y - (3 >> 1) + $j);
$new_a = $pxl >> 24; $new_a = $pxl >> 24;
//$r = (($pxl >> 16) & 0xFF); // $r = (($pxl >> 16) & 0xFF);
//$g = (($pxl >> 8) & 0xFF); // $g = (($pxl >> 8) & 0xFF);
//$b = ($pxl & 0xFF); // $b = ($pxl & 0xFF);
$new_r = abs((($cpxl >> 16) & 0xFF) - (($pxl >> 16) & 0xFF)); $new_r = abs((($cpxl >> 16) & 0xFF) - (($pxl >> 16) & 0xFF));
if ($new_r != 0) { if ($new_r != 0) {
$flt_r[$j][$i] = 1 / $new_r; $flt_r[$j][$i] = 1 / $new_r;
@ -222,7 +248,7 @@ if (!function_exists('imagefilter')) {
for ($j = 0; $j < 3; $j++) { for ($j = 0; $j < 3; $j++) {
for ($i = 0; $i < 3; $i++) { for ($i = 0; $i < 3; $i++) {
$pxl = imagecolorat($source, $x - (3 >> 1) + $i, $y - (3 >> 1) + $j); $pxl = imagecolorat($image, $x - (3 >> 1) + $i, $y - (3 >> 1) + $j);
$new_r += (($pxl >> 16) & 0xFF) * $flt_r[$j][$i]; $new_r += (($pxl >> 16) & 0xFF) * $flt_r[$j][$i];
$new_g += (($pxl >> 8) & 0xFF) * $flt_g[$j][$i]; $new_g += (($pxl >> 8) & 0xFF) * $flt_g[$j][$i];
$new_b += ($pxl & 0xFF) * $flt_b[$j][$i]; $new_b += ($pxl & 0xFF) * $flt_b[$j][$i];
@ -232,29 +258,35 @@ if (!function_exists('imagefilter')) {
$new_r = ($new_r > 255) ? 255 : (($new_r < 0) ? 0 : $new_r); $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_g = ($new_g > 255) ? 255 : (($new_g < 0) ? 0 : $new_g);
$new_b = ($new_b > 255) ? 255 : (($new_b < 0) ? 0 : $new_b); $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); $new_pxl = imagecolorallocatealpha($image, (int) $new_r, (int) $new_g, (int) $new_b, $new_a);
if ($new_pxl == FALSE) { if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($source, (int) $new_r, (int) $new_g, (int) $new_b, $new_a); $new_pxl = imagecolorclosestalpha($image, (int) $new_r, (int) $new_g, (int) $new_b, $new_a);
} }
imagesetpixel($source, $x, $y, $new_pxl); imagesetpixel($image, $x, $y, $new_pxl);
} }
} }
return TRUE; return TRUE;
break;
case 9: case 9:
return imageconvolution($source, array( return imageconvolution($image, array(
array(-1, -1, -1), array(-1, -1, -1),
array(-1, 9, -1), array(-1, 9, -1),
array(-1, -1, -1) array(-1, -1, -1),
), 1, 0); ), 1, 0);
break;
case 10: case 10:
return imageconvolution($source, array( return imageconvolution($image, array(
array(1, 1, 1), array(1, 1, 1),
array(1, $arg1, 1), array(1, $arg1, 1),
array(1, 1, 1) array(1, 1, 1),
), $arg1 + 8, 0); ), $arg1 + 8, 0);
break;
case 11:
default:
watchdog('imagecache_actions', 'Image filter failed: unknown filter type: %filtertype',
array('%filtertype' => $filtertype),
WATCHDOG_ERROR);
return FALSE;
} }
} }
} }

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,15 +1,16 @@
name = Imagecache_actions Test Suite name = Imagecache_actions Test Suite
description = Displays a collection of demo presets. description = Displays a collection of demo image styles.
package = Media package = Media
core = 7.x core = 7.x
dependencies[] = imagecache_actions dependencies[] = imagecache_actions
dependencies[] = system_stream_wrapper
features[image][] = 'corners_combo' features[image][] = 'corners_combo'
; Information added by drupal.org packaging script on 2012-12-04 ; Information added by Drupal.org packaging script on 2018-03-20
version = "7.x-1.1" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "imagecache_actions" project = "imagecache_actions"
datestamp = "1354653754" datestamp = "1521550387"

View File

@ -1,16 +1,12 @@
<?php <?php
/** /**
* @file An admin-only utility to demo and check a number of imagecache presets * @file An admin-only utility to test image styles and effects.
* and actions.
*
* It provides a tab in Home > Administer > Site Building > Imagecache
*
* that lists a whole bunch of sample presets.
*
* @author dman http://coders.co.nz/
*
* *
* It provides a page Test Suite in Administration > Configuration > Media >
* Image Styles (admin/config/media/image-styles/testsuite) that displays
* results for all existing image styles for all toolkits as well as for a set
* of test image styles defined in the various modules.
*/ */
include_once('imagecache_testsuite.features.inc'); include_once('imagecache_testsuite.features.inc');
@ -22,8 +18,8 @@ function imagecache_testsuite_menu() {
$items = array(); $items = array();
$items['admin/config/media/image-styles/testsuite'] = array( $items['admin/config/media/image-styles/testsuite'] = array(
'title' => 'Test Suite', 'title' => 'Test Suite',
'page callback' => 'imagecache_testsuite_generate', 'page callback' => 'imagecache_testsuite_page',
'access arguments' => array('administer imagecache'), 'access arguments' => array('administer image styles'),
'type' => MENU_LOCAL_TASK, 'type' => MENU_LOCAL_TASK,
'weight' => 10, 'weight' => 10,
); );
@ -31,13 +27,13 @@ function imagecache_testsuite_menu() {
'title' => 'Test Suite Image', 'title' => 'Test Suite Image',
'page callback' => 'imagecache_testsuite_generate', 'page callback' => 'imagecache_testsuite_generate',
'page arguments' => array(5, 6), 'page arguments' => array(5, 6),
'access arguments' => array('administer imagecache'), 'access arguments' => array('administer image styles'),
'type' => MENU_CALLBACK, 'type' => MENU_CALLBACK,
); );
$items['admin/config/media/image-styles/testsuite/positioning_test'] = array( $items['admin/config/media/image-styles/testsuite/positioning_test'] = array(
'title' => 'Positioning Test', 'title' => 'Positioning Test',
'page callback' => 'imagecache_testsuite_positioning', 'page callback' => 'imagecache_testsuite_positioning',
'access arguments' => array('administer imagecache'), 'access arguments' => array('administer image styles'),
'type' => MENU_LOCAL_TASK, 'type' => MENU_LOCAL_TASK,
); );
return $items; return $items;
@ -46,187 +42,278 @@ function imagecache_testsuite_menu() {
/** /**
* Implementation of hook_help() * Implementation of hook_help()
*/ */
function imagecache_testsuite_help($path, $arg) { function imagecache_testsuite_help($path /*, $arg*/) {
switch ($path) { switch ($path) {
// @todo: this path does not exist anymore.
case 'admin/build/imagecache/test' : case 'admin/build/imagecache/test' :
$output = file_get_contents(drupal_get_path('module', 'imagecache_testsuite') ."/README.txt"); $output = file_get_contents(drupal_get_path('module', 'imagecache_testsuite') . "/README.txt");
return _filter_autop($output); return _filter_autop($output);
break; break;
case 'admin/build/imagecache/test' : case 'admin/config/media/image-styles/testsuite' :
return t('This displays a number of examples of keyword positioning. This positioning algorithm is used when placing image overlays, such as watermarks or text on a base image canvas. Illustrated are both the expected result and the actual result. This page is just for debugging to confirm that this behavior doesnt change as the code gets updated. If the two illustrations do not match, there is probably something wrong with the calculation logic.'); return t("<p>
break; This page displays a number of examples of image effects.
Illustrated are both the expected result and the actual result.
</p><p>
This page is just for debugging to confirm that this behavior doesn't
change as the code gets updated.
If the two illustrations do not match, there is probably something
that needs fixing.
</p><p>
More actions are provided by each of the imagecache actions submodules
and will be shown as you enable them.
</p>");
break;
case 'admin/config/media/image-styles' :
return t('
A number of styles here are provided by the Imagecache
Testsuite module as examples.
Disable this module to make them go away.
');
break;
} }
return '';
} }
/** /**
* Either returns the whole testsuite page or generates the requested * Returns the test suite page.
* image+preset *
* The test suite page contians img links to all image derivatives to create as
* part of the test suite.
*
* Samples to test are scanned from:
* - The existing image styles.
* - The file features.inc attached to this module. (@todo: no longer eisting?)
* - Individual *.imagecache_preset.inc files found near any known modules.
* Images illustrating the named preset are looked for also.
* *
* Flushes the entire test cache every time anything is done. * Flushes the entire test cache every time anything is done.
*
* @return string
* The html for the page.
*/ */
function imagecache_testsuite_generate($test_id = '', $toolkit = 'gd') { function imagecache_testsuite_page() {
// Samples to test are scanned from
// - the existing installed presets
// - features inc attached to this module
// - individual *.imagecache_preset.inc files found near any known modules
// Images illustrating the named preset are looked for also.
module_load_include('inc', 'image', 'image.admin'); module_load_include('inc', 'image', 'image.admin');
module_load_include('inc', 'image', 'image.effects'); module_load_include('inc', 'image', 'image.effects');
$sample_path = drupal_get_path('module' , 'imagecache_testsuite') ; $tests = array_merge(image_styles(), imagecache_testsuite_get_tests());
$target = $sample_path .'/sample.jpg'; $toolkits = image_get_available_toolkits();
$tests = image_styles() + imagecache_testsuite_get_tests();
$toolkits = module_invoke_all('image_toolkits');
if (empty($test_id)) { // Present the all-in-one overview page.
// Present the all-in-one overview page $sample_folders = imagecache_testsuite_get_folders();
$sample_folders = imagecache_testsuite_get_folders();
// Firstly, remove any previous images // Draw the admin table.
image_style_flush('testsuite'); $test_table = array();
foreach ($tests as $style_name => $style) {
// Firstly, remove any previous images for the current style
image_style_flush($style);
// Draw the admin table $row = array();
$test_table = array(); $row_class = 'test';
foreach ($tests as $style_name => $style) { $details_list = array();
$row = array();
$row_class = 'test';
$details = '';
// Render the details
foreach ($style['effects'] as $i => $effect) {
if (! isset($effect['name'])) {
// badness
watchdog('imagecache_testsuite', 'invalid testcase within %style_name. No effect name', array('%style_name' => $style_name), WATCHDOG_ERROR);
$details .= '<div>Unidentified effect</div>';
continue;
}
#$effect_definition = image_effect_definition_load($effect['name'], $style['name']);
$effect_definition = image_effect_definition_load($effect['name']);
if (function_exists($effect_definition['effect callback'])) {
$description = "<strong>{$effect_definition['label']}</strong> ";
$description .= isset($effect_definition['summary theme']) ? theme($effect_definition['summary theme'], array('data' => $effect['data'])) : '';
$details .= "<div>$description</div>";
}
else {
// Probably an action that requires a module that is not installed.
$details .= t("<div><b>Action %action Unavailable</b></div>", array('%action' => $effect['name']));
$row_class = 'error';
}
}
$row['details'] = "<h3>{$style['name']}</h3><p>$details</p>";
// Look for a sample image. May also be defined by the definition itself,
// but normally assume a file named after the presetname, in the preset file path.
foreach ($sample_folders as $sample_folder) {
if (file_exists("{$sample_folder}/{$style_name}.png")) {
$style['sample'] = "{$sample_folder}/{$style_name}.png";
}
elseif (file_exists("{$sample_folder}/{$style_name}.jpg")) {
$style['sample'] = "{$sample_folder}/{$style_name}.jpg";
}
}
if (isset($style['sample']) && file_exists($style['sample']) ) {
$sample_img = theme('image', array('path' => $style['sample']));
// I was having trouble with permissions on an OSX dev machine
if (! is_readable($style['sample'])) {
$sample_img = "FILE UNREADABLE: {$style['sample']}";
}
}
else {
$sample_img = "[no sample]";
}
$row['sample'] = $sample_img;
// Generate a result for each available toolkit
foreach ($toolkits as $toolkit => $toolkit_info) {
$test_url = "admin/config/media/image-styles/testsuite/$style_name/$toolkit";
$test_img = theme('image', array(
'path' => $test_url,
'alt' => "$style_name/$toolkit"
));
$row[$toolkit] = l($test_img, $test_url, array('html' => TRUE));
}
$test_table[$style_name] = array('data' => $row, 'class' => array($row_class));
}
$header = array_merge(array('test', 'sample'), array_keys($toolkits));
$output = theme('table', array('header' => $header, 'rows' => $test_table, 'id' => 'imagecache-testsuite'));
// Default system zebra-striping fails to show my transparency on white
drupal_add_html_head('<style type="text/css" >#imagecache-testsuite tr.even{background-color:#EEEEEE !important;} #imagecache-testsuite td{vertical-align:top;} #imagecache-testsuite tr.error{background-color:#FFCCCC !important;}</style>');
return $output;
}
else {
// Run the process and return the image.
// @see image_style_create_derivative()
$style = $tests[$test_id];
if (! $style) {
trigger_error("Unknown test style preset '$test_id' ", E_USER_ERROR);
return FALSE;
}
// Start emulating image_style_create_derivative()
// The main difference being I determine the toolkit I want to use.
// SOME of this code is probably redundant, was a lot of copy&paste without true understanding of the new image.module
$image_uri = $target;
if (!$image = image_load($target, $toolkit)) {
trigger_error("Failed to open original image $target with toolkit $toolkit", E_USER_ERROR);
return FALSE;
}
// Need to save the result before returning it - to stay compatible with imagemagick
$filename = "$test_id-$toolkit.{$image->info['extension']}";
$derivative_uri = image_style_path($style['name'], $filename);
file_prepare_directory(dirname($derivative_uri), FILE_CREATE_DIRECTORY);
watchdog('imagecache_testsuite', 'Checking a save dir %dir', array('%dir' => dirname($derivative_uri)), WATCHDOG_DEBUG);
// Imagemagick is not quite right? place a file where the file is supposed to go
// before I put the real path there? else drupal_realpath() says nuh.
#file_save_data('touch this for imagemagick', $derivative_uri, FILE_EXISTS_REPLACE);
// Render the details.
foreach ($style['effects'] as $effect) { foreach ($style['effects'] as $effect) {
// Need to load the full effect definitions, our test ones don't know all the callback info if (!isset($effect['name'])) {
$effect_definition = image_effect_definition_load($effect['name']); // badness
if (empty($effect_definition)) { watchdog('imagecache_testsuite', 'invalid testcase within %style_name. No effect name', array('%style_name' => $style_name), WATCHDOG_ERROR);
watchdog('imagecache_testsuite', 'I have no idea what %name is', array('%name' => $full_effect['name']), WATCHDOG_ERROR); $details_list[] = '<div>Unidentified effect</div>';
$row_class = 'error';
continue; continue;
} }
$full_effect = array_merge($effect_definition, array('data' => $effect['data'])); $effect_definition = image_effect_definition_load($effect['name']);
if (function_exists($effect_definition['effect callback'])) {
$description = "<strong>{$effect_definition['label']}</strong> ";
$description .= isset($effect_definition['summary theme']) ? theme($effect_definition['summary theme'], array('data' => $effect['data'])) : '';
$details_list[] = "<div>$description</div>";
}
else {
// Probably an action that requires a module that is not installed.
$strings = array(
'%action' => $effect['name'],
'%module' => $effect['module'],
);
$details_list[$effect['name']] = t("<div><b>%action unavailable</b>. Please enable %module module.</div>", $strings);
$row_class = 'error';
}
}
$row['details'] = "<h3>{$style['name']}</h3><p>" . implode($details_list) . "</p>";
if (! image_effect_apply($image, $full_effect)) { // Look for a sample image. May also be defined by the definition itself,
watchdog('imagecache_testsuite', 'action: %action (%callback) failed for %src', array('%action' => $full_effect['label'], '%src' => $target, '%callback' => $full_effect['effect callback']), WATCHDOG_ERROR); // but normally assume a file named after the image style, in (one of the)
#return FALSE; // directories with test styles.
foreach ($sample_folders as $sample_folder) {
if (file_exists("{$sample_folder}/{$style_name}.png")) {
$style['sample'] = "{$sample_folder}/{$style_name}.png";
}
elseif (file_exists("{$sample_folder}/{$style_name}.jpg")) {
$style['sample'] = "{$sample_folder}/{$style_name}.jpg";
} }
} }
#watchdog('imagecache_testsuite', "processed $test_id-$toolkit, ready to save", array(), WATCHDOG_DEBUG); if (isset($style['sample']) && file_exists($style['sample'])) {
#watchdog('imagecache_testsuite', print_r($image, 1)); $sample_img = theme('image', array('path' => $style['sample']));
#dpm(get_defined_vars()); // I was having trouble with permissions on an OSX dev machine.
if (!image_save($image, $derivative_uri)) { if (!is_readable($style['sample'])) {
watchdog('imagecache_testsuite', 'saving image %label failed for %derivative_uri', array('%derivative_uri' => $derivative_uri, '%label' => $effect['label']), WATCHDOG_ERROR); $sample_img = "FILE UNREADABLE: {$style['sample']}";
return FALSE; }
} }
else {
$sample_img = "[no sample]";
}
$row['sample'] = $sample_img;
if ($result_image = image_load($derivative_uri)) { // Generate a result for each available toolkit.
#watchdog('imagecache_testsuite', 'transferring result', array(), WATCHDOG_DEBUG); foreach ($toolkits as $toolkit => $toolkit_info) {
file_transfer($result_image->source, array('Content-Type' => $result_image->info['mime_type'], 'Content-Length' => $result_image->info['file_size'])); $test_url = "admin/config/media/image-styles/testsuite/$style_name/$toolkit";
drupal_exit(); $test_img = theme('image', array(
'path' => $test_url,
'alt' => "$style_name/$toolkit"
));
$row[$toolkit] = l($test_img, $test_url, array('html' => TRUE));
} }
return "Failed to load the expected result from $derivative_uri"; $test_table[$style_name] = array(
'data' => $row,
'class' => array($row_class)
);
} }
$header = array_merge(array('test', 'sample'), array_keys($toolkits));
$output = theme('table', array(
'header' => $header,
'rows' => $test_table,
'id' => 'imagecache-testsuite'
));
// @todo: zebra striping can be disabled in D7.
// Default system zebra-striping fails to show my transparency on white.
drupal_add_html_head('<style type="text/css" >#imagecache-testsuite tr.even{background-color:#EEEEEE !important;} #imagecache-testsuite td{vertical-align:top;} #imagecache-testsuite tr.error{background-color:#FFCCCC !important;}</style>');
return $output;
} }
/**
* Returns the requested image derivative.
*
* If the image derivative generation is successful, the function does not
* return but exits processing using drupal_exit().
*
* Flushes the entire test cache every time anything is done.
*
* @param string $test_id
* The id of the test to generate the derivative for.
* @param string $toolkit
* The toolkit to use, or empty for the default toolkit
*
* @return string|bool
* - The html for the page ($test_id is empty)
* - False when the image derivative could not be created.
*/
function imagecache_testsuite_generate($test_id = '', $toolkit = '') {
module_load_include('inc', 'image', 'image.admin');
module_load_include('inc', 'image', 'image.effects');
if (empty($toolkit)) {
$toolkit = image_get_toolkit();
}
else {
// Set the toolkit for this invocation only, so do not use variable_set.
global $conf;
$conf['image_toolkit'] = $toolkit;
if ($toolkit === 'gd') {
// This seems not to be done automatically elsewhere.
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'image.gd.inc';
}
}
$target = 'module://imagecache_testsuite/sample.jpg';
$tests = array_merge(image_styles(), imagecache_testsuite_get_tests());
// Run the process and return the image, @see image_style_create_derivative().
$style = $tests[$test_id];
if (!$style) {
trigger_error("Unknown test style preset '$test_id' ", E_USER_ERROR);
return FALSE;
}
// @todo: should we let the image style system do its work and just interfere on hook_init with setting the toolkit?
// @todo: this would make the page generator easier as well and keep it working with secure image derivatives.
// Start emulating image_style_create_derivative()
// The main difference being I determine the toolkit I want to use.
// SOME of this code is probably redundant, was a lot of copy&paste without true understanding of the new image.module
if (!$image = image_load($target, $toolkit)) {
trigger_error("Failed to open original image $target with toolkit $toolkit", E_USER_ERROR);
return FALSE;
}
// Need to save the result before returning it - to stay compatible with imagemagick
$filename = "$test_id-$toolkit.{$image->info['extension']}";
$derivative_uri = image_style_path($style['name'], $filename);
$directory = dirname($derivative_uri);
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
watchdog('imagecache_testsuite', 'Checking a save dir %dir', array('%dir' => dirname($derivative_uri)), WATCHDOG_DEBUG);
// Imagemagick is not quite right? place a file where the file is supposed to go
// before I put the real path there? else drupal_realpath() says nuh.
#file_save_data('touch this for imagemagick', $derivative_uri, FILE_EXISTS_REPLACE);
foreach ($style['effects'] as $effect) {
// Need to load the full effect definitions, our test ones don't know all the callback info
$effect_definition = image_effect_definition_load($effect['name']);
if (empty($effect_definition)) {
watchdog('imagecache_testsuite', 'I have no idea what %name is', array('%name' => $effect['name']), WATCHDOG_ERROR);
continue;
}
$full_effect = array_merge($effect_definition, array('data' => $effect['data']));
// @todo: effects that involve other images (overlay, underlay) will load that image with the default toolkit which may differ from the toolkit tested here.
if (!image_effect_apply($image, $full_effect)) {
watchdog('imagecache_testsuite', 'action: %action (%callback) failed for %src', array(
'%action' => $full_effect['label'],
'%src' => $target,
'%callback' => $full_effect['effect callback']
), WATCHDOG_ERROR);
}
}
if (!image_save($image, $derivative_uri)) {
watchdog('imagecache_testsuite', 'saving image %label failed for %derivative_uri', array(
'%derivative_uri' => $derivative_uri,
'%label' => isset($style['label']) ? $style['label'] : $style['name']
), WATCHDOG_ERROR);
return FALSE;
}
if ($result_image = image_load($derivative_uri)) {
file_transfer($result_image->source, array(
'Content-Type' => $result_image->info['mime_type'],
'Content-Length' => $result_image->info['file_size']
));
drupal_exit();
}
return "Failed to load the expected result from $derivative_uri";
}
/** /**
* Implements hook_image_default_styles(). * Implements hook_image_default_styles().
* *
* Loads the individual test cases and makes them available as enabled styles * Lists all our individual test cases and makes them available
* as enabled styles
*/ */
function imagecache_testsuite_image_default_styles() { function imagecache_testsuite_image_default_styles() {
return imagecache_testsuite_get_tests(); $styles = imagecache_testsuite_get_tests();
// Need to filter out the invalid test cases
// (ones that use unavailable actions)
// or the core complains with notices.
// foreach ($styles as $id => $style) {
// foreach ($style['effects'] as $delta => $action) {
// if (!empty($action['module']) && ($action['module'] != 'imagecache') && !module_exists($action['module'])) {
// unset($styles[$id]);
// break;
// }
// }
// }
return $styles;
} }
/** /**
@ -250,7 +337,7 @@ function imagecache_testsuite_get_tests() {
// Setting filepath in this scope allows the tests to know where they are. // Setting filepath in this scope allows the tests to know where they are.
// The inc files may use it to create their rules. // The inc files may use it to create their rules.
$filepath = $folder; $filepath = $folder;
foreach($preset_files as $preset_file) { foreach ($preset_files as $preset_file) {
include_once($preset_file->uri); include_once($preset_file->uri);
} }
} }
@ -261,8 +348,8 @@ function imagecache_testsuite_get_tests() {
/** /**
* Places to scan for test presets and sample images. * Places to scan for test presets and sample images.
* *
* @return an array of foldernames of everything that implements * @return array
* imagecache_actions. * an array of folder names of everything that implements imagecache_actions.
*/ */
function imagecache_testsuite_get_folders() { function imagecache_testsuite_get_folders() {
$folders = array(drupal_get_path('module', 'imagecache_testsuite')); $folders = array(drupal_get_path('module', 'imagecache_testsuite'));
@ -272,7 +359,6 @@ function imagecache_testsuite_get_folders() {
return $folders; return $folders;
} }
/** /**
* Display a page demonstrating a number of positioning tests * Display a page demonstrating a number of positioning tests
* *
@ -280,13 +366,16 @@ function imagecache_testsuite_get_folders() {
* pls the css-like left=, top= version also. * pls the css-like left=, top= version also.
*/ */
function imagecache_testsuite_positioning() { function imagecache_testsuite_positioning() {
module_load_include('inc', 'imagecache_actions', 'utility');
drupal_set_title("Testing the positioning algorithm"); drupal_set_title("Testing the positioning algorithm");
$tests = imagecache_testsuite_positioning_get_tests(); $tests = imagecache_testsuite_positioning_get_tests();
$table = array(); $table = array();
// $dst_image represents tha field or canvas. // $dst_image represents tha field or canvas.
// $src_image is the item being placed on it. // $src_image is the item being placed on it.
// Both these represent an imageapi-type image resource handle, but contain just dimensions // Both these represent an imageapi-type image resource handle, but contain just dimensions
$src_image = new stdClass();
$src_image->info = array('width' => 75, 'height' => 100); $src_image->info = array('width' => 75, 'height' => 100);
$dst_image = new stdClass();
$dst_image->info = array('width' => 200, 'height' => 150); $dst_image->info = array('width' => 200, 'height' => 150);
foreach ($tests as $testname => $test) { foreach ($tests as $testname => $test) {
@ -310,18 +399,27 @@ function imagecache_testsuite_positioning() {
$row['result_image'] = $result_illustration; $row['result_image'] = $result_illustration;
$table[] = $row; $table[] = $row;
} }
return 'Result of test:'. theme('table', array('test', 'parameters', 'expected', 'image', 'result', 'actual image', 'status'), $table); return 'Result of test:' . theme('table', array(
'test',
'parameters',
'expected',
'image',
'result',
'actual image',
'status'
), $table);
} }
function theme_positioning_test($x, $y) { function theme_positioning_test($x, $y) {
$inner = "<div style='background-color:red; width:75px; height:100px; position:absolute; left:{$x}px; top:{$y}px'>"; $inner = "<div style='background-color:red; width:75px; height:100px; position:absolute; left:{$x}px; top:{$y}px'>";
$outer = "<div style='background-color:blue; width:200px; height:150px; position:absolute; left:25px; top:25px'><div style='position:relative'>$inner</div></div>"; $outer = "<div style='background-color:blue; width:200px; height:150px; position:absolute; left:25px; top:25px'><div style='position:relative'>$inner</div></div>";
$wrapper = "<div style='background-color:#CCCCCC; width:250px; height:200px; position:relative'>$outer</div>"; $wrapper = "<div style='background-color:#CCCCCC; width:250px; height:200px; position:relative'>$outer</div>";
return $wrapper; return $wrapper;
} }
function theme_positioning_parameters($parameters) { function theme_positioning_parameters($parameters) {
$outputs = array();
foreach ($parameters as $key => $value) { foreach ($parameters as $key => $value) {
$outputs[] = "[$key] => $value"; $outputs[] = "[$key] => $value";
} }
@ -396,7 +494,6 @@ function imagecache_testsuite_positioning_get_tests() {
'y' => '25', 'y' => '25',
), ),
), ),
'keyword with percent' => array( 'keyword with percent' => array(
'parameters' => array( 'parameters' => array(
'x' => 'right+10%', 'x' => 'right+10%',
@ -408,7 +505,6 @@ function imagecache_testsuite_positioning_get_tests() {
'y' => '85', 'y' => '85',
), ),
), ),
'css styles' => array( 'css styles' => array(
'parameters' => array( 'parameters' => array(
'left' => '10px', 'left' => '10px',
@ -420,7 +516,6 @@ function imagecache_testsuite_positioning_get_tests() {
'y' => '40', 'y' => '40',
), ),
), ),
'css negatives' => array( 'css negatives' => array(
'parameters' => array( 'parameters' => array(
'left' => '-10px', 'left' => '-10px',
@ -432,7 +527,6 @@ function imagecache_testsuite_positioning_get_tests() {
'y' => '60', 'y' => '60',
), ),
), ),
'css with percents' => array( 'css with percents' => array(
'parameters' => array( 'parameters' => array(
'right' => '+10%', 'right' => '+10%',
@ -444,7 +538,6 @@ function imagecache_testsuite_positioning_get_tests() {
'y' => '85', 'y' => '85',
), ),
), ),
'css centering' => array( 'css centering' => array(
'parameters' => array( 'parameters' => array(
'right' => '50%', 'right' => '50%',
@ -469,4 +562,4 @@ function imagecache_testsuite_positioning_get_tests() {
), ),
); );
} }

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
@ -15,7 +14,7 @@ $presets['scale'] = array (
'effects' => array ( 'effects' => array (
0 => array ( 0 => array (
'weight' => '-1', 'weight' => '-1',
'module' => 'imagecache', 'module' => 'image',
'name' => 'image_scale', 'name' => 'image_scale',
'data' => array ( 'data' => array (
'width' => '100', 'width' => '100',

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.
@ -16,7 +15,7 @@ $presets['scale_canvas'] = array (
'effects' => array ( 'effects' => array (
1 => array ( 1 => array (
'weight' => '1', 'weight' => '1',
'module' => 'imagecache', 'module' => 'image',
'name' => 'image_scale', 'name' => 'image_scale',
'data' => array ( 'data' => array (
'width' => '100', 'width' => '100',

View File

@ -1,5 +1,4 @@
<?php <?php
// $ID: $
/** /**
* @file * @file
* Test imagecache preset. * Test imagecache preset.

View File

@ -1,30 +1,75 @@
<?php <?php
/** /**
* @file Utility functions for color widgets * @file Utility functions for color widgets.
*/ */
/** /**
* Prepare a subform for displaying RGB fields * Prepares a subform for displaying RGB fields.
* *
* Helper function to render a common element. * Helper function to render a common element.
* *
* Note that any module that re-uses this form also has to declare the theme * Note that any module that re-uses this form also has to declare the theme
* function in order to ensure it's in the registry. * function in order to ensure it's in the registry.
*
* @param array $action
*
* @return array
*/ */
function imagecache_rgb_form($action) { function imagecache_rgb_form($action) {
if ($action['HEX'] && $deduced = imagecache_actions_hex2rgba($action['HEX'])) { if ($action['HEX'] && $deduced = imagecache_actions_hex2rgba($action['HEX'])) {
$action = array_merge($action, $deduced); $action = array_merge($action, $deduced);
$action['HEX'] = ltrim($action['HEX'], '#'); // With or without # is valid, but the colorpicker expects it always.
// With or without # is valid, but trim for consistancy $action['HEX'] = '#' . ltrim($action['HEX'], '#');
} }
$form = array('#theme' => 'imagecacheactions_rgb_form'); $form = array(
$form['farb'] = array('#weight' => -1); // Placeholder to get its weight right '#prefix' => '<div class="colorform" >',
'#suffix' => '</div>',
);
$form['colorpicker'] = array(
'#weight' => -1,
'#markup' => '<div class="colorpicker" style="float:right;"></div>',
);
$form['HEX'] = array( $form['HEX'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('HEX'), '#title' => t('HEX'),
'#default_value' => $action['HEX'], '#default_value' => $action['HEX'],
'#size' => 7, '#size' => 7,
'#element_validate' => array('imagecache_rgb_validate'), '#element_validate' => array('imagecache_rgb_validate'),
'#attributes' => array('class' => array('colorentry')),
);
// Adds the JS that binds the textarea with the farbtastic element.
// Find each colorpicker placeholder:
// initialize it,
// then find the nearby textfield that is of type colorentry
// and attach the colorpicker behavior to it.
// This is so we can support more that one per page if neccessary.
$init_colorpicker_script = "
(function ($) {
Drupal.behaviors.attachcolorpicker = {
attach: function(context) {
$('.colorpicker').each(function () {
// Configure picker to be attached to the nearest colorentry field.
linked_target = $('.colorentry', $(this).closest('.colorform'))
farb = $.farbtastic($(this), linked_target);
});
}
}
})(jQuery);
";
// Add Farbtastic color picker.
$form['HEX']['#attached'] = array(
'library' => array(
array('system', 'farbtastic'),
),
'js' => array(
array(
'type' => 'inline',
'data' => $init_colorpicker_script,
),
),
); );
return $form; return $form;
@ -32,77 +77,36 @@ function imagecache_rgb_form($action) {
/** /**
* Element validate handler to ensure a hexadecimal color value. * Element validate handler to ensure a hexadecimal color value.
* *
* The core image_effect_color_validate() was not intelligent enough. * The core image_effect_color_validate() was not intelligent enough.
* Actually parse the value in order to see if it is parsable. * Actually parse the value in order to see if it is parsable.
*
* @param array $element
* param array $form_state
* Not used
*/ */
function imagecache_rgb_validate($element, &$form_state) { function imagecache_rgb_validate($element/*, &$form_state*/) {
if ($element['#value'] != '') { if ($element['#value'] != '') {
$rgba_value = imagecache_actions_hex2rgba($element['#value']); $rgba_value = imagecache_actions_hex2rgba($element['#value']);
if (!$rgba_value) { if (!$rgba_value) {
// Returning NULL means a parse error // Returning NULL means a parse error.
form_error($element, t('!name must be a hexadecimal color value.', array('!name' => $element['#title']))); form_error($element, t('!name must be a hexadecimal color value.', array('!name' => $element['#title'])));
} }
} }
} }
/** /**
* Render the subform in a table * Displays a chosen color for use in the style summary snippets,
*/ * by actually rendering the color.
function theme_imagecacheactions_rgb_form($variables) { *
$form = $variables['form']; * @param array $variables
// Add a farb element *
drupal_add_css('misc/farbtastic/farbtastic.css', array('preprocess' => FALSE)); * @return string
drupal_add_js('misc/farbtastic/farbtastic.js');
// drupal_add_js(drupal_get_path('module', 'imagecache_coloractions') . '/color.js');
$hex_id = $form['HEX']['#id'];
$form['farb'] = array(
'#value' => "<div id=\"$hex_id-farb\" style=\"float:right\"></div>",
'#weight' => -1,
);
// Adds the JS that binds the textarea with the farb element
$js = "
$(document).ready(function() {
farbify($('#$hex_id'), '#$hex_id-farb');
});
function farbify(elt, wrapper) {
var farb = $.farbtastic(wrapper);
farb.linkTo(function(color) {
elt
.css('background-color', color)
.css('color', this.hsl[2] > 0.5 ? '#000' : '#fff')
.val(color.substring(1));
});
farb.setColor('#' + elt.val());
elt.bind('keyup', function(){ updateColor(elt, farb); });
}
function updateColor(elt, farb) {
var text = elt.val();
if (text.length == 6)
farb.setColor('#' + text);
}
";
drupal_add_js($js, array('type' => 'inline', 'scope' => JS_DEFAULT));
$output = drupal_render_children($form);
return $output;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/ */
function theme_imagecacheactions_rgb($variables) { function theme_imagecacheactions_rgb($variables) {
$rgb = $variables['RGB']; if (!empty($variables['RGB']['HEX'])) {
if ($rgb['HEX']) { $hex = ltrim($variables['RGB']['HEX'], '#');
return " <span style=\"width:2em; border:1px solid white; background-color:#{$rgb['HEX']}\" >&nbsp;#{$rgb['HEX']}&nbsp;</span>"; return " <span style=\"width:2em; border:1px solid white; background-color:#{$hex}\" >&nbsp;#{$hex}&nbsp;</span>";
}
else {
return ' ' . t('Transparent');
} }
return ' ' . t('Transparent');
} }

View File

@ -1,36 +1,41 @@
<?php <?php
/** /**
* @file Utility form, conversion and rendering functions for image processes * @file Utility form, conversion and rendering functions for image processes.
*/ */
/** /**
* Prepare a subform for displaying positioning fields * Prepares a sub form for displaying positioning fields.
* *
* Helper function to render a common element. * @param array $data
* Effect data of the effect where this sub form will be integrated.
*
* @return array
* The form definition for this sub form.
*/ */
function imagecache_actions_pos_form($action) { function imagecache_actions_pos_form(array $data) {
$defaults = array( $defaults = array(
'xpos' => 'center', 'xpos' => 'center',
'ypos' => 'center', 'ypos' => 'center',
); );
$action = array_merge($defaults, (array) $action); $data = array_merge($defaults, (array) $data);
$description1 = t('Enter an offset in pixels (e.g. 10, 10px), a percentage (e.g. 25%), or one of the keywords: <em>left</em>, <em>center</em>, or <em>right</em> wih an optional offset (e.g. center, right - 10%).');
$description2 = t('Enter an offset in pixels (e.g. 10, 10px), a percentage (e.g. 25%), or one of the keywords: <em>top</em>, <em>center</em>, or <em>bottom</em> wih an optional offset (e.g. center, bottom - 10%).');
$form = array( $form = array(
#'#theme' => 'canvasactions_pos_form',
'xpos' => array( 'xpos' => array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('X offset'), '#title' => t('X offset'),
'#default_value' => $action['xpos'], '#default_value' => $data['xpos'],
'#size' => 6, '#size' => 6,
'#description' => t('Enter an offset in pixels or use a keyword: <em>left</em>, <em>center</em>, or <em>right</em>.'), '#description' => $description1,
'#element_validate' => array('imagecache_actions_validate_number'), '#element_validate' => array('imagecache_actions_validate_number'),
), ),
'ypos' => array( 'ypos' => array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Y offset'), '#title' => t('Y offset'),
'#default_value' => $action['ypos'], '#default_value' => $data['ypos'],
'#size' => 6, '#size' => 6,
'#description' => t('Enter an offset in pixels or use a keyword: <em>top</em>, <em>center</em>, or <em>bottom</em>.'), '#description' => $description2,
'#element_validate' => array('imagecache_actions_validate_number'), '#element_validate' => array('imagecache_actions_validate_number'),
), ),
); );
@ -38,9 +43,10 @@ function imagecache_actions_pos_form($action) {
} }
/** /**
* Ensure the numbers are valid. * Form element validator that checks for a valid number.
* *
* Set blanks to zero, just so the status summary doesn't get odd blanks * @param array $element
* @param $form_state
*/ */
function imagecache_actions_validate_number(&$element, &$form_state) { function imagecache_actions_validate_number(&$element, &$form_state) {
if (empty($element['#value'])) { if (empty($element['#value'])) {
@ -49,12 +55,12 @@ function imagecache_actions_validate_number(&$element, &$form_state) {
} }
/** /**
* @todo Please document this function. * Form element validator that checks a transparency percentage value.
* @see http://drupal.org/node/1354 *
* @param array $element
*/ */
function imagecache_actions_validate_alpha(&$element, &$form_status) { function imagecache_actions_validate_alpha(&$element/*, &$form_status*/) {
if (!is_numeric($element['#value']) || $element['#value'] < 1 || $element['#value'] > 100) { if (!is_numeric($element['#value']) || $element['#value'] < 1 || $element['#value'] > 100) {
form_set_error(join('][', $element['#parents']), t('Opacity must be a number between 1 and 100.')); form_set_error(join('][', $element['#parents']), t('Opacity must be a number between 1 and 100.'));
} }
} }

View File

@ -4,6 +4,40 @@
* image processing. * image processing.
*/ */
/**
* Validates that the element is a positive number.
*
* This is a Form API #element_validate callback.
*
* @param array $element
* param array $form_status
*/
function imagecache_actions_validate_number_positive(&$element/*, &$form_status*/) {
$value = $element['#value'];
if ($value != '') {
if (!is_numeric($value) || (float) $value <= 0.0) {
form_error($element, t('%name must be a positive number.', array('%name' => $element['#title'])));
}
}
}
/**
* Validates that the element is a non negative number.
*
* This is a Form API #element_validate callback.
*
* @param array $element
* param array $form_status
*/
function imagecache_actions_validate_number_non_negative(&$element/*, &$form_status*/) {
$value = $element['#value'];
if ($value != '') {
if (!is_numeric($value) || (float) $value < 0.0) {
form_error($element, t('%name must be a non negative number.', array('%name' => $element['#title'])));
}
}
}
/* /*
* File field handling. * File field handling.
*/ */
@ -12,7 +46,7 @@
*/ */
function imagecache_actions_file_field_description() { function imagecache_actions_file_field_description() {
return t('File is either a file with one of the valid schemes, an absolute path, or a relative path (relative to the current directory, probably the Drupal site root). Valid schemes are a.o. private://, public://, and if the !module module has been installed, also module:// and theme://.', return t('File is either a file with one of the valid schemes, an absolute path, or a relative path (relative to the current directory, probably the Drupal site root). Valid schemes are a.o. private://, public://, and if the !module module has been installed, also module:// and theme://.',
array('!module' => l('System Stream Wrapper', 'http://drupal.org/project/system_stream_wrapper', array('external' => TRUE)))); array('!module' => l('System Stream Wrapper', 'https://drupal.org/project/system_stream_wrapper', array('external' => TRUE))));
} }
/** /**
@ -21,33 +55,27 @@ function imagecache_actions_file_field_description() {
* This is a Form API #element_validate callback. * This is a Form API #element_validate callback.
* *
* @param array $element * @param array $element
* @param array $form_status * param array $form_status
*/ */
function imagecache_actions_validate_file(&$element, &$form_status) { function imagecache_actions_validate_file(&$element/*, &$form_status*/) {
if (!imagecache_actions_find_file($element['#value'])) { if (!imagecache_actions_find_file($element['#value'])) {
form_error($element, t("Unable to find the file '%file'. Please check the path.", array('%file' => $element['#value'])) ); form_error($element, t("Unable to find the file '%file'. Please check the path.", array('%file' => $element['#value'])));
} }
} }
/** /**
* Looks up and returns the full path of the given file. * Looks up and returns the full path of the given file.
* *
* We accept files with the following schemes:
* - private://
* - public://
* - temporary://
* - module://{module}/{component}
*
* Files without a scheme are looked up as are:
* - relative: relative to the current directory (probably $drupal_root).
* - absolute: as is.
*
* @param string $file * @param string $file
* A file name, with a scheme, relative, or absolute. * A file name. We accept:
* - stream wrapper notation like: private://, public://, temporary:// and
* module:// (the latter provided by the system stream wrapper module).
* - relative: relative to the current directory (probably DRUPAL_ROOT).
* - absolute: as is.
* *
* @return string|false * @return string|false
* The full file path of the file, so the image toolkit knows exactly where it * The full file path of the file, so the image toolkit knows exactly where it
* is. * is. False if the file cannot be found or is not readable.
*/ */
function imagecache_actions_find_file($file) { function imagecache_actions_find_file($file) {
$result = FALSE; $result = FALSE;
@ -86,12 +114,43 @@ function imagecache_actions_image_load($file, $toolkit = FALSE) {
} }
/** /**
* Return an array with context information about the image. * Returns the label for the given style name.
* *
* This information can e.g. be used by effects that allow custom PHP like * As of D7.23 image styles can also have a human readable label besides their
* machine readable name. This function returns that label if available, or the
* name if the label is absent.
*
* @param string|null $style_name
* The name of the image style.
*
* @return string
* The label for the image style.
*/
function imagecache_actions_get_style_label($style_name) {
if (!empty($style_name)) {
$style = image_style_load($style_name);
$label = isset($style['label']) ? $style['label'] : $style['name'];
if (!empty($style)) {
return $label;
}
else {
return $label . ' ' . t('<span class="error">Invalid reference. The referenced image style may have been deleted!</span>');
}
}
else {
return t('none');
}
}
/**
* Returns an array with context information about the image.
*
* This information is called by effects that need contextual information or
* that allow custom PHP:
* - Custom action. * - Custom action.
* - Text from image alt or title.
* - Text with tokens.
* - Text from PHP code. * - Text from PHP code.
* - Text from alt or title.
* *
* @param object $image * @param object $image
* The image object. * The image object.
@ -99,55 +158,125 @@ function imagecache_actions_image_load($file, $toolkit = FALSE) {
* An associative array with the effect options. * An associative array with the effect options.
* *
* @return array * @return array
* An associative array with context information about the image. * An associative array with context information about the image. It contains
* the following keys:
* - effect_data: array with the effect data.
* - managed_file: object|null, a managed file object for the current image.
* This may be (an extended) media/file_entity file object.
* - referring_entities: array, list of (loaded) entities that have an image
* field referring to the managed file.
* - entity: object|null, the 1st (and often only) entity that has an image
* field referring to the managed file.
* - image_field: array|null, the (1st and often only) image field item of
* entity that refers to the managed file (single field item, not the
* whole field value).
*/ */
function imagecache_actions_get_image_context($image, $data) { function imagecache_actions_get_image_context($image, $data) {
// Store context about the image. // Store context about the image.
$image_context = array( $image_context = array(
'effect_data' => $data, 'effect_data' => $data,
'managed_file' => NULL, 'managed_file' => NULL,
'referring_entities' => array(), 'referring_entities' => array(),
'entity' => NULL, 'entity' => NULL,
'image_field' => NULL, 'image_field' => NULL,
); );
// Find the managed file object (at most 1 object as 'uri' is a unique index). // Find the managed file object (at most 1 object as 'uri' is a unique index).
$managed_file = reset(file_load_multiple(array(), array('uri' => $image->source))); $managed_file = file_load_multiple(array(), array('uri' => $image->source));
$managed_file = reset($managed_file);
if ($managed_file !== FALSE) { if ($managed_file !== FALSE) {
$image_context['managed_file'] = $managed_file; $image_context['managed_file'] = $managed_file;
// And find the entities referring to this managed file. // And find the entities referring to this managed file, image fields first,
$references = file_get_file_references($managed_file, NULL, FIELD_LOAD_CURRENT, 'image'); // then file fields (very specific use cases).
$references = file_get_file_references($managed_file, NULL, FIELD_LOAD_CURRENT, 'image')
+ file_get_file_references($managed_file, NULL, FIELD_LOAD_CURRENT, 'file');
if ($references) { if ($references) {
// Load referring entities. // Load referring entities., keep track of 1st reference separately.
$entity_type_first = '';
$field_name_first = '';
foreach ($references as $field_name => $field_references) { foreach ($references as $field_name => $field_references) {
foreach ($field_references as $entity_type => $entity_stubs) { foreach ($field_references as $entity_type => $entity_stubs) {
$image_context['referring_entities'][$field_name][$entity_type] = entity_load($entity_type, array_keys($entity_stubs)); $entities = entity_load($entity_type, array_keys($entity_stubs));
if (!empty($entities)) {
$image_context['referring_entities'][$field_name][$entity_type] = $entities;
// Make it easy to access the '1st' entity and its referring image
// field, part 1: entity.
if (empty($entity_type_first)) {
$entity_type_first = $entity_type;
$field_name_first = $field_name;
$image_context['entity'] = reset($entities);
}
}
} }
} }
// Make it easy to access the '1st' entity and its referring image field. // Make it easy to access the '1st' entity and its referring image field,
reset($image_context['referring_entities']); // part 2: image field.
list($field_name, $field_references) = each($image_context['referring_entities']); /** @var array|false $image_field */
reset($field_references); $image_field = field_get_items($entity_type_first, $image_context['entity'], $field_name_first);
list($entity_type, $entities) = each($field_references); if ($image_field) {
reset($entities); // Get referring item.
list(, $image_context['entity']) = each($entities);
$image_field = field_get_items($entity_type, $image_context['entity'], $field_name);
if ($image_field !== FALSE) {
// Get referring item
foreach ($image_field as $image_field_value) { foreach ($image_field as $image_field_value) {
if ($image_field_value['fid'] === $managed_file->fid) { if ($image_field_value['fid'] === $managed_file->fid) {
$image_context['image_field'] = $image_field_value; $image_context['image_field'] = $image_field_value;
break;
} }
} }
} }
} }
} }
// @todo: support for media module, or is that based upon managed files?
return $image_context; return $image_context;
} }
/**
* Returns information about the context, image style and image effect id, of
* the current image effect.
*
* Note that this information is not alterable, that is, it will not have any
* effect on the rest of the derivative image generation process including its
* subsequent effects.
*
* This information is called by effects that may need contextual information or
* that allow custom PHP:
* - Custom action.
* - Text from image alt or title.
* - Text with tokens.
* - Text from PHP code.
*
* This information is fetched by traversing the backtrace, looking for known
* functions that have the image style or effect as argument..
*
* @return array
* Array with keys 'image_style' and 'image_effect_id' pointing to the current
* image style (array) resp. image effect id (int).
*/
function imagecache_actions_get_image_effect_context() {
$result = array();
$backtrace = debug_backtrace();
foreach ($backtrace as $function_call) {
if ($function_call['function'] && $function_call['function'] === 'image_effect_apply') {
$result['image_effect_id'] = isset($function_call['args'][1]['ieid']) ? $function_call['args'][1]['ieid'] : NULL;
}
else if ($function_call['function'] && $function_call['function'] === 'image_style_create_derivative') {
$result['image_style'] = isset($function_call['args'][0]) ? $function_call['args'][0] : NULL;
}
if (count($result) === 2) {
break;
}
}
$result += array(
'image_effect_id' => NULL,
'image_style' => NULL,
);
return $result;
}
/** /**
* Given two imageapi objects with dimensions, and some positioning values, * Given two imageapi objects with dimensions, and some positioning values,
* calculate a new x,y for the layer to be placed at. * calculate a new x,y for the layer to be placed at.
@ -172,8 +301,7 @@ function imagecache_actions_get_image_context($image, $data) {
* A keyed array of absolute x,y co-ordinates to place the layer at. * A keyed array of absolute x,y co-ordinates to place the layer at.
*/ */
function imagecache_actions_calculate_relative_position($base, $layer, $style) { function imagecache_actions_calculate_relative_position($base, $layer, $style) {
// both images should now have size info available. // Both images should now have size info available.
if (isset($style['bottom'])) { if (isset($style['bottom'])) {
$ypos = imagecache_actions_calculate_offset('bottom', $style['bottom'], $base->info['height'], $layer->info['height']); $ypos = imagecache_actions_calculate_offset('bottom', $style['bottom'], $base->info['height'], $layer->info['height']);
} }
@ -186,37 +314,55 @@ function imagecache_actions_calculate_relative_position($base, $layer, $style) {
if (isset($style['left'])) { if (isset($style['left'])) {
$xpos = imagecache_actions_calculate_offset('left', $style['left'], $base->info['width'], $layer->info['width']); $xpos = imagecache_actions_calculate_offset('left', $style['left'], $base->info['width'], $layer->info['width']);
} }
if (! isset($ypos)) { if (!isset($ypos)) {
// assume center // Assume center.
$ypos = ($base->info['height'] / 2) - ($layer->info['height'] / 2); $ypos = ($base->info['height'] / 2) - ($layer->info['height'] / 2);
} }
if (! isset($xpos)) { if (!isset($xpos)) {
// assume center // Assume center.
$xpos = ($base->info['width'] / 2) - ($layer->info['width'] / 2); $xpos = ($base->info['width'] / 2) - ($layer->info['width'] / 2);
} }
#dpm(__FUNCTION__ . " Calculated offsets"); // dpm(__FUNCTION__ . " Calculated offsets");
#dpm(get_defined_vars()); // dpm(get_defined_vars());
return array('x' => $xpos, 'y' => $ypos); return array('x' => $xpos, 'y' => $ypos);
} }
/** /**
* Calculates an offset from an edge.
*
* Positive numbers are IN from the edge, negative offsets are OUT. * Positive numbers are IN from the edge, negative offsets are OUT.
* *
* $keyword, $value, $base_size, $layer_size * Examples:
* eg * - left, 20, 200, 100 = 20
* left,20 200, 100 = 20 * - right, 20, 200, 100 = 80 (object 100 wide placed 20px from the right)
* right,20 200, 100 = 80 (object 100 wide placed 20 px from the right = x=80) * - top, 50%, 200, 100 = 50 (Object is centered when using %)
* * - top, 20%, 200, 100 = -10
* top,50%, 200, 100 = 50 (Object is centered when using %) * - bottom, -20, 200, 100 = 220
* top,20%, 200, 100 = -10 * - right, -25%, 200, 100 = 200 (this ends up just off screen)
* bottom,-20, 200, 100 = 220
* right, -25%, 200, 100 = 200 (this ends up just off screen)
* *
* Also, the value can be a string, eg "bottom-100", or "center+25%" * Also, the value can be a string, eg "bottom-100", or "center+25%"
* @param string $keyword
* The edge to calculate the offset from. Can be one of: left, right, top,
* bottom, middle, center.
* @param string $value
* The value to offset with: can be:
* - a numeric value: offset in pixels
* - a percentage value: offset with a percentage of the $base_size.
* - a keyword indicating a pxiel size: left, right, top, bottom, middle,
* center.
* - an expression of the format: keyword +|- value[%], e.g. center+25%.
* @param int $base_size
* The size of the dimension. Used to calculate percentages of.
* @param int $layer_size
* The size of the canvas in the current dimension. The value to take for
* $keyword will be based on this value.
*
* @return int
*/ */
function imagecache_actions_calculate_offset($keyword, $value, $base_size, $layer_size) { function imagecache_actions_calculate_offset($keyword, $value, $base_size, $layer_size) {
$offset = 0; // used to account for dimensions of the placed object $offset = 0;
// Used to account for dimensions of the placed object.
$direction = 1; $direction = 1;
$base = 0; $base = 0;
if ($keyword == 'right' || $keyword == 'bottom') { if ($keyword == 'right' || $keyword == 'bottom') {
@ -229,16 +375,18 @@ function imagecache_actions_calculate_offset($keyword, $value, $base_size, $laye
$offset = -1 * ($layer_size / 2); $offset = -1 * ($layer_size / 2);
} }
// Keywords may be used to stand in for numeric values // Keywords may be used to stand in for numeric values.
switch ($value) { switch ($value) {
case 'left': case 'left':
case 'top': case 'top':
$value = 0; $value = 0;
break; break;
case 'middle': case 'middle':
case 'center': case 'center':
$value = $base_size / 2; $value = $base_size / 2;
break; break;
case 'bottom': case 'bottom':
case 'right': case 'right':
$value = $base_size; $value = $base_size;
@ -246,22 +394,24 @@ function imagecache_actions_calculate_offset($keyword, $value, $base_size, $laye
// Handle keyword-number cases like top+50% or bottom-100px, // Handle keyword-number cases like top+50% or bottom-100px,
// @see imagecache_actions_keyword_filter(). // @see imagecache_actions_keyword_filter().
if (preg_match('/^(.+)([\+\-])(\d+)([^\d]*)$/', $value, $results)) { if (preg_match('/^([a-z]+) *([+-]) *(\d+)((px|%)?)$/', $value, $results)) {
list(, $value_key, $value_mod, $mod_value, $mod_unit) = $results; list(, $value_key, $value_mod, $mod_value, $mod_unit) = $results;
if ($mod_unit == '%') { if ($mod_unit == '%') {
$mod_value = $mod_value / 100 * $base_size; $mod_value = $mod_value / 100 * $base_size;
} }
$mod_direction = ($value_mod == '-') ? -1 : + 1; $mod_direction = ($value_mod == '-') ? -1 : +1;
switch ($value_key) { switch ($value_key) {
case 'left': case 'left':
case 'top': case 'top':
default: default:
$mod_base = 0; $mod_base = 0;
break; break;
case 'middle': case 'middle':
case 'center': case 'center':
$mod_base = $base_size / 2; $mod_base = $base_size / 2;
break; break;
case 'bottom': case 'bottom':
case 'right': case 'right':
$mod_base = $base_size; $mod_base = $base_size;
@ -271,14 +421,17 @@ function imagecache_actions_calculate_offset($keyword, $value, $base_size, $laye
return $modified_value; return $modified_value;
} }
// handle % values // Handle % values.
if (substr($value, strlen($value) -1, 1) == '%') { if (substr($value, -1) === '%') {
$value = intval($value / 100 * $base_size); // Explicitly convert $value to prevent warnings in PHP 7.1.
$value = intval((int) $value / 100 * $base_size);
$offset = -1 * ($layer_size / 2); $offset = -1 * ($layer_size / 2);
} }
$value = $base + ($direction * $value); // $value may contain 'px' at the end: explicitly convert to prevent warnings
// in PHP 7.1.
$value = $base + ($direction * (int) $value);
// Add any extra offset to position the item // Add any extra offset to position the item.
return $value + $offset; return $value + $offset;
} }
@ -329,9 +482,9 @@ function imagecache_actions_hex2rgba($hex) {
$g = hexdec($g); $g = hexdec($g);
$b = hexdec($b); $b = hexdec($b);
$a = hexdec($a); $a = hexdec($a);
// alpha over 127 is illegal. assume they meant half that. // Alpha over 127 is illegal. assume they meant half that.
if ($a > 127) { if ($a > 127) {
$a = (int) $a/2; $a = (int) $a / 2;
} }
return array('red' => $r, 'green' => $g, 'blue' => $b, 'alpha' => $a); return array('red' => $r, 'green' => $g, 'blue' => $b, 'alpha' => $a);
} }
@ -358,8 +511,8 @@ function imagecache_actions_hex2rgba($hex) {
* @return int * @return int
*/ */
function imagecache_actions_keyword_filter($value, $base_size, $layer_size) { function imagecache_actions_keyword_filter($value, $base_size, $layer_size) {
// See above for the patterns this matches // See above for the patterns this matches.
if (! preg_match('/([a-z]*)([\+\-]?)(\d*)([^\d]*)/', $value, $results) ) { if (!preg_match('/([a-z]*) *([+-]?) *(\d*)((px|%)?)/', $value, $results)) {
trigger_error("imagecache_actions had difficulty parsing the string '$value' when calculating position. Please check the syntax.", E_USER_WARNING); trigger_error("imagecache_actions had difficulty parsing the string '$value' when calculating position. Please check the syntax.", E_USER_WARNING);
} }
list(, $keyword, $plusminus, $value, $unit) = $results; list(, $keyword, $plusminus, $value, $unit) = $results;
@ -368,83 +521,28 @@ function imagecache_actions_keyword_filter($value, $base_size, $layer_size) {
} }
/** /**
* @deprecated * Computes a length based on a length specification and an actual length.
* *
* Given a file path relative the default file scheme, tries to locate it and, * Examples:
* is successful, finds all fields that use it. * (50, 400) returns 50; (50px, 400) returns 50; (50%, 400) returns 200;
* (50, null) returns 50; (50%, null) returns null;
* (null, null) returns null; (null, 100) returns null.
* *
* @param string $filepath * @param string|null $length_specification
* Actually a Drupal file URI, probably. * The length specification. An integer constant optionally followed by 'px'
* @param bool $load_entity * or '%'.
* Whether to return whole entities or just the identifying fields of them. * @param int|null $current_length
* * The current length. May be null.
* @return array
* A nested array of basic entity data, grouped by entity type and field name.
* Empty array if no file fields are referring the given path.
*/
function imagecache_actions_fields_from_filepath($filepath, $load_entity = TRUE) {
if (!module_exists('file')) {
return array();
}
// Unsure if we are given a real filepath (the normal assumption) or a
// Drupal scheme URI. Resolve either.
if (!file_valid_uri($filepath)) {
$filepath = file_build_uri($filepath);
}
$files = file_load_multiple(array(), array('uri' => $filepath));
if ($files) {
$fields = array();
foreach ($files as $fid => $file) {
$references = file_get_file_references($file, NULL, FIELD_LOAD_CURRENT, 'image');
$fields[$fid] = $references;
}
if ($load_entity) {
// These references are pretty low on information. Load the actual nodes/entities too.
imagecache_actions_entities_from_references($fields);
}
return $fields;
}
return array();
}
/**
* @deprecated
*
* Load additional data - Fully load the entities that actually use the given file, with all their data.
*
* Replaces the full entity object in place of the placeholder object that file_get_file_references() gives us.
*/
function imagecache_actions_entities_from_references(&$fields) {
foreach ($fields as $fid => $field_ids) {
foreach ($field_ids as $field_id => $entity_types) {
foreach ($entity_types as $entity_type => $entity_instances) {
#$entities = entity_load($entity_type, array_keys($entity_instances));
foreach ($entity_instances as $entity_id => $entity) {
$entities = entity_load($entity_type, array($entity_id));
$entity = reset($entities);
// Add this extra data to the return info, replacing the lightweight version
$fields[$fid][$field_id][$entity_type][$entity_id] = $entity;
} // All actual entities
} // All types of entity that ref the file
} // All references to this file
} // All entries for this file found in the db (expected to be only 1)
}
/**
* @param string $value
* @param int|null $current_pixels
* *
* @return int|null * @return int|null
*/ */
function imagecache_actions_percent_filter($value, $current_pixels) { function imagecache_actions_percent_filter($length_specification, $current_length) {
if (strpos($value, '%') !== FALSE) { if (strpos($length_specification, '%') !== FALSE) {
$value = $current_pixels !== NULL ? str_replace('%', '', $value) * 0.01 * $current_pixels : NULL; $length_specification = $current_length !== NULL ? str_replace('%', '', $length_specification) * 0.01 * $current_length : NULL;
} }
return $value; else {
// Strips 'px' if available.
$length_specification = (int) $length_specification;
}
return $length_specification;
} }

View File

@ -1,86 +1,119 @@
<?php <?php
// $ID $
/** /**
* @file routine for image layering * @file Routine for image layering.
* *
*/ * Note that the below code is laboriously slow - it takes and compares every
* pixel of the two inputs and calculates a new values for each of them. this is
* only a fallback because reliable image toolkit transparencies was buggy on
/****************************************************************************************************************************************/ * certain platforms :(
/** *
* Note that the below code is laboriously slow - it takes and compares every pixel of the two inputs * Code from: Niko (http://www.codeguru.com.ua).
* and calculates a new valus for each of them. this is only a fallback because reliable image toolkit */
* transparencies was buggy on certain platforms :( class watermark {
*/
/**
//Niko (http://www.codeguru.com.ua) * TODO
class watermark { *
function create_watermark($main_img_obj, $watermark_img_obj, $x_ins, $y_ins, $alpha_level = 100) { * @param resource $main_img_obj
$alpha_level /= 100; * @param resource $watermark_img_obj
# Should this change to match both images? * @param int $x_ins
#$main_img_obj_w = max(imagesx($main_img_obj), imagesx($watermark_img_obj)); * @param int $y_ins
#$main_img_obj_h = max(imagesy($main_img_obj), imagesy($watermark_img_obj)); * @param int $alpha_level
$main_img_obj_w = imagesx($main_img_obj); *
$main_img_obj_h = imagesy($main_img_obj); * @return resource
$watermark_img_obj_w = imagesx($watermark_img_obj); *
$watermark_img_obj_h = imagesy($watermark_img_obj); */
function create_watermark($main_img_obj, $watermark_img_obj, $x_ins, $y_ins, $alpha_level = 100) {
$main_img_obj_min_x = $x_ins; $alpha_level /= 100;
$main_img_obj_min_y = $y_ins; // Should this change to match both images?
//$main_img_obj_w = max(imagesx($main_img_obj), imagesx($watermark_img_obj));
$return_img = imagecreatetruecolor($main_img_obj_w, $main_img_obj_h); //$main_img_obj_h = max(imagesy($main_img_obj), imagesy($watermark_img_obj));
imagesavealpha($return_img, true); $main_img_obj_w = imagesx($main_img_obj);
imagealphablending($return_img, false); $main_img_obj_h = imagesy($main_img_obj);
$watermark_img_obj_w = imagesx($watermark_img_obj);
$watermark_img_obj_h = imagesy($watermark_img_obj);
for ($y = 0; $y < $main_img_obj_h; $y++) {
for ($x = 0; $x < $main_img_obj_w; $x++) { $main_img_obj_min_x = $x_ins;
$main_img_obj_min_y = $y_ins;
$return_color = NULL;
$return_img = imagecreatetruecolor($main_img_obj_w, $main_img_obj_h);
$watermark_x = $x - $main_img_obj_min_x; imagesavealpha($return_img, TRUE);
$watermark_y = $y - $main_img_obj_min_y; imagealphablending($return_img, FALSE);
$main_rgb = imagecolorsforindex($main_img_obj, imagecolorat($main_img_obj, $x, $y)); for ($y = 0; $y < $main_img_obj_h; $y++) {
for ($x = 0; $x < $main_img_obj_w; $x++) {
if ($watermark_x >= 0 && $watermark_x < $watermark_img_obj_w && $watermark_y >= 0 && $watermark_y < $watermark_img_obj_h) {
$watermark_rbg = imagecolorsforindex($watermark_img_obj, imagecolorat($watermark_img_obj, $watermark_x, $watermark_y)); $return_color = NULL;
$watermark_alpha = round(((127 - $watermark_rbg['alpha']) / 127), 2); $watermark_x = $x - $main_img_obj_min_x;
$watermark_alpha = $watermark_alpha * $alpha_level; $watermark_y = $y - $main_img_obj_min_y;
$avg_red = $this->_get_ave_color($main_rgb['red'], $watermark_rbg['red'], $watermark_alpha); $main_rgb = imagecolorsforindex($main_img_obj, imagecolorat($main_img_obj, $x, $y));
$avg_green = $this->_get_ave_color($main_rgb['green'], $watermark_rbg['green'], $watermark_alpha);
$avg_blue = $this->_get_ave_color($main_rgb['blue'], $watermark_rbg['blue'], $watermark_alpha); if ($watermark_x >= 0 && $watermark_x < $watermark_img_obj_w && $watermark_y >= 0 && $watermark_y < $watermark_img_obj_h) {
$watermark_rbg = imagecolorsforindex($watermark_img_obj, imagecolorat($watermark_img_obj, $watermark_x, $watermark_y));
// TODO figure out the maths for merging two transparent images
$new_alpha = min($watermark_rbg['alpha'], $main_rgb['alpha']); $watermark_alpha = round(((127 - $watermark_rbg['alpha']) / 127), 2);
#$new_alpha = $main_rgb['alpha']; $watermark_alpha = $watermark_alpha * $alpha_level;
#$new_alpha = 0;
$avg_red = $this->_get_ave_color($main_rgb['red'], $watermark_rbg['red'], $watermark_alpha);
$return_color = $this->_get_image_color($return_img, $avg_red, $avg_green, $avg_blue, $new_alpha); $avg_green = $this->_get_ave_color($main_rgb['green'], $watermark_rbg['green'], $watermark_alpha);
} $avg_blue = $this->_get_ave_color($main_rgb['blue'], $watermark_rbg['blue'], $watermark_alpha);
else {
$return_color = imagecolorat($main_img_obj, $x, $y); // TODO figure out the maths for merging two transparent images.
} $new_alpha = min($watermark_rbg['alpha'], $main_rgb['alpha']);
imagesetpixel($return_img, $x, $y, $return_color); //$new_alpha = $main_rgb['alpha'];
} //$new_alpha = 0;
}
return $return_img; $return_color = $this->_get_image_color($return_img, $avg_red, $avg_green, $avg_blue, $new_alpha);
} }
else {
function _get_ave_color($color_a, $color_b, $alpha_level) { $return_color = imagecolorat($main_img_obj, $x, $y);
return round((($color_a * (1 - $alpha_level)) + ($color_b * $alpha_level))); }
} imagesetpixel($return_img, $x, $y, $return_color);
}
function _get_image_color($im, $r, $g, $b, $alpha) { }
$c = imagecolorexactalpha($im, $r, $g, $b, $alpha); return $return_img;
if ($c != -1) }
return $c;
$c = imagecolorallocate($im, $r, $g, $b, $alpha); /**
if ($c != -1) * Get an average color.
return $c; *
return imagecolorclosest($im, $r, $g, $b, $alpha); * @param int $color_a
} * @param int $color_b
} * @param float $alpha_level
/****************************************************************************************************************************************/ *
* @return float
*
*/
function _get_ave_color($color_a, $color_b, $alpha_level) {
return round((($color_a * (1 - $alpha_level)) + ($color_b * $alpha_level)));
}
/**
* TODO
*
* @param resource $image
* @param int $r
* @param int $g
* @param int $b
* @param int $alpha
*
* @return int
*/
function _get_image_color($image, $r, $g, $b, $alpha) {
$c = imagecolorexactalpha($image, $r, $g, $b, $alpha);
if ($c != -1) {
return $c;
}
// @todo: this now uses imagecolorallocatealpha(): test
$c = imagecolorallocatealpha($image, $r, $g, $b, $alpha);
if ($c != -1) {
return $c;
}
// @todo: this now uses imagecolorclosestalpha(): test
return imagecolorclosestalpha($image, $r, $g, $b, $alpha);
}
}
/****************************************************************************************************************************************/

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,53 @@
CONTENTS OF THIS FILE
---------------------
* Introduction
* Requirements
* Installation
* Configuration
* Maintainers
INTRODUCTION
------------
The Module Missing Message Fixer module displays a list of missing modules that appear in the message log after the Drupal 7.50 release and provides an interface to fix the entries. This module was designed in sn effort to help those people who didnt want to run SQL commands directly.
* For a full description of the module, visit https://www.drupal.org/project/module_missing_message_fixer
* To submit bug reports and feature suggestions, or to track changes visit https://www.drupal.org/project/issues/module_missing_message_fixer
REQUIREMENTS
------------
This module requires no additional modules outside of Drupal core.
INSTALLATION
------------
Install the Module Missing Message Fixer module as you would normally install a contributed Drupal module. Visit https://www.drupal.org/docs/7/extending-drupal-7/installing-contributed-modules-find-import-enable-configure-drupal-7 for further information.
CONFIGURATION
-------------
Through the UI interface:
1. Enable the Module Missing Message Fixer module.
2. Ensure that you have the proper permissions by navigating to Administration > People > Permissions and selecting the checkbox.
3. Navigate to Administration > Configuration > System > Missing Module Message Fixer. Select the missing modules you would like to fix and select the "Remove These Errors!" tab. The module will now go through and remove the "ghost records" from the systems table.
Through Drush:
1. $ drush en module_missing_message_fixer
2. $ drush module-missing-message-fixer-list OR $ drush mmmfl. This will generate a list of missing modules.
3. $ drush module-missing-message-fixer-fix machine_name_of_module (or --all) OR $ drush mmmff machine_name_of_module (or --all). This will fix the missing modules entities.
For more information visit https://www.drupal.org/node/2487215
MAINTAINERS
-----------
* John Ouellet (labboy0276) - https://www.drupal.org/u/labboy0276
Supporting organization:
* Kalamuna - https://www.drupal.org/kalamuna

View File

@ -0,0 +1,64 @@
<?php
/**
* @file
* The Missing Module Message Fixer Admin Settings file.
*/
/**
* Missing Module Message Fixer Form.
*/
function module_missing_message_fixer_form() {
$form = array();
// Fancy title string.
$title = t('This list comes from the system table and is checked against the drupal_get_filename() function. See <a href="@link" target="_blank">this issue</a> for more information.', array(
'@link' => 'https://www.drupal.org/node/2487215',
));
// Title.
$form['title'] = array(
'#type' => 'item',
'#markup' => '<h2><center>' . $title . '</h2></center>',
);
// Fancy submit buttons to win this.
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Remove These Errors!'),
'#submit' => array('module_missing_message_fixer_form_submit'),
'#prefix' => '<center>',
'#suffix' => '</center>',
);
// Set the tables select to make this more granular.
$form['table'] = array(
'#type' => 'tableselect',
'#header' => _module_missing_message_fixer_get_table_header(),
'#options' => _module_missing_message_fixer_get_table_rows(),
'#empty' => t('No Missing Modules Found!!!'),
);
return $form;
}
/**
* Submit handler for Missing Module Message Fixer Form.
*
* @param array $form
* @param array $form_state
*/
function module_missing_message_fixer_form_submit(array $form, array &$form_state) {
$modules = array();
// Go through each record and add it to the array to win.
foreach ($form_state['values']['table'] as $module) {
$modules[] = $module;
}
// Delete if there is no modules.
if (count($modules) > 0) {
db_delete('system')
->condition('name', $modules, 'IN')
->execute();
}
}

View File

@ -0,0 +1,130 @@
<?php
/**
* @file
* Provide Drush integration for release building and dependency building.
*/
/**
* Helper function to check for modules to fix.
*
* @param bool $return
* If we are to return to rows or just print the list.
*
* @return array[]|null
* An array of table rows, or NULL if $return === FALSE.
*/
function module_missing_message_fixer_check_modules($return = FALSE) {
if ($return) {
return _module_missing_message_fixer_get_table_rows();
}
$rows = array();
// Use a key for the head row that is not a valid module name.
$rows['*HEAD*'] = _module_missing_message_fixer_get_table_header();
$rows += _module_missing_message_fixer_get_table_rows();
// Print Table here instead of in the hook_command.
$output = count($rows) > 1 ? drush_format_table($rows, TRUE) : 'No Missing Modules Found!!!';
drush_print($output);
return NULL;
}
/**
* Implements hook_drush_help().
*
* @param string $section
*
* @return null|string
*/
function module_missing_message_fixer_drush_help($section) {
switch ($section) {
case 'module-missing-message-fixer-list':
return dt("Returns a list of modules that have missing messages.");
case 'module-missing-message-fixer-fix':
return dt("Fixes a specified module that has missing messages. (optional --all)");
default:
return NULL;
}
}
/**
* Implements hook_drush_command().
*/
function module_missing_message_fixer_drush_command() {
$items = array();
$items['module-missing-message-fixer-list'] = array(
'description' => dt('Returns a list of modules that have missing messages.'),
'aliases' => array(
'mmmfl',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL
);
$items['module-missing-message-fixer-fix'] = array(
'description' => dt('Fixes modules that have missing messages.'),
'aliases' => array(
'mmmff',
),
'arguments' => array(
'name' => 'The name of the module to fix.',
),
'options' => array(
'all' => dt('Fixes all module missing messages'),
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL
);
return $items;
}
/**
* Drush command.
*
* Displays a list of modules that have missing messages.
*/
function drush_module_missing_message_fixer_list() {
module_missing_message_fixer_check_modules();
}
/**
* Drush command.
*
* @param string $name
* The name of the module to fix messages for.
*/
function drush_module_missing_message_fixer_fix($name = NULL) {
$modules = array();
if (drush_get_option('all') !== NULL) {
$rows = module_missing_message_fixer_check_modules(TRUE);
if (!empty($rows)) {
foreach ($rows as $row) {
$modules[] = $row['name'];
}
}
}
elseif ($name !== NULL) {
if (module_exists($name)) {
$modules[] = $name;
}
else {
drush_log(dt('Module ' . $name . ' was not found.'), 'error');
}
}
else {
drush_log(dt('Missing input, provide module name or run with --all'), 'error');
}
// Delete if there is no modules.
if (count($modules) > 0) {
db_delete('system')
->condition('name', $modules, 'IN')
->execute();
if (drush_get_option('all') !== NULL) {
drush_log(dt('All missing references have been removed.'), 'success');
}
elseif ($name !== NULL) {
if (in_array($name, $modules, TRUE)) {
drush_log(dt('Reference to ' . $name . ' (if found) has been removed.'), 'success');
}
}
}
}

View File

@ -0,0 +1,13 @@
name = Module Missing Message Fixer
description = This module displays a list of missing modules that appear after the Drupal 7.50 release and lets you fix the entries.
package = Administration
core = 7.x
configure = admin/config/system/module-missing-message-fixer
; Information added by Drupal.org packaging script on 2017-11-26
version = "7.x-1.7"
core = "7.x"
project = "module_missing_message_fixer"
datestamp = "1511726895"

View File

@ -0,0 +1,159 @@
<?php
/**
* @file
* The Missing Module Message Fixer Module file.
*/
/**
* Implements hook_permission().
*/
function module_missing_message_fixer_permission() {
return array(
'administer module missing message fixer' => array(
'title' => t('Administer Module Missing Message Fixer'),
),
);
}
/**
* Implements hook_menu().
*/
function module_missing_message_fixer_menu() {
$items = array();
$items['admin/config/system/module-missing-message-fixer'] = array(
'title' => 'Missing Module Message Fixer',
'description' => 'This module display a list of missing module that appear after the Drupal 7.50 release and lets you fix the entries.',
'type' => MENU_NORMAL_ITEM,
'page callback' => 'drupal_get_form',
'page arguments' => array('module_missing_message_fixer_form'),
'access arguments' => array('administer module missing message fixer'),
'file' => 'includes/module_missing_message_fixer.admin.inc',
);
return $items;
}
/**
* @return string[]
* Format: $[$column_key] = $cell
*/
function _module_missing_message_fixer_get_table_header() {
return array(
'name' => 'Name',
'type' => 'Type',
'filename' => 'Filename',
);
}
/**
* Produces one table row for each missing module.
*
* The table rows are suitable for drush and for the admin UI.
*
* @return array[]
* Format: $[$extension_name][$column_key] = $cell
*/
function _module_missing_message_fixer_get_table_rows() {
// These are special modules that have their own patches already.
// This will help eliminate some of the brute force of this module.
$special = array(
'adminimal_theme' => 'https://www.drupal.org/node/2763581',
'content' => 'https://www.drupal.org/node/2763555',
'field_collection_table' => 'https://www.drupal.org/node/2764331',
);
// Grab all the modules in the system table.
/** @var \DatabaseStatementBase|\DatabaseStatementInterface $query */
$query = db_query("SELECT filename, type, name FROM {system}");
$rows = array();
// Go through the query and check to see if the module exists in the directory.
foreach ($query->fetchAll() as $record) {
if ($record->name === 'default') {
continue;
}
// Add exception to this since content module seems to be ubercart sad only.
if ($record->name === 'content' && !module_exists('ubercart')) {
$rows[$record->name] = array(
'name' => $record->name,
'type' => $record->type,
'filename' => $record->filename,
);
continue;
}
if (array_key_exists($record->name, $special)) {
// Everyone else fails into here.
// Set the message.
$msg = t('The @module module has a patch. See <a href="@link" target="_blank">this issue</a> for more information. It <strong>WILL NOT</strong> be removed by Module Missing Message Fixer.', array(
'@module' => $record->name,
'@link' => $special[$record->name],
));
// Now print it!
drupal_set_message($msg, 'status', FALSE);
continue;
}
// Grab the checker.
$filename = drupal_get_filename(
$record->type,
$record->name,
$record->filename,
FALSE);
if ($filename === NULL) {
// Report this module in the table.
$rows[$record->name] = array(
'name' => $record->name,
'type' => $record->type,
'filename' => $record->filename,
);
continue;
}
$message = NULL;
$replacements = array(
'@name' => $record->name,
'@type' => $record->type,
'@file' => $filename,
);
if (!file_exists($filename)) {
// This case is unexpected, because drupal_get_filename() should take care
// of it already.
$message = 'The file @file for @name @type is missing.';
}
elseif (!is_readable($filename)) {
// This case is unexpected, because drupal_get_filename() should take care
// of it already.
$message = 'The file @file for @name @type is not readable.';
}
else {
// Verify if *.info file exists. See https://www.drupal.org/node/2789993#comment-12306555
$info_filename = dirname($filename) . '/' . $record->name . '.info';
$replacements['@info_file'] = $info_filename;
if (!file_exists($info_filename)) {
$message = 'The *.info file @info_file for @name @type is missing.';
}
elseif (!is_readable($info_filename)) {
$message = 'The *.info file @info_file for @name @type is not readable.';
}
}
if ($message !== NULL) {
// This case should never occur.
drupal_set_message(
t($message, $replacements),
'warning',
FALSE);
}
}
return $rows;
}