/[drupal]/contributions/modules/signwriter/signwriter.module
ViewVC logotype

Contents of /contributions/modules/signwriter/signwriter.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.18 - (show annotations) (download) (as text)
Wed Sep 10 20:33:06 2008 UTC (14 months, 2 weeks ago) by agileware
Branch: MAIN
CVS Tags: HEAD
Changes since 1.17: +2 -2 lines
File MIME type: text/x-php
Made stip tags case insensitive
1 <?php
2 // $Id: signwriter.module,v 1.17 2008/09/08 02:07:34 agileware Exp $
3
4 /**
5 * Implementation of hook_help()
6 */
7 function signwriter_help($path, $arg) {
8 switch ($path) {
9 case 'admin/help#signwriter':
10 return t("<p>The signwriter module allows you to use custom truetype fonts for headings. It does this by creating images with the headings' text, and replacing the headings with the images.</p><p>There are several ways in which you can use signwriter: <ul><li>It can be used as an input filter to replace headers in content,</li><li>it can be used and configured by a theme, or</li><li>it can be used by a theme and configured in drupal.</li></ul></p>");
11 break;
12 }
13 }
14
15 /**
16 * Implementation of hook_menu().
17 */
18 function signwriter_menu() {
19 $items = array();
20
21 $items['admin/settings/signwriter'] = array(
22 'title' => 'Signwriter',
23 'description' => 'Manage Signwriter profiles, for custom font headings.',
24 'page callback' => 'signwriter_settings_page',
25 'access arguments' => array('administer signwriter'),
26 'type' => MENU_NORMAL_ITEM,
27 );
28
29 $items['admin/settings/signwriter/profile/add'] = array(
30 'title' => 'Add profile',
31 'description' => 'Add a new Signwriter profile.',
32 'page callback' => 'signwriter_profile_page',
33 'access arguments' => array('administer signwriter'),
34 'page arguments' => null,
35 'type' => MENU_NORMAL_ITEM,
36 'weight' => -8);
37
38 $items['admin/settings/signwriter/profile/%signwriter_profile'] = array(
39 'title' => 'signwriter profile',
40 'description' => t('Manage the Signwriter profile.'),
41 'page callback' => 'signwriter_profile_page',
42 'access arguments' => array('administer signwriter'),
43 'page arguments' => array(4),
44 'type' => MENU_NORMAL_ITEM,);
45
46 $items['admin/settings/signwriter/profile/%signwriter_profile/delete'] = array(
47 'title' => "Delete signwriter profile",
48 'description' => t('Delete the Signwriter profile'),
49 'page callback' => 'signwriter_confirm_delete_profile_page',
50 'access arguments' => array('administer signwriter'),
51 'page arguments' => array(4),
52 'type' => MENU_NORMAL_ITEM,);
53
54 return $items;
55 }
56
57 /**
58 * Implementation of hook_perm().
59 */
60 function signwriter_perm() {
61 return array('administer signwriter');
62 }
63
64 function signwriter_filter($op, $delta = 0, $format = -1, $text = '') {
65 switch ($op) {
66 case 'list':
67 $profiles = signwriter_load_profiles();
68 $filters = array();
69 foreach ($profiles as $profile) {
70 if (isset($profile->pattern) && !empty($profile->pattern)) {
71 $filters[$profile->id] = $profile->name;
72 }
73 }
74 return $filters;
75
76 case 'description':
77 $profile = signwriter_load_profile($delta);
78 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));
79
80 case "process":
81 $profile = signwriter_load_profile($delta);
82 return preg_replace($profile->pattern . 'e', 'signwriter_title_convert("$0", $profile)', $text);
83
84 case 'no cache':
85 return true;
86
87 default:
88 return $text;
89 }
90 }
91
92 /**
93 * Implementation of hook_filter_tips()
94 */
95 function signwriter_filter_tips($delta, $format, $long = false) {
96 $profile = signwriter_load_profile($delta);
97 if ($long) {
98 return t('The signwriter filter will replace headings matching the regular expression %pattern with images according to the settings of <a href="@profile-admin-url">the signwriter profile %profilename</a>.', array('%pattern' => $profile->pattern, '@profile-admin-url' => url('admin/settings/signwriter/profile/' . $profile->id), '%profilename' => $profile->name));
99 }
100 else {
101 return t("The signwriter filter '%profilename' is enabled.", array('%profilename' => $profile->name));
102 }
103 }
104
105 function _signwriter_db_fields() {
106 return array('id', 'name', 'pattern', 'fontfile', 'fontsize', 'imagetype', 'background', 'foreground','multiline', 'drop_shadow', 'shadow_color', 'shadow_xoffset', 'shadow_yoffset', 'transparent', 'bgimage', 'width', 'height', 'maxwidth', 'textalign', 'xoffset', 'yoffset');
107 }
108
109 function _signwriter_profile_whereclause($profile) {
110 if ($profile->id)
111 return "WHERE id = '{$profile->id}'";
112 if ($profile->name)
113 return "WHERE name = '{$profile->name}'";
114 if (is_numeric($profile))
115 return "WHERE id = '$profile'";
116 if (is_string($profile))
117 return "WHERE name = '$profile'";
118 return false;
119 }
120
121 /**
122 * Delete a signwriter profile without asking for confirmation.
123 * Returns the user to the main signwriter settings page.
124 *
125 * @param $profile
126 * The profile to delete. This can be one of:
127 * - a profile id
128 * - a profile name
129 * - a profile object with at least the name or id set
130 */
131 function signwriter_delete_profile($profile) {
132 if ($where = _signwriter_profile_whereclause($profile)) {
133 db_query("DELETE FROM {signwriter} $where");
134 drupal_set_message(t("Deleted the '@name' profile.", array('@name' => $profile->name)));
135 }
136 drupal_goto('admin/settings/signwriter');
137 }
138
139 /**
140 * Ask for user confirmation before deleting a profile.
141 *
142 * @param $profile
143 * The profile to delete. This can be one of:
144 * - a profile id
145 * - a profile name
146 * - a profile object with at least the name or id set
147 */
148 function signwriter_confirm_delete_profile_page($profile) {
149 print theme('page', drupal_get_form('signwriter_confirm_delete_profile_form', $profile));
150 }
151
152 function signwriter_confirm_delete_profile_form($form_state, $profile) {
153 // $profile = signwriter_load_profile($profile);
154 $form['id'] = array('#type' => 'value', '#value' => $profile->id);
155 $form['#submit'] = array('_signwriter_confirm_delete_profile_submit');
156 return confirm_form($form,
157 t('Are you sure you want to delete the \'%title\' profile?', array('%title' => $profile->name)),
158 'admin/settings/signwriter',
159 t('Deleting a profile cannot be undone.'),
160 t('Delete'),
161 t('Cancel'));
162 }
163
164 function _signwriter_confirm_delete_profile_submit($form, $form_state) {
165 $profile = signwriter_load_profile($form_state['values']['id']);
166 return signwriter_delete_profile($profile);
167 }
168
169 /**
170 * Save a profile or update a profile in the database.
171 *
172 * @param $profile
173 * The profile object to save
174 */
175 function signwriter_save_profile($profile) {
176 $profile->is_new = false;
177 if (empty($profile->id)) {
178 $profile->is_new = true;
179 }
180
181 $fields = _signwriter_db_fields();
182 foreach ($fields as $fieldname) {
183 if (!is_null($profile->$fieldname)) {
184 $keys[] = $fieldname;
185 $value_keywords[] = "'%s'";
186 $values[] = $profile->$fieldname;
187 }
188 }
189
190 if (count($keys)) {
191 if ($profile->is_new) {
192 $keys = implode(', ', $keys);
193 $value_keywords = implode(', ', $value_keywords);
194 db_query("INSERT INTO {signwriter} ($keys) VALUES ($value_keywords)", $values);
195 $result = db_result(db_query("SELECT LAST_INSERT_ID()"));
196 return $result;
197 }
198 else {
199 $where = _signwriter_profile_whereclause($profile);
200 foreach ($keys as $key) {
201 $assignments[] = "$key = '%s'";
202 }
203 $assignments = implode(', ', $assignments);
204 db_query("UPDATE {signwriter} SET $assignments $where", $values);
205 return FALSE;
206 }
207 }
208 }
209
210 /**
211 * Load a profile from the database.
212 *
213 * @param $profile
214 * The profile to load. This can be one of:
215 * - a profile id
216 * - a profile name
217 * - a profile object with at least the name or id set
218 *
219 * @return
220 * A signwriter profile object.
221 */
222 function signwriter_load_profile($profile) {
223 if ($where = _signwriter_profile_whereclause($profile)) {
224 $fields = implode(', ', _signwriter_db_fields());
225 return db_fetch_object(db_query("SELECT $fields FROM {signwriter} $where"));
226 }
227 }
228
229 /**
230 * Load zero or more signwriter profiles.
231 *
232 * @param $profiles
233 * Describes which profile(s) to load. If absent or null then load all
234 * profiles. Otherwise this should be an array, each element of which should
235 * be one of:
236 * - a profile id
237 * - a profile name
238 * - a profile object with at least the name or id set
239 */
240 function signwriter_load_profiles($profiles = null) {
241 if (is_null($profiles)) {
242 $profiles = array();
243 $results = db_query("SELECT %s FROM {signwriter}", implode(', ', _signwriter_db_fields()));
244 while ($profile = db_fetch_object($results)) {
245 $profiles[] = $profile;
246 }
247 }
248 else {
249 $results = array();
250 foreach ($profiles as $profile) {
251 $results[] = signwriter_load_profile($profile);
252 }
253 $profiles = $results;
254 }
255 return $profiles;
256 }
257
258 /**
259 * The signwriter profile settings form page.
260 *
261 * @param $p
262 * The profile object to edit (optional).
263 */
264 function signwriter_profile_page($p = null) {
265 $output = drupal_get_form('_signwriter_profile_form', $p);
266 print theme('page', $output);
267 }
268
269 function _signwriter_profile_form(&$form_state, $p = null) {
270 drupal_add_css('misc/farbtastic/farbtastic.css', 'module', 'all', FALSE);
271 drupal_add_js('misc/farbtastic/farbtastic.js');
272 drupal_add_js(drupal_get_path('module', 'signwriter') . '/color.js');
273
274 // TODO: add a preview, and maybe an 'Apply' button which submits the page
275 // then returns to it, rather than back to the main signwriter settings page.
276 $profileid = empty($p->id) ? null : $p->id;
277 $form = array();
278 $form['id'] = array('#type' => 'value', '#value' => $profileid);
279 $form['name'] = array(
280 '#type' => 'textfield',
281 '#title' => t('Profile Name'),
282 '#required' => true,
283 '#default_value' => $p->name,
284 '#size' => 40,
285 );
286
287 $form['font'] = array('#type' => 'fieldset', '#collapsible' => true, '#collapsed' => false, '#title' => t('Text'));
288 $all_fonts = signwriter_available_fonts();
289
290 // Match font names selected in an older version of signwriter
291 $default_font = $p->fontfile;
292 foreach ($all_fonts as $font) {
293 if (strpos($default_font, '/') === false && strpos($default_font, '\\') === false && preg_match('/' . $default_font . '.ttf/', $font)) {
294 $default_font = $font;
295 break;
296 }
297 }
298
299 $form['font']['fontfile'] = array(
300 '#title' => t('Font'),
301 '#type' => 'select',
302 '#options' => $all_fonts,
303 '#default_value' => $default_font,
304 '#description' => t('These fonts have been found on the host system. The directories searched for fonts were @fontsearch. To change the font search path, go to !signwriter_admin.', array('@fontsearch' => implode(', ', signwriter_get_fontpath()), '!signwriter_admin' => l('the signwriter settings page', 'admin/settings/signwriter'))),
305 );
306 $form['font']["fontsize"] = array(
307 '#type' => 'textfield',
308 '#title' => t("Font Size"),
309 '#default_value' => _signwriter_get_val($p->fontsize, ''),
310 '#description' => t("If you define 'Max Width' below then this size may be overridden in order to fit the text within the given width. Defaults to 20."),
311 '#size' => 4,
312 );
313 $form['font']["foreground"] = array(
314 '#type' => 'textfield',
315 '#title' => t("Font Colour"),
316 '#default_value' => $p->foreground,
317 '#description' => t("This should be six hexadecimal digits, such as ff0000 for red, 00ff00 for green, or 0000ff for blue."),
318 '#size' => 6,
319 '#maxlength' => 6,
320 );
321 $form['font']['foreground-farb'] = array('#value' => '<div id="foreground-farb"></div>');
322 $form['font']['multiline'] = array(
323 '#type' => 'checkbox',
324 '#title' => t("Multiline"),
325 '#default_value' => is_null($p->multiline) ? true : $p->multiline,
326 '#description' => t("If enabled and the max width is set, the text will roll over to a new line when max width is reached, otherwise the font size will be decreased to fit the text in one line."),
327 );
328
329 $form['shadow_settings'] = array('#type' => 'fieldset', '#collapsible' => true, '#collapsed' => false, '#title' => t('Drop Shadow'));
330 $form['shadow_settings']['drop_shadow'] = array(
331 '#type' => 'checkbox',
332 '#title' => t("Drop Shadow"),
333 '#default_value' => is_null($p->drop_shadow) ? false : $p->drop_shadow,
334 '#description' => t("If enabled, the text will cast a shadow."),
335 );
336 $form['shadow_settings']["shadow_color"] = array(
337 '#type' => 'textfield',
338 '#title' => t("Shadow Colour"),
339 '#default_value' => $p->shadow_color,
340 '#description' => t("This should be six hexadecimal digits, such as ff0000 for red, 00ff00 for green, or 0000ff for blue."),
341 '#size' => 6,
342 '#maxlength' => 6,
343 );
344 $form['shadow_settings']['shadow_color-farb'] = array('#value' => '<div id="shadow-color-farb"></div>');
345 $form['shadow_settings']["shadow_xoffset"] = array(
346 '#type' => 'textfield',
347 '#title' => t("Shadow X Offset"),
348 '#default_value' => is_null($p->shadow_xoffset) ? 5 : $p->shadow_xoffset,
349 '#description' => t("The horizontal distance of the shadow from the actual text. A negative value will put the shadow to the left of the text."),
350 '#size' => 4,
351 );
352 $form['shadow_settings']["shadow_yoffset"] = array(
353 '#type' => 'textfield',
354 '#title' => t("Shadow Y Offset"),
355 '#default_value' => is_null($p->shadow_yoffset) ? 5 : $p->shadow_yoffset,
356 '#description' => t("The vertical distance of the shadow from the actual text. A negative value will put the shadow above the text."),
357 '#size' => 4,
358 );
359
360 $form['background_settings'] = array('#type' => 'fieldset', '#collapsible' => true, '#collapsed' => false, '#title' => t('Background'));
361 $form['background_settings']["transparent"] = array(
362 '#type' => 'checkbox',
363 '#title' => t("Transparent"),
364 '#default_value' => is_null($p->transparent) ? true : $p->transparent,
365 '#description' => t("If enabled, then the background colour selected below will be made transparent in the generated image."),
366 );
367 $form['background_settings']["background"] = array(
368 '#type' => 'textfield',
369 '#title' => t("Background Colour"),
370 '#default_value' => $p->background,
371 '#description' => t("This should be six hexadecimal digits, such as ff0000 for red, 00ff00 for green, or 0000ff for blue. To avoid jagged fonts when using transparency, make sure that this colour is the same as the page background colour. If you are using a background image and transparency then this colour will be made transparent in the source background image."),
372 '#size' => 6,
373 '#maxlength' => 6,
374 );
375 $form['background_settings']['background-farb'] = array('#value' => '<div id="background-farb"></div>');
376 $form['background_settings']["bgimage"] = array(
377 '#type' => 'textfield',
378 '#title' => t("Background Image"),
379 '#default_value' => $p->bgimage,
380 '#description' => t("Path to the background image to use, relative to your drupal directory. Leave blank to not use a background image."),
381 '#size' => 40,
382 );
383
384 $form['layout'] = array('#type' => 'fieldset', '#collapsible' => true, '#collapsed' => false, '#title' => t('Image Layout'));
385 $form['layout']["width"] = array(
386 '#type' => 'textfield',
387 '#title' => "Width",
388 '#default_value' => _signwriter_get_val($p->width, ''),
389 '#description' => t("Set the width of the image in pixels. Leave blank to have the width automatically assigned, or if you're using a background image."),
390 '#size' => 4,
391 );
392 $form['layout']["height"] = array(
393 '#type' => 'textfield',
394 '#title' => "Height",
395 '#default_value' => _signwriter_get_val($p->height, ''),
396 '#description' => t("Set the height of the image in pixels. Leave blank to have the height automatically assigned, or if you're using a background image."),
397 '#size' => 4,
398 );
399 $form['layout']["maxwidth"] = array(
400 '#type' => 'textfield',
401 '#title' => "Max Width",
402 '#default_value' => _signwriter_get_val($p->maxwidth, ''),
403 '#description' => t("This value is in pixels. If it is set then the text size will be decreased so that the text fits within the given width. Leave this blank to have no maximum."),
404 '#size' => 4,
405 );
406 $form['layout']["textalign"] = array(
407 '#type' => 'select',
408 '#title' => t("Text Align"),
409 '#default_value' => $p->textalign,
410 '#options' => array('left' => 'left', 'center' => 'center', 'right' => 'right'),
411 '#description' => t("Text Align only makes sense if your image is wider than the text. To make this happen either assign a background image, or set the width."),
412 );
413 $form['layout']["xoffset"] = array(
414 '#type' => 'textfield',
415 '#title' => t("X Offset"),
416 '#default_value' => _signwriter_get_val($p->xoffset, ''),
417 '#description' => t("Adds to the distance from the left of the image to the start of the text (or from the right of the image in the case of right-aligned text)."),
418 '#size' => 4,
419 );
420 $form['layout']["yoffset"] = array(
421 '#type' => 'textfield',
422 '#title' => t("Y Offset"),
423 '#default_value' => _signwriter_get_val($p->yoffset, ''),
424 '#description' => t("Adds to the distance from the top of the image to the baseline of the text (or something like that)."),
425 '#size' => 4,
426 );
427
428 $form["imagetype"] = array(
429 '#type' => 'select',
430 '#title' => t("Image Type"),
431 '#default_value' => $p->imagetype,
432 '#description' => t("For transparency png is best, but it doesn't work in IE without hacks (see http://webfx.eae.net/dhtml/pngbehavior/pngbehavior.html for one such hack), so gif is a good alternative."),
433 '#options' => signwriter_available_image_types(),
434 );
435
436 $form["pattern"] = array(
437 '#type' => 'textfield',
438 '#title' => t('Input Filter Pattern'),
439 '#default_value' => $p->pattern,
440 '#description' => t("If this pattern is defined then this profile will be available as an input filter which you can enable on the <a href='@input-formats-url'>%inputformats</a> page. When the filter is enabled, anything matching this pattern will be replaced with a signwriter image. The pattern should be a perl regular expression. For example, to replace all headings, use: <code>/&lt;h.*?&gt;.*?&lt;\/h.*?&gt;/s</code>. To replace only h1 headings, use <code>/&lt;h1.*&gt;.*?&lt;\/h1&gt;/s</code>. To define a custom pseudo-html tag (such as &lt;signwriter&gt;), use: <code>/&lt;signwriter&gt;.*?&lt;\/signwriter&gt;/</code>", array('@input-formats-url' => url('admin/settings/filters'), '%inputformats' => 'Administer >> Site configuration >> Input formats')),
441 '#size' => 20,
442 );
443
444 $form['save'] = array('#type' => 'submit', '#value' => t('Save'));
445 $form['submit'] = array('#type' => 'submit', '#value' => t('Save and edit'));
446 // Don't add the delete button if they are on the add new profile page
447 // Otherwise clicking the button will return page not found due to there not being a profile to delete.
448 if (request_uri() != '/admin/settings/signwriter/profile/add') {
449 $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
450 }
451
452 $form['#submit'] = array('_signwriter_profile_submit');
453 $form['#validate'] = array('_signwriter_profile_validate');
454 return $form;
455 }
456
457 function _signwriter_profile_submit($form, &$form_state) {
458 $profile = (object)$form_state['values'];
459 if ($form_state['clicked_button']['#value'] == 'Delete') {
460 $form_state['redirect'] = 'admin/settings/signwriter/profile/' . $profile->id . '/delete';
461 }
462 else {
463 $newid = signwriter_save_profile($profile);
464 drupal_set_message(t("Fontimage profile '@name' saved.", array('@name' => $profile->name)));
465 if ($form_state['clicked_button']['#value'] == 'Save and edit') {
466 // If a new profile has been created redirect from the add page to the new profile's page, otherwise just reload.
467 if ($newid !== FALSE) {
468 $form_state['redirect'] = 'admin/settings/signwriter/profile/' . $newid;
469 }
470 }
471 else {
472 $form_state['redirect'] = 'admin/settings/signwriter';
473 }
474
475 }
476 }
477
478 function _signwriter_profile_validate($form, $form_state) {
479 if ($form_state['values']['drop_shadow']) {
480 if ($form_state['values']['shadow_xoffset'] == 0 && $form_state['values']['shadow_yoffset'] == 0) {
481 form_set_error('shadow_xoffset', t('If using a drop shadow, the Shadow X Offset and Shadow Y Offset values must not both be zero.'));
482 form_set_error('shadow_yoffset', t('If using a drop shadow, the Shadow X Offset and Shadow Y Offset values must not both be zero.'));
483 }
484 }
485 }
486
487 /**
488 * The main admin>>settings>>signwriter page
489 */
490 function signwriter_settings_page() {
491 if (!function_exists('imagetypes')) {
492 print theme('page', "It appears that you do not have the GD image library installed. GD is enabled by default in PHP >= 4.3, but can be enabled at compile time in earlier versions. If your php installation is on windows, try uncommenting the line which reads 'extension=php_gd2.dll' in your php.ini. For more information see <a href='http://php.net/image'>php's Image library</a>.");
493 }
494 else {
495 $profiles = signwriter_load_profiles();
496 $rows = array();
497 foreach ($profiles as $profile) {
498 $links[] = l($profile->name, 'admin/settings/signwriter/profile/'. $profile->id);
499 $rows[] = array('name' => check_plain($profile->name),
500 'edit' => l(t('edit'), 'admin/settings/signwriter/profile/' . $profile->id),
501 'delete' => l(t('delete'), "admin/settings/signwriter/profile/$profile->id/delete"));
502 }
503 if (empty($rows)) {
504 $rows[] = array(array('data' => t('No profiles'), 'colspan' => '3', 'class' => 'message'));
505 }
506
507 $rows[] = array(array('data' => l(t('Add a profile'), 'admin/settings/signwriter/profile/add'), 'colspan' => '3'));
508 $header = array(array('data' => t('Profiles'), 'colspan' => '3'));
509 $output = '<p>' . theme('table', $header, $rows) . '</p>';
510 $output .= '<p>' . drupal_get_form('signwriter_settings_form') . '</p>';
511 print theme('page', $output);
512 }
513 }
514
515 function signwriter_settings_form($form_state) {
516 $form = array();
517 $form['settings'] = array('#type' => 'fieldset', '#title' => t('Settings'), '#collapsible' => true, '#collapsed' => false);
518 $form['settings']['cachedir'] = array(
519 '#type' => 'textfield',
520 '#title' => t('Cache Directory'),
521 '#description' => t('This is the directory that signwriter will store its generated images in. It should be a path relative to the drupal base directory. The default is \'signwriter-cache\'. If your files directory is publicly accessible, then another good option would be \'files/signwriter-cache\'. Make sure that your webserver process is able to create and write to this directory. Files can be deleted from this directory at any time.'),
522 '#default_value' => variable_get('signwriter_cachedir', 'signwriter-cache'),
523 );
524
525 $description = t('Add a : separated list of directories to search for your font files. Signwriter will automatically search the drupal directory, your files directory, and your current theme\'s directory.');
526 if (ini_get('safe_mode')) {
527 // full path must be specified in safe mode since we can't putenv
528 $description .= t(' WARNING: this will be ignored because your PHP installation is in safe mode. You will need to use the full path to your fonts in any Signwriter profiles.');
529 }
530 $form['settings']['fontpath'] = array(
531 '#type' => 'textfield',
532 '#title' => t('Font Search Path'),
533 '#default_value' => variable_get('signwriter_fontpath', ''),
534 '#description' => $description,
535 );
536 $form['submit'] = array('#type' => 'submit', '#value' => t('Save Settings'));
537
538 $form['#submit'] = array('signwriter_settings_form_submit');
539
540 return $form;
541 }
542
543 function signwriter_settings_form_submit($form, &$form_state) {
544 variable_set('signwriter_cachedir', $form_state['values']['cachedir']);
545 variable_set('signwriter_fontpath', $form_state['values']['fontpath']);
546 drupal_set_message(t('Signwriter settings updated.'));
547 }
548
549 function _signwriter_get_val($var, $default = null) {
550 return empty($var) ? $default : $var;
551 }
552
553 /**
554 * Generate a signwriter image using the given profile.
555 *
556 * @param $profile
557 * The signwriter profile to use to render the image. The following fields
558 * are required:
559 * - $profile->text
560 * The text to display. Can contain html entities. For example, &amp;
561 * will be displayed as &
562 * - $profile->fontfile
563 * Which font to use. This can be a system path to a .ttf file, or the
564 * basename minus the .ttf extension of a .ttf font file in your font
565 * path or your drupal files directory.
566 * These fields are optional:
567 * - $profile->fontsize
568 * - $profile->foreground
569 * - $profile->background
570 * - $profile->width
571 * - $profile->height
572 * - $profile->maxwidth
573 * - $profile->imagetype
574 * - $profile->textalign
575 * - $profile->transparent
576 * - $profile->bgimage
577 * - $profile->xoffset
578 * - $profile->yoffset
579 *
580 * @return
581 * The absolute url to the image.
582 */
583 function signwriter_url($profile) {
584 $htmltext = _signwriter_get_val($profile->text, '');
585 $text = html_entity_decode($htmltext, ENT_QUOTES);
586 $fontfile = $profile->fontfile;
587 $size = _signwriter_get_val($profile->fontsize, 20);
588 $fg = (is_string($profile->foreground)) ? _signwriter_parse_colour($profile->foreground) : array(0, 0, 0);
589 $bg = (is_string($profile->background)) ? _signwriter_parse_colour($profile->background) : array(255, 255, 255);
590 $shadow_rgb = (is_string($profile->shadow_color)) ? _signwriter_parse_colour($profile->shadow_color) : array(210, 210, 210);
591 $width = _signwriter_get_val($profile->width);
592 $height = _signwriter_get_val($profile->height);
593 $maxwidth = ($profile->maxwidth > 0) ? $profile->maxwidth : null;
594 $imagetype = _signwriter_get_val($profile->imagetype, 'gif');
595 $cachedir = variable_get('signwriter_cachedir', 'signwriter-cache');
596 $align = _signwriter_get_val($profile->textalign, 'left');
597 $transparent = (isset($profile->transparent)) ? $profile->transparent : true;
598 if ($profile->bgimage) {
599 $backgroundimage = $profile->bgimage;
600 $backgroundimagename = basename($backgroundimage);
601 if (!($bgimagetype = signwriter_get_image_type($backgroundimagename))) {
602 drupal_set_message("Signwriter: unsupported image type: $backgroundimage", 'error');
603 return '';
604 }
605 }
606 $xoffset = _signwriter_get_val($profile->xoffset, 0);
607 $yoffset = _signwriter_get_val($profile->yoffset, 0);
608 $shadow_xoffset = _signwriter_get_val($profile->shadow_xoffset, 0);
609 $shadow_yoffset = _signwriter_get_val($profile->shadow_yoffset, 0);
610
611
612 file_check_directory($cachedir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
613
614 // can't putenv in safe mode
615 if (!ini_get('safe_mode')) {
616 $fontpath = signwriter_get_fontpath();
617 $path_delimiter = (substr(PHP_OS, 0, 3) == 'WIN') ? ';' : ':';
618 putenv('GDFONTPATH=' . implode($path_delimiter, $fontpath));
619 }
620 $fontname = basename($fontfile);
621
622 $filename = serialize($profile);
623 $filename = "text:$text-" . $filename;
624
625 //for shorter filenames that are still unique and repeatable (for caching)
626 //TODO: fix question marks in text when md5 filenames aren't used
627 $filename = $urlfilename = md5($filename) . '.' . $imagetype;
628
629 $file = "$cachedir/$filename";
630
631 if (!is_readable($file)) {
632 $angle = 0;
633
634 // calculate the size of the text
635 $box = imagettfbbox($size, $angle, $fontfile, $text);
636 if (!$box) {
637 drupal_set_message(t("Unable to generate a signwriter image. Is your font set correctly in the %profilename signwriter profile?", array('%profilename' => $profile->name)), 'error');
638 return '';
639 }
640
641 // calculate the maximum possible hight of the text to properly vertically align multiple images
642 $biggest_box = imagettfbbox($size, $angle, $fontfile, "HÅßåŮůÃÕÑÁÉÍÓÚÄËÏÖÜÀÈÌÒÙÂÊÎÔÛÇçjgpq");
643 foreach (array(1,3,5,7) as $i) {
644 $box[$i] = $biggest_box[$i];
645 }
646
647 // If there is a shadow add the offset to the size of the box.
648 if ($profile->drop_shadow) {
649 $textwidth = abs($box[2]) + abs($box[0]) + abs($shadow_xoffset) + 5; // sometimes text is clipped. I don't know why, so I add 5px here...
650 $textheight = abs($box[1]) + abs($box[7]) + abs($shadow_yoffset);
651 }
652 else {
653 $textwidth = abs($box[2]) + abs($box[0]) + 5;
654 $textheight = abs($box[1]) + abs($box[7]);
655 }
656
657
658 // if we exceed maxwidth, then use a smaller font size unless multiline is selected
659 // LS:. Start of the multiline magic
660 $lines = array();
661 $maxlinewidth = 0;
662 if ($maxwidth && ($textwidth > $maxwidth)) {
663 if ($profile->multiline) {
664 $words = split(' ', $text);
665 $line = '';
666 foreach ( $words as $word ) {
667 $wordbox = imagettfbbox ( $size, 0, $fontfile, $line . $word );
668 $newwidth = $wordbox[4] - $wordbox[0] + abs($shadow_xoffset) + 5; // +5: dirty hack to avoid clipping on some lines
669 if ($newwidth > $maxwidth){
670 $lines[] = trim ($line);
671 $tempbox = imagettfbbox ( $size, 0, $fontfile, $line );
672 $linewidth = $tempbox[4] - $tempbox[0] + abs($shadow_xoffset) + 5;
673 if ($maxlinewidth < $linewidth) {
674 $maxlinewidth = $linewidth;
675 }
676 $line = '';
677 }
678 $line .= $word . ' ';
679 }
680 $lines[] = trim($line);
681 $tempbox = imagettfbbox ( $size, 0, $fontfile, $line );
682 $linewidth = $tempbox[4] - $tempbox[0] + abs($shadow_xoffset) + 5;
683 if ($maxlinewidth < $linewidth) {
684 $maxlinewidth = $linewidth;
685 }
686 // Check for and remove any empty lines.
687 $lines = array_values(array_filter($lines));
688 }
689 else {
690 $profile->fontsize = ($size * ($maxwidth / $textwidth)) - 0.5; // we take an extra 0.5 to avoid endless recursing due to actual font size not decreasing
691 return signwriter_url($profile);
692 }
693 }
694 else{
695 $lines[] = $text;
696 }
697
698 if ($maxlinewidth) {
699 $width = $width ? $width : $maxlinewidth;
700 }
701 else {
702 $width = $width ? $width : ($maxwidth ? $maxwidth : $textwidth + $xoffset);
703 }
704 $height = $height ? $height : $textheight * count($lines) + $yoffset;
705
706 // create the image
707 if ($backgroundimage) {
708 $imagefunction = 'imagecreatefrom' . $bgimagetype;
709 $im = $imagefunction($backgroundimage);
710 $width = imagesx($im);
711 $height = imagesy($im);
712 }
713 else {
714 $im = imagecreate($width, $height);
715 }
716
717 $background = imagecolorallocate($im, $bg[0], $bg[1], $bg[2]);
718 $foreground = imagecolorallocate($im, $fg[0], $fg[1], $fg[2]);
719 $shadow_color = imagecolorallocate($im, $shadow_rgb[0], $shadow_rgb[1], $shadow_rgb[2]);
720
721 if ($transparent) {
722 // the background is transparent, but it should be the same colour as
723 // whatever the image is over, otherwise you will get jagged edges
724 // (unless you're using png).
725 imagecolortransparent($im, $background);
726 }
727
728 foreach($lines as $n => $line){
729 // align the text
730 $linebox = imagettfbbox($size, 0, $fontfile, $line);
731 $linewidth = $linebox[4] - $linebox[0];
732 switch ($align) {
733 case 'center':
734 case 'centre':
735 $x = $xoffset + ($width - $linewidth) / 2;
736 break;
737 case 'right':
738 $x = $width - $linewidth - $xoffset;
739 break;
740 default:
741 $x = $xoffset;
742 }
743 $y = $yoffset + $n * $textheight;
744
745 // This is so that the shadow doesn't go outside of the box.
746 if ($profile->drop_shadow) {
747 if ($shadow_xoffset < 0 && $shadow_yoffset < 0) {
748 imagettftext($im, $size, $angle, $x + abs($box[0]), $y + abs($box[5]), $shadow_color, $fontfile, $line);
749 imagettftext($im, $size, $angle, $x + abs($box[0]) - $shadow_xoffset, $y + abs($box[5]) - $shadow_yoffset, $foreground, $fontfile, $line);
750 }
751 else if($shadow_xoffset > 0 && $shadow_yoffset > 0) {
752 imagettftext($im, $size, $angle, $x + abs($box[0]) + $shadow_xoffset, $y + abs($box[5]) + $shadow_yoffset, $shadow_color, $fontfile, $line);
753 imagettftext($im, $size, $angle, $x + abs($box[0]), $y + abs($box[5]), $foreground, $fontfile, $line);
754 }
755 else if ($shadow_xoffset < 0 && $shadow_yoffset > 0) {
756 imagettftext($im, $size, $angle, $x + abs($box[0]), $y + abs($box[5]) + $shadow_yoffset, $shadow_color, $fontfile, $line);
757 imagettftext($im, $size, $angle, $x + abs($box[0]) - $shadow_xoffset, $y + abs($box[5]), $foreground, $fontfile, $line);
758 }
759 else if ($shadow_xoffset > 0 && $shadow_yoffset < 0) {
760 imagettftext($im, $size, $angle, $x + abs($box[0]) + $shadow_xoffset, $y + abs($box[5]), $shadow_color, $fontfile, $line);
761 imagettftext($im, $size, $angle, $x + abs($box[0]), $y + abs($box[5]) - $shadow_yoffset, $foreground, $fontfile, $line);
762 }
763 }
764 else {
765 imagettftext($im, $size, $angle, $x + abs($box[0]), $y + abs($box[5]), $foreground, $fontfile, $text);
766 }
767 }
768
769 $imagefunction = "image$imagetype";
770 $imagefunction($im, $file);
771 imagedestroy($im);
772 }
773 return base_path() . $cachedir . '/' . $urlfilename;
774 }
775
776 /**
777 * Turn a title into a signwriter image.
778 *
779 * @param $title
780 * The title text. Can be wrapped in html tags.
781 * @param $signwriter
782 * The signwriter profile to use. Can be one of:
783 * - a profile id
784 * - a profile name
785 * - a profile object with at least the name or id set
786 *
787 * @return
788 * HTML text to replace the title.
789 */
790 function signwriter_title_convert($title, $signwriter) {
791 $title = _signwriter_strip_tags($title);
792 preg_match('/(<.*?>)*([^<]*)(<.*?>)*/s', $title, $matches);
793 $titletext = $matches[2];
794 $openingtags = $matches[1];
795 $closingtags = $matches[3];
796 return $openingtags . theme('signwriter_text_convert', $titletext, $signwriter) . $closingtags;
797 }
798
799 /**
800 * Turn text into a signwriter image.
801 *
802 * @param $text
803 * The text to display
804 * @param $signwriter
805 * The signwriter profile to use. Can be one of:
806 * - a profile id
807 * - a profile name
808 * - a profile object with at least the name or id set
809 *
810 * @return
811 * HTML text to replace the input text.
812 */
813 function theme_signwriter_text_convert($text, $signwriter) {
814 $text = _signwriter_strip_tags($text);
815 $signwriter->text = $text;
816 $imgsrc = signwriter_url($signwriter);
817 return "<span style='display: none'>$text</span><img src='$imgsrc' alt='" . htmlspecialchars($text, ENT_QUOTES) . "' />";
818 }
819
820 function _signwriter_strip_tags($text) {
821 return preg_replace('/<\/?(i|b|em)>/i', '', $text);
822 }
823
824 function signwriter_get_fontpath() {
825 // search drupal's base dir, files dir, and current theme dir, as well as user-supplied dirs for a font
826 $fontpath = array('.', file_directory_path(), path_to_theme());
827 $userfontpath = variable_get('signwriter_fontpath', '');
828 if ($userfontpath != '') {
829 array_push($fontpath, $userfontpath);
830 }
831 return $fontpath;
832 }
833
834 /**
835 * Return a list of all available fonts in the system, searching the font path and other likely spots
836 */
837 function signwriter_available_fonts() {
838 $fontpath = signwriter_get_fontpath();
839 $fonts = array();
840 foreach ($fontpath as $dir) {
841 $ttfs = glob($dir . '/*.ttf');
842 if (!empty($ttfs)) {
843 foreach ($ttfs as $font) {
844 $fonts[$font] = $font;
845 }
846 }
847 }
848 return $fonts;
849 }
850
851 /**
852 * Return a list of the image types available in this php install
853 *
854 * @return
855 * An array in the form 'type' => 'type' (for convenient use in form select)
856 */
857 function signwriter_available_image_types() {
858 $types = imagetypes();
859 $return = array();
860 if ($types & IMG_GIF) $return['gif'] = 'gif';
861 if ($types & IMG_PNG) $return['png'] = 'png';
862 if ($types & IMG_JPG) $return['jpeg'] = 'jpeg';
863 if ($types & IMG_WBMP) $return['bmp'] = 'bmp';
864 if ($types & IMG_XPM) $return['xpm'] = 'xpm';
865 return $return;
866 }
867
868 /**
869 * Determine the type of image from a filename extension.
870 *
871 * @param $imagename
872 * The image filename.
873 *
874 * @return
875 * A string representing the image type (gif, png, jpeg, bmp, or xpm), or
876 * false if the image type is not recognised.
877 */
878 function signwriter_get_image_type($imagename) {
879 $types = signwriter_available_image_types();
880 $types['jpg'] = 'jpeg'; // synonym for jpeg
881 foreach ($types as $extension => $imagetype) {
882 if (preg_match("/.*$extension$/", $imagename)) return $imagetype;
883 }
884 return false;
885 }
886
887 /**
888 * Parse a six letter colour code into a colour array.
889 *
890 * @param $str
891 * A string of six hexadecimal digits.
892 *
893 * @return
894 * An array in the form (red, green, blue), or false.
895 */
896 function _signwriter_parse_colour($str) {
897 if (strlen($str) == 6) {
898 $red = intval(substr($str, 0, 2), 16);
899 $green = intval(substr($str, 2, 2), 16);
900 $blue = intval(substr($str, 4, 2), 16);
901 return array($red, $green, $blue);
902 }
903 return false;
904 }
905
906 function signwriter_profile_load($pid)
907 {
908 if (!is_numeric($pid)) {
909 return FALSE;
910 }
911 $profile = signwriter_load_profile($pid);
912 if (!isset($profile->id)) {
913 return FALSE;
914 }
915 return $profile;
916 }
917
918 function signwriter_theme() {
919 return array(
920 'signwriter_text_convert' => array(
921 'arguments' => array('text','signwriter'),
922 ),
923 );
924 }

  ViewVC Help
Powered by ViewVC 1.1.2