The signwriter module allows you to use custom truetype fonts for headings and menu items. It does this by creating images with the headings' text and replacing the original headings.
There are several ways in which you can use signwriter:
- It can be used as an input filter to replace headers in content,
- the provided hooks for replacing block and page titles can be enabled
- it can be used and configured by a theme, or
- it can be used by a theme and configured in drupal.
");
break;
}
}
/**
* Implementation of hook_menu().
*/
function signwriter_menu() {
$items = array();
$items['admin/settings/signwriter'] = array(
'title' => 'Signwriter',
'description' => 'Manage Signwriter profiles, for custom font headings.',
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array('signwriter_settings_form'),
'access arguments' => array('administer signwriter'),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/settings/signwriter/profile/add'] = array(
'title' => 'Add profile',
'description' => 'Add a new Signwriter profile.',
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_profile_form'),
'type' => MENU_NORMAL_ITEM,
'weight' => -8,
);
$items['admin/settings/signwriter/profile/%signwriter_profile'] = array(
'title' => 'Edit signwriter profile',
'description' => t('Edit Signwriter profile.'),
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_profile_form', 4),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/settings/signwriter/profile/%signwriter_profile/delete'] = array(
'title' => "Delete signwriter profile",
'description' => t('Delete Signwriter profile'),
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_confirm_delete_profile_form', 4),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/settings/signwriter/menuprofile/add'] = array(
'title' => 'Add menu profile',
'description' => 'Add a new Signwriter menu profile.',
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_menuprofile_form'),
'type' => MENU_NORMAL_ITEM,
'weight' => -8,
);
$items['admin/settings/signwriter/menuprofile/%signwriter_menuprofile'] = array(
'title' => 'Edit signwriter menu profile',
'description' => t('Edit Signwriter menu profile.'),
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_menuprofile_form', 4),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/settings/signwriter/menuprofile/%signwriter_menuprofile/delete'] = array(
'title' => "Delete signwriter menu profile",
'description' => t('Delete Signwriter menu profile'),
'file' => 'signwriter.admin.inc',
'page callback' => 'drupal_get_form',
'access arguments' => array('administer signwriter'),
'page arguments' => array('signwriter_confirm_delete_menuprofile_form', 4),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implementation of hook_theme().
*/
function signwriter_theme() {
return array(
'signwriter_text' => array(
'arguments' => array('text', 'signwriter'),
),
);
}
/**
* Implementation of hook_perm().
*/
function signwriter_perm() {
return array('administer signwriter', 'use PHP for signwriter pages');
}
/**
* Implementation of hook_cron().
* Cleans the signwriter cache if called explicitly or enough time has passed.
*/
function signwriter_cron($now = FALSE) {
$last_cleanup = variable_get('signwriter_last_cleanup', 0);
$cachecleanperiod = variable_get('signwriter_cachecleanperiod', '100 years');
if ($last_cleanup < strtotime("-$cachecleanperiod") || $now) {
$cachedir = variable_get('signwriter_cachedir', file_directory_path() .'/signwriter');
// Prevent removal of files not created by this module.
$cached_images = glob($cachedir .'/*-signwriter.*');
if (array_walk($cached_images, 'file_delete') && $now) {
drupal_set_message(t('!imagenum images have been cleared from the signwriter cache.', array('!imagenum' => count($cached_images))));
}
}
variable_set('signwriter_last_cleanup', time());
}
/**
* Implementation of hook_filter().
*/
function signwriter_filter($op, $delta = 0, $format = -1, $text = '') {
switch ($op) {
case 'list':
$profiles = signwriter_load_profiles();
$filters = array();
foreach ($profiles as $profile) {
if (isset($profile->pattern) && !empty($profile->pattern)) {
$filters[$profile->id] = $profile->name;
}
}
return $filters;
case 'description':
$profile = signwriter_load_profile($delta);
return t("Replaces anything matching the regular expression: %pattern with the image generated by the '%profilename' signwriter profile.", array('%pattern' => $profile->pattern, '%profilename' => $profile->name));
case "process":
// Make the profile global so the preg_replace_callback callback function can access it
global $signwriter_filter_profile;
$signwriter_filter_profile = signwriter_load_profile($delta);
$text = preg_replace_callback($signwriter_filter_profile->pattern, 'signwriter_filter_replace_callback', $text);
unset($signwriter_filter_profile);
return $text;
case 'no cache':
return TRUE;
default:
return $text;
}
}
/**
* Callback function for the call to preg_replace_callback()
* in the signwriter_filter() function.
* This is used to avoid using preg_replace with the e modifier,
* which would create a security issue
*/
function signwriter_filter_replace_callback($matches) {
global $signwriter_filter_profile;
return signwriter_title_convert($matches[0], $signwriter_filter_profile);
}
/**
* Implementation of hook_filter_tips().
*/
function signwriter_filter_tips($delta, $format, $long = FALSE) {
$profile = signwriter_load_profile($delta);
if ($long) {
return t('The signwriter filter will replace headings matching the regular expression %pattern with images according to the settings of the signwriter profile %profilename.', array('%pattern' => $profile->pattern, '@profile-admin-url' => url('admin/settings/signwriter/profile/'. $profile->id), '%profilename' => $profile->name));
}
else {
return t("The signwriter filter '%profilename' is enabled.", array('%profilename' => $profile->name));
}
}
/**
* Implementation of hook_theme_registry_alter()
*
* Add other menu module hooks here and provide respective 'signwritermenu_hook
*/
function signwriter_theme_registry_alter(&$theme_registry) {
// functions to be intercepted
//$replace_hooks = array('menu_item_link','menu_item','dhtml_menu_item','nice_menu');
$replace_hooks = array('menu_item_link');
foreach ($replace_hooks as $hook) {
// if hook is a function then install my interception proc
if (isset($theme_registry[$hook]['function'])) {
$intercept= 'function';
if ($theme_registry[$hook]['function'] == 'devel_themer_catch_function') {
$intercept = 'devel_function_intercept';
}
// Add interception proc for hook
$theme_registry[$hook]['signwriter_function'] = $theme_registry[$hook][$intercept];
$theme_registry[$hook][$intercept] = 'signwriter_' . $hook;
}
}
}
/**
* Internal: Return the original function name of hook $origin that
* was replaced by 'signwriter'
*/
function _signwriter_get_origfunc($origin) {
// load and cache theme registry
static $hooks = NULL;
if (!isset($hooks)) {
init_theme();
$hooks = theme_get_registry();
}
return $hooks[$origin]['signwriter_function'];
}
/**
* Return path to 'signwriter-dynamic.css'
*/
function signwriter_dynamiccss_path() {
$cachedir = variable_get('signwriter_cachedir', file_directory_path() .'/signwriter');
return $cachedir . "/signwriter-dynamic.css";
}
/**
* Creates 'signwriter-dynamic.css' based on the given heights
* @param $heights
* @return unknown_type
*/
function signwriter_update_menucss($cssfile, $heights) {
$handle = fopen($cssfile, "w");
foreach (array_keys($heights) as $height) {
$line = ".swm-h" . $height . " { height: " . $height . "px; }\n";
fwrite($handle, $line);
}
fclose($handle);
}
/**
* Implementation of interception hook
*/
function signwriter_menu_item_link($link) {
// Check that signwriter is to be used on this page.
if (!_signwriter_display_on_page()) {
return theme_menu_item_link($link);
}
// keep cached version of menu profiles around
static $menuprofiles = NULL;
// Check if there are any settings for menu items
$menusettings = variable_get('signwriter_menusettings', NULL);
if ($menusettings) {
// Check if we have settings for the given 'menu_name'
$profile_id = $menusettings[$link['menu_name']];
if ($profile_id) {
// read menu profiles if not already done
if (!isset($menuprofiles)) {
$menuprofiles = signwriter_load_menuprofiles();
}
$menulevel = $link['depth'];
$profile = $menuprofiles[$profile_id]->data[$menulevel];
}
}
// use signwriter to render menu item if prefs are set properly
if (isset($profile) && $profile) {
if (empty($link['localized_options'])) {
$link['localized_options'] = array();
}
// remove MENU_IS_LOCAL_TASK flag
$link['type'] &= ~MENU_IS_LOCAL_TASK;
// cache height info (css and menu item)
static $heightcache = NULL;
static $csscache = NULL;
// init cache and dynamic css file on first call
if (!isset($heightcache)) {
// add dynamic css file
$menucss = signwriter_dynamiccss_path();
drupal_add_css($menucss);
// get cache infos
$heightcache = cache_get("signwriter_menu");
$csscache = cache_get("signwriter_menucss");
// init height cache
if (!$heightcache) {
$heightcache = new stdClass();
$heightcache->data = array();
}
// init cache for dynamic css file generation or recreate dynamic css file
if (!$csscache) {
$csscache = new stdClass();
$csscache->data = array();
}
else if (!is_readable($menucss)) {
signwriter_update_menucss($menucss, $csscache->data);
}
}
// render image (or use cached version) and get info about it
$imageinfo = new stdClass();
$link['title'] = signwriter_theme_text($link['title'], $profile, NULL, TRUE, 'menu', $imageinfo);
// we didn't get a height?
if (!isset($imageinfo->height)) {
// we don't have heigth in cache?
if (!isset($heightcache->data[$imageinfo->key])) {
$size = getimagesize($imageinfo->path);
$height = $heightcache->data[$imageinfo->key] = $size[1]/3;
cache_set("signwriter_menu", $heightcache->data);
} else {
$height = $heightcache->data[$imageinfo->key];
}
}
else {
$height = $heightcache->data[$imageinfo->key] = $imageinfo->height;
cache_set("signwriter_menu", $heightcache->data);
}
if (!isset($csscache->data[$height])) {
$csscache->data[$height] = 1;
signwriter_update_menucss(signwriter_dynamiccss_path(), $csscache->data);
cache_set("signwriter_menucss", $csscache->data);
}
// make original renderer accept the already rendered text
$link['localized_options']['html'] = TRUE;
$link['localized_options']['attributes'] = array(
'class' => 'swm-h' . $height,
);
}
// call original renderer
$original_func = _signwriter_get_origfunc('menu_item_link');
$args = array($link);
return call_user_func_array($original_func, $args);
}
/**
* Internal: Returns variable if set in object or a default.
*/
function _signwriter_get_val($object, $variable, $default = NULL) {
$var = $object->$variable;
$return = (isset($var) && !is_null($var)) ? $var : $default;
return $return;
}
/**
* Internal: Returns paths to be searched for fonts.
*/
function _signwriter_get_fontpaths() {
// search drupal's base dir, files dir, and current theme dir, as well as user-supplied dirs for a font
$fontpaths = array('.', file_directory_path(), file_directory_path() .'/fonts', path_to_theme(), path_to_theme() .'/fonts');
$userfontpath = variable_get('signwriter_fontpath', '');
if ($userfontpath != '') {
array_push($fontpaths, $userfontpath);
}
if (ini_get('safe_mode')) {
// Convert relative to absolute paths.
foreach ($fontpaths as $index => $path) {
$fontpaths[$index] = realpath($path);
}
}
return $fontpaths;
}
/**
* Internal: Returns a list of all available fonts in the system, searching the font path and other likely spots.
*/
function _signwriter_available_fonts() {
$fontpath = _signwriter_get_fontpaths();
$fonts = array();
foreach ($fontpath as $dir) {
$ttfs = glob($dir .'/{*.ttf, *.TTF}', GLOB_BRACE);
if (!empty($ttfs)) {
foreach ($ttfs as $font) {
$fonts[$font] = basename($font);
}
}
}
return $fonts;
}
/**
* Internal: Returns a list of image types available in this PHP installation.
*
* @return
* An array in the form 'type' => 'type' (for convenient use in form select)
*/
function _signwriter_available_image_types() {
$types = imagetypes();
$return = array();
if ($types & IMG_GIF) $return['gif'] = 'gif';
if ($types & IMG_PNG) $return['png'] = 'png';
if ($types & IMG_JPG) $return['jpeg'] = 'jpeg';
return $return;
}
/**
* Internal: Determines the type of image by a filename's extension.
*
* @param $imagename
* The image filename.
*
* @return
* A string representing the image type (gif, png, jpeg, bmp, or xpm), or
* FALSE if the image type is not recognised.
*/
function _signwriter_get_image_type($imagename) {
$types = _signwriter_available_image_types();
if (isset($types['jpeg'])) {
$types['jpg'] = 'jpeg'; // synonym for jpeg
}
$imagename = basename($imagename);
foreach ($types as $extension => $imagetype) {
if (preg_match("/.*$extension$/", $imagename)) {
return $imagetype;
}
}
return FALSE;
}
/**
* Internal: Parse a six letter colour code into a colour array.
*
* @param $str
* A string of six hexadecimal digits.
*
* @return
* An array in the form (red, green, blue), or FALSE.
*/
function _signwriter_parse_colour($str) {
if (drupal_strlen($str) == 6) {
$red = intval(drupal_substr($str, 0, 2), 16);
$green = intval(drupal_substr($str, 2, 2), 16);
$blue = intval(drupal_substr($str, 4, 2), 16);
return array($red, $green, $blue);
}
return NULL;
}
/**
* Signwriter profile wildcard loader.
*/
function signwriter_profile_load($pid) {
if (!is_numeric($pid)) {
return FALSE;
}
$profile = signwriter_load_profile($pid);
if (!isset($profile->id)) {
return FALSE;
}
return $profile;
}
/**
* Load a profile from the database.
*
* @param $p
* The profile to load. This can be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*
* @return
* A signwriter profile object.
*/
function signwriter_load_profile($p) {
$id = is_numeric($p) ? $p : (!empty($p->id) ? $p->id : 0);
$name = is_string($p) ? $p : (!empty($p->name) ? $p->name : '');
if ($id) {
return db_fetch_object(db_query("SELECT * FROM {signwriter} WHERE id = %d", $id));
}
else {
return db_fetch_object(db_query("SELECT * FROM {signwriter} WHERE name = '%s'", $name));
}
}
/**
* Load zero or more signwriter profiles.
*
* @param $profiles
* Describes which profile(s) to load. If absent or NULL then load all
* profiles. Otherwise this should be an array, each element of which should
* be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*/
function signwriter_load_profiles($profiles = NULL) {
if (is_NULL($profiles)) {
$profiles = array();
$results = db_query("SELECT * FROM {signwriter}");
while ($profile = db_fetch_object($results)) {
$profiles[] = $profile;
}
}
else {
$results = array();
foreach ($profiles as $profile) {
$results[] = signwriter_load_profile($profile);
}
$profiles = $results;
}
return $profiles;
}
/**
* Checks for a given text if there is a signwriter image in the cache.
*
* @param $text
* Text to look up in cache
* @param $imagetype
* Type of the image
* @param $imagefile
* Reference to variable that will take the file name of the image
* @param $tag
* Tag used during md5 ID generation.
* Defaults to 'text' for signwriter.
* Can be used by other modules to distinguish images with same text
* that are needed for different purposes (i.e. menu items on different levels)
* @return
* true, if the file is found in cache
* false, if the file is not found in the cache
*/
function signwriter_image_check_cache($text, $profile, &$imagefile, $tag = 'text', &$imageinfo = NULL) {
$cachedir = variable_get('signwriter_cachedir', file_directory_path() .'/signwriter');
file_check_directory($cachedir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
$imagefileid = "$tag:$text-". serialize($profile);
$key = md5($imagefileid);
// for shorter filenames that are still unique and repeatable (for caching)
$imagefilename = preg_replace('/[^\w\d]/', '', $text) .'-'. $key .'-signwriter.'. $profile->imagetype;
$imagefile = "$cachedir/$imagefilename";
if ($imageinfo) {
$imageinfo->key = $key;
$imageinfo->path = $imagefile;
}
return is_readable($imagefile);
}
/**
* Copies color information from a profile to the image variable
*
* @param $image
* Image context handle
* @param $profile
* Profile to take color info from
*/
function _signwriter_image_prepare_state($image, $profile, $idx, $state = "") {
$image->fontfile[$idx] = _signwriter_get_val($profile, $state.'fontfile', $profile->fontfile);
$image->fontsize[$idx] = (float) _signwriter_get_val($profile, 'fontsize', $state ? $profile->fontsize : 20);
# shadow, colors, background
$image->bg[$idx] = _signwriter_parse_colour(_signwriter_get_val($profile, $state.'background', 'ffffff'));
$image->fg[$idx] = _signwriter_parse_colour(_signwriter_get_val($profile, $state.'foreground', '000000'));
$image->transparent[$idx] = _signwriter_get_val($profile, $state.'transparent', TRUE);
$image->drop_shadow[$idx] = _signwriter_get_val($profile, $state.'drop_shadow', 0);
$image->shadow_rgb[$idx] = _signwriter_parse_colour(_signwriter_get_val($profile, $state.'shadow_color', 'd2d2d2'));
$image->shadow_xoffset[$idx] = _signwriter_get_val($profile, $state.'shadow_xoffset', 0);
$image->shadow_yoffset[$idx] = _signwriter_get_val($profile, $state.'shadow_yoffset', 0);
$image->border[$idx] = _signwriter_get_val($profile, $state.'border', 0);
$image->border_rgb[$idx] = _signwriter_parse_colour(_signwriter_get_val($profile, $state.'border_color', 'd2d2d2'));
$image->border_radius[$idx] = $image->border[$idx] ? _signwriter_get_val($profile, $state.'border_radius', 0) : 0;
$image->bgimage[$idx] = _signwriter_get_val($profile, $state.'bgimage');
$image->bgimagetype[$idx] = _signwriter_get_image_type($image->bgimage[$idx]);
if ($image->bgimage[$idx] && !$image->bgimagetype[$idx]) {
drupal_set_message("Signwriter: unsupported image type: {$image->bgimage[$idx]}", 'error');
}
}
/**
* Create an image handle that contains all information needed during
* image creation.
*
* @param $profile
* Profile to take the initial information from
* @return
* Image context handle
*/
function signwriter_image_prepare($profile) {
$image = new stdClass();
# image dimensions, type
$image->width = _signwriter_get_val($profile, 'width');
$image->height = _signwriter_get_val($profile, 'height');
$image->maxwidth = _signwriter_get_val($profile, 'maxwidth');
$image->align = _signwriter_get_val($profile, 'textalign', 'left');
$image->imagetype = _signwriter_get_val($profile, 'imagetype', 'gif');
$image->xoffset = _signwriter_get_val($profile, 'xoffset', 0);
$image->yoffset = _signwriter_get_val($profile, 'yoffset', 0);
$image->multiline = $profile->multiline;
_signwriter_image_prepare_state($image, $profile, 0);
if ($profile->threestate) {
_signwriter_image_prepare_state($image, $profile, 1, "hov_");
_signwriter_image_prepare_state($image, $profile, 2, "act_");
}
return $image;
}
/**
* Performs dimension calculation upon the text and stores
* results in the image context handle
*
* @param $image
* Image context handle
* @param $text
* Text to process
* @return
* False, if there is an error
*/
function signwriter_image_calc_dimension($image, $text) {
$angle = 0;
do {
$done = TRUE;
// calculate the size of the text
$box = imagettfbbox($image->fontsize[0], $angle, $image->fontfile[0], $text);
if (!$box) {
drupal_set_message(t('Unable to generate a signwriter image with font !font at size !size.', array('!font' => $image->fontfile[$idx], '!size' => $image->fontsize)), 'error');
return FALSE;
}
// calculate the maximum possible hight of the text to properly vertically align multiple images
$biggest_box = imagettfbbox($image->fontsize[0], $angle, $image->fontfile[0], "ÅßåŮůÃÕÑÁÉÍÓÚÄËÏÖÜÀÈÌÒÙÂÊÎÔÛÇçabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
foreach (array(1, 3, 5, 7) as $i) {
$box[$i] = $biggest_box[$i];
}
// If there is a shadow add the offset to the size of the box.
if ($image->drop_shadow[0]) {
$textwidth = abs($box[2]) + abs($box[0]) + abs($image->shadow_xoffset[0]) + 5; // sometimes text is clipped. I don't know why, so I add 5px here...
$image->textheight = abs($box[1]) + abs($box[7]) + abs($image->shadow_yoffset[0]);
}
else {
$textwidth = abs($box[2]) + abs($box[0]) + 5;
$image->textheight = abs($box[1]) + abs($box[7]);
}
// if we exceed maxwidth, then use a smaller font size unless multiline is selected
// LS:. Start of the multiline magic
$lines = array();
$maxlinewidth = 0;
if (($image->maxwidth > 0) && ($textwidth > $image->maxwidth)) {
if ($image->multiline) {
$words = split(' ', $text);
$line = '';
foreach ($words as $word) {
$wordbox = imagettfbbox($image->fontsize[0], $angle, $image->fontfile[0], $line . $word);
$newwidth = $wordbox[4] - $wordbox[0] + abs($image->shadow_xoffset[0]) + 5; // +5: dirty hack to avoid clipping on some lines
if ($newwidth > $image->maxwidth) {
$lines[] = trim($line);
$tempbox = imagettfbbox($image->fontsize[0], $angle, $image->fontfile[0], $line);
$linewidth = $tempbox[4] - $tempbox[0] + abs($image->shadow_xoffset[0]) + 5;
if ($maxlinewidth < $linewidth) {
$maxlinewidth = $linewidth;
}
$line = '';
}
$line .= $word .' ';
}
$lines[] = trim($line);
$tempbox = imagettfbbox($image->fontsize[0], $angle, $image->fontfile[0], $line);
$linewidth = $tempbox[4] - $tempbox[0] + abs($image->shadow_xoffset[0]) + 5;
if ($maxlinewidth < $linewidth) {
$maxlinewidth = $linewidth;
}
// Check for and remove any empty lines.
$image->lines = array_values(array_filter($lines));
}
else {
$image->fontsize[0] = ($image->fontsize[0] * ($image->maxwidth / $textwidth)) - 0.5;
// we take an extra 0.5 to avoid endless recursing due to actual font size not decreasing
$done = FALSE;
}
}
else{
$image->lines = array($text);
}
}
while (!$done);
if ($maxlinewidth) {
$image->calcwidth = $image->width ? $image->width : $maxlinewidth;
}
else {
$image->calcwidth = $image->width ? $image->width : $textwidth + $image->xoffset;
}
$image->calcheight = $image->height ? $image->height : $image->textheight * count($image->lines) + $image->yoffset;
$image->box = $box;
return TRUE;
}
/**
* Creates the image resource to be drawn in
* Note:
* Width and height are not taken from the image context handle
* so we can use the API more flexible
* ToDo:
* Does not work properly with a given background image yet.
*
* @param $image
* Image context handle
* @param $width
* Width of image.
* @param $height
* Height of image
*/
function signwriter_create_image($image, $profile, $width, $height) {
// create the image
if (!empty($image->bgimage[$idx])) {
$imagefunction = 'imagecreatefrom'. $image->bgimagetype[$idx];
$image->im = $imagefunction($image->bgimage);
$width = imagesx($image->im);
$height = imagesy($image->im);
}
else {
$image->im = imagecreate($width, $height);
}
// pre allocate the colors
for ($idx = 0; $idx < ($profile->threestate ? 3 : 1); $idx++) {
$image->background[$idx] = imagecolorallocate($image->im, $image->bg[$idx][0], $image->bg[$idx][1], $image->bg[$idx][2]);
$image->foreground[$idx] = imagecolorallocate($image->im, $image->fg[$idx][0], $image->fg[$idx][1], $image->fg[$idx][2]);
if ($image->drop_shadow[$idx]) {
$image->shadow_color[$idx] = imagecolorallocate($image->im, $image->shadow_rgb[$idx][0], $image->shadow_rgb[$idx][1], $image->shadow_rgb[$idx][2]);
}
if ($image->border[$idx]) {
$image->border_color[$idx] = imagecolorallocate($image->im, $image->border_rgb[$idx][0], $image->border_rgb[$idx][1], $image->border_rgb[$idx][2]);
}
}
// set the transparent color
if ($image->transparent) {
// the background is transparent, but it should be the same colour as
// whatever the image is over, otherwise you will get jagged edges
// (unless you're using png).
imagecolortransparent($image->im, $image->background[0]);
}
}
/**
* Slightly crude way of getting a bordered effect
*/
function signwriter_bordered_text(&$im, $fontsize, $angle, $x, $y, $text_color, $fontfile, $line, $border_radius = 0, $border_color = 0) {
if ($border_radius) {
imagettftext($im, $fontsize, $angle, $x - $border_radius, $y, $border_color, $fontfile, $line); // Text
imagettftext($im, $fontsize, $angle, $x + $border_radius, $y, $border_color, $fontfile, $line); // Text
imagettftext($im, $fontsize, $angle, $x, $y - $border_radius, $border_color, $fontfile, $line); // Text
imagettftext($im, $fontsize, $angle, $x, $y + $border_radius, $border_color, $fontfile, $line); // Text
}
// Actual Text
imagettftext($im, $fontsize, $angle, $x, $y, $text_color, $fontfile, $line); // Text
}
/**
* Renders the text (stored in the image context handle) using
* the parameters in the image context handle.
*
* Fills a rectangle with the given dimension at the given offset
* if neither transparency nor a background image is used.
*
* @param $image
* image context handle
* @param $xofs
* @param $yofs
* @param $width
* @param $height
*/
function signwriter_render_text($image, $idx, $xofs, $yofs, $width, $height) {
$angle = 0;
$box = $image->box;
if (!$image->transparent && empty($image->bgimage[$idx])) {
imagefilledrectangle ($image->im, $xofs, $yofs , $xofs + $width, $yofs+ $height, $image->background[$idx]);
}
foreach ($image->lines as $n => $line) {
// align the text
$linebox = imagettfbbox($image->fontsize[$idx], $angle, $image->fontfile[$idx], $line);
$linewidth = $linebox[4] - $linebox[0];
switch ($image->align) {
case 'center':
case 'centre':
$x = $xofs + $image->xoffset + ($image->width - $linewidth) / 2;
break;
case 'right':
$x = $xofs + $image->width - $linewidth - $xoffset;
break;
default:
$x = $xofs + $image->xoffset;
}
$y = $yofs + $image->yoffset + $n * $image->textheight;
// This is so that the shadow doesn't go outside of the box.
if ($image->drop_shadow[$idx]) {
if ($image->shadow_xoffset[$idx] < 0 && $image->shadow_yoffset[$idx] < 0) {
imagettftext($image->im, $image->fontsize[$idx], $angle, $x + abs($box[0]),$y + abs($box[5]), $image->shadow_color[$idx], $image->fontfile[$idx], $line); // Shadow
signwriter_bordered_text($image->im, $image->fontsize[$idx], $angle, $x + abs($box[0]) - $image->shadow_xoffset[$idx], $y + abs($box[5]) - $image->shadow_yoffset[$idx], $image->foreground[$idx], $image->fontfile[$idx], $line, $image->border_radius[$idx], $image->border_color[$idx]); // Text
}
else if ($image->shadow_xoffset[$idx] > 0 && $image->shadow_yoffset[$idx] > 0) {
imagettftext($image->im, $image->fontsize[$idx], $angle, $x + abs($box[0]) + $image->shadow_xoffset[$idx], $y + abs($box[5]) + $image->shadow_yoffset[$idx], $image->shadow_color[$idx], $image->fontfile[$idx], $line); // Shadow
signwriter_bordered_text($image->im, $image->fontsize[$idx], $angle, $x + abs($box[0]), $y + abs($box[5]), $image->foreground[$idx], $image->fontfile[$idx], $line, $image->border_radius[$idx], $image->border_color[$idx]); // Text
}
else if ($image->shadow_xoffset[$idx] < 0 && $image->shadow_yoffset[$idx] > 0) {
imagettftext($image->im, $fontsize, $angle, $x + abs($box[0]), $y + abs($box[5]) + $image->shadow_yoffset[$idx], $image->shadow_color[$idx], $image->fontfile[$idx], $line); // Shadow
signwriter_bordered_text($image->im, $fontsize, $angle, $x + abs($box[0]) - $shadow_xoffset, $y + abs($box[5]), $image->foreground[$idx], $image->fontfile[$idx], $line, $image->border_radius[$idx], $image->border_color[$idx]); // Text
}
else if ($image->shadow_xoffset[$idx] > 0 && $image->shadow_yoffset[$idx] < 0) {
imagettftext($image->im, $fontsize, $angle, $x + abs($box[0]) + $image->shadow_xoffset[$idx], $y + abs($box[5]), $image->shadow_color[$idx], $image->fontfile[$idx], $line); // Shadow
signwriter_bordered_text($image->im, $fontsize, $angle, $x + abs($box[0]), $y + abs($box[5]) - $shadow_yoffset, $image->foreground[$idx], $image->fontfile[$idx], $line, $image->border_radius[$idx], $image->border_color[$idx]); // Text
}
}
else {
signwriter_bordered_text($image->im, $image->fontsize[$idx], $angle, $x + abs($box[0]), $y + abs($box[5]), $image->foreground[$idx], $image->fontfile[$idx], $line, $image->border_radius[$idx], $image->border_color[$idx]);
}
}
}
/**
* Finishes image by writing it to file and releasing the
* resources used by drawing.
*
* @param $image
* Image context handle
* @param $imagefile
* Path and filename where to write the image
*/
function signwriter_write_file($image, $imagefile) {
$imagefunction = "image$image->imagetype";
$imagefunction($image->im, $imagefile);
imagedestroy($image->im);
}
/**
* Generate a signwriter image using the given profile.
*
* @param $profile
* The signwriter profile to render the image with.
* The following field is required:
* - $profile->fontfile
* Which font to use. This can be a system path to a .ttf file, or the
* basename minus the .ttf extension of a .ttf font file in your font
* path or your drupal files directory.
* These fields are optional:
* - $profile->fontsize
* - $profile->foreground
* - $profile->background
* - $profile->width
* - $profile->height
* - $profile->maxwidth
* - $profile->imagetype
* - $profile->textalign
* - $profile->transparent
* - $profile->bgimage
* - $profile->xoffset
* - $profile->yoffset
* - $profile->drop_shadow
* - $profile->shadow_xoffset
* - $profile->shadow_yoffset
* - $profile->shadow_color
* - $profile->angle
* @param $text
* The text to display. Can contain html entities. For example, &
* will be displayed as &
*
* @return
* The absolute url to the image.
* */
function signwriter_image($text, $profile, $tag = 'text', &$imageinfo = NULL) {
$text = html_entity_decode($text, ENT_QUOTES);
$imagefile = '';
if (signwriter_image_check_cache($text, $profile, $imagefile, $tag, $imageinfo)) {
return $imagefile;
}
$image = signwriter_image_prepare($profile);
if (!$image) {
return '';
}
if (!signwriter_image_calc_dimension($image, $text)) {
return '';
}
if ($imageinfo) {
$imageinfo->height = $image->calcheight;
}
signwriter_create_image($image, $profile, $image->calcwidth, $image->calcheight * ($profile->threestate ? 3 : 1));
signwriter_render_text($image, 0, 0, 0, $image->calcwidth, $image->calcheight);
if ($profile->threestate) {
signwriter_render_text($image, 1, 0, $image->calcheight, $image->calcwidth, $image->calcheight);
signwriter_render_text($image, 2, 0, 2 * $image->calcheight, $image->calcwidth, $image->calcheight);
}
signwriter_write_file($image, $imagefile);
return $imagefile;
}
function _signwriter_strip_tags($text) {
return preg_replace('/<\/?(i|b|em|strong)>/', '', $text);
}
/**
* Turn a title into a signwriter image.
*
* @param $title
* The title text. Can be wrapped in html tags.
* @param $signwriter
* The signwriter profile to use. Can be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*
* @return
* HTML text to replace the title.
*/
function signwriter_title_convert($title, $signwriter) {
// Check that signwriter is to be used on this page.
if (!_signwriter_display_on_page()) {
return $title;
}
// Remove escapes from single quotes
$title = preg_replace("/\\\'/","'", $title);
$title = _signwriter_strip_tags($title);
preg_match('/^(<.*?>)*([^<]*)(<.*?>)*($)/s', $title, $matches);
$openingtags = $matches[1];
$titletext = $matches[2];
$closingtags = $matches[3];
return $openingtags . theme('signwriter_text', $titletext, $signwriter) . $closingtags;
}
/**
* Turn text into a signwriter image.
*
* @param $text
* The text to display
* @param $profile
* The signwriter profile to use. Can be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*
* @return
* HTML text to replace the input text.
*/
function signwriter_theme_text($text, $profile, $attributes = NULL, $threestate = TRUE, $tag = 'text', &$imageinfo = NULL) {
// Check that signwriter is to be used on this page.
if (!_signwriter_display_on_page()) {
return $text;
}
drupal_add_css(drupal_get_path('module', 'signwriter') .'/signwriter.css');
if (empty($profile->fontfile)) {
$profile = signwriter_load_profile($profile);
}
$unicode_rest = array();
$alttext = '"'. decode_entities($text);
preg_match_all('/[^[:print:]'. $profile->allowed_nonasciichars .']/u', $alttext, $unicode_rest);
if (mb_detect_encoding($alttext) === 'UTF-8' && $profile->allowed_nonasciichars !== 'all' && !empty($unicode_rest[0])) {
if (variable_get('signwriter_specialcharnotice', TRUE) && user_access('administer signwriter')) {
unset($profile->width);
unset($profile->maxwidth);
foreach ($unicode_rest[0] as $num => $character) {
$alttext = t($character .'rendered with profile '. $profile->name);
$unicode_rest[0][$num] = $character .' ('. theme( 'image', signwriter_image($character, $profile), $alttext, $alttext, array('style' => 'vertical-align:middle')) .')';
}
drupal_set_message(t("Signwriter notice: The unicode string '$text' will not be replaced by an
image because it contains following special characters = ". implode(',', $unicode_rest[0])
.'. If your font indeed does include these glyphs, paste them into the allowed charactersfield on the
!profilesettings.', array('!profilesettings' => l("profile's setting page", 'admin/settings/signwriter/profile/'. $profile->id))));
}
return $text;
}
$text = _signwriter_strip_tags($text);
$alttext= htmlspecialchars($text, ENT_QUOTES);
$titletext = $profile->use_title_text ? $alttext : '';
$imgsrc = signwriter_image($text, $profile, $tag, $imageinfo);
if ($threestate && $profile->threestate) {
$attributes['class'] = $attributes['class'] ? $attributes['class'] . ' signwriter signwriter-hover' : 'signwriter signwriter-hover';
}
else {
$attributes['class'] = $attributes['class'] ? $attributes['class'] . ' signwriter' : 'signwriter';
}
$output = $text;
if (!empty($imgsrc)) {
if ($profile->disable_span) {
$output = theme('image', $imgsrc, $alttext, $titletext, $attributes);
}
else {
$output = '' . $text . '' . theme('image', $imgsrc, $alttext, $titletext, $attributes);
}
}
return $output;
}
/**
* Turn text into a signwriter image.
*
* @param $text
* The text to display
* @param $profile
* The signwriter profile to use. Can be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*
* @return
* HTML text to replace the input text.
*/
function theme_signwriter_text($text, $profile, $attributes = NULL, $threestate = TRUE) {
return signwriter_theme_text($text, $profile, $attributes, $threestate);
}
/**
* Implementation of hook_preprocess_page().
*/
function signwriter_preprocess_page(&$variables) {
$pagehookprofile = variable_get('signwriter_pagehookprofile', 'disabled');
$title = &$variables['title'];
if ($pagehookprofile !== 'disabled' && !empty($title)) {
$title = signwriter_title_convert($title, $pagehookprofile);
}
}
/**
* Implementation of hook_preprocess_block().
*/
function signwriter_preprocess_block(&$variables) {
static $blockhookprofile = NULL;
if (!$blockhookprofile) {
$blockhookprofile = variable_get('signwriter_blockhookprofile', 'disabled');
}
$subject = &$variables['block']->subject;
if ($blockhookprofile !== 'disabled' && !empty($subject)) {
$subject = signwriter_title_convert($subject, $blockhookprofile);
}
}
function signwriter_panels_pane_content_alter (&$content, $pane, $args, $context) {
$panelstitlehookprofile = variable_get('signwriter_panelshooktitleprofile', 'disabled');
if ($panelstitlehookprofile !== 'disabled' && !empty($content->title)) {
$content->title = signwriter_title_convert($content->title, $panelstitlehookprofile);
}
}
/**
* Signwriter profile wildcard loader.
*/
function signwriter_menuprofile_load($pid) {
if (!is_numeric($pid)) {
return FALSE;
}
$profile = signwriter_load_menuprofile($pid);
if (!isset($profile->id)) {
return FALSE;
}
return $profile;
}
/**
* Load a profile from the database.
*
* @param $p
* The profile to load. This can be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*
* @return
* A signwriter profile object.
*/
function signwriter_load_menuprofile($p) {
$id = is_numeric($p) ? $p : (!empty($p->id) ? $p->id : 0);
$name = is_string($p) ? $p : (!empty($p->name) ? $p->name : '');
if ($id) {
return _signwriter_unpack_menuprofile(db_fetch_object(db_query("SELECT * FROM {signwriter_menu} WHERE id = %d", $id)));
}
else {
return _signwriter_unpack_menuprofile(db_fetch_object(db_query("SELECT * FROM {signwriter_menu} WHERE name = '%s'", $name)));
}
}
/**
* Load zero or more signwriter profiles.
*
* @param $profiles
* Describes which profile(s) to load. If absent or NULL then load all
* profiles. Otherwise this should be an array, each element of which should
* be one of:
* - a profile id
* - a profile name
* - a profile object with at least the name or id set
*/
function signwriter_load_menuprofiles($profiles = NULL) {
if (is_NULL($profiles)) {
$profiles = array();
$results = db_query("SELECT * FROM {signwriter_menu}");
while ($profile = db_fetch_object($results)) {
$profiles[$profile->id] = _signwriter_unpack_menuprofile($profile);
}
}
else {
$results = array();
foreach ($profiles as $profile) {
$results[] = signwriter_load_menuprofile($profile);
}
$profiles = $results;
}
return $profiles;
}
function _signwriter_unpack_menuprofile($in) {
$out = $in;
$out->data = unserialize($in->data);
return $out;
}
/**
* Work out whether or not to use signwriter on the given page
*/
function _signwriter_display_on_page() {
$type = variable_get('signwriter_pages_type', 0);
$list = variable_get('signwriter_pages_list', '');
// Match path if necessary
if ($list) {
if ($type < 2) {
$path = drupal_get_path_alias($_GET['q']);
// Compare with the internal and path alias (if any).
$page_match = drupal_match_path($path, $list);
if ($path != $_GET['q']) {
$page_match = $page_match || drupal_match_path($_GET['q'], $list);
}
// When $type has a value of 0, the signwriter is used on
// all pages except those listed in $list. When set to 1, it
// is used only on those pages listed in $list.
$page_match = !($type xor $page_match);
}
else {
$page_match = drupal_eval($list);
}
}
else {
$page_match = TRUE;
}
return $page_match;
}
//---------------------------------------------------------
//>>>> check implementation