I am trying to create textimages in a particular folders with particular names, so that I can programmatically chose my preferred textimage for my banner in particular situations. However, I have two problems, the 'files/textimage' folder continues to be created as well as my folder and I don't know why. Also I am using rules to unlink the textimage when the text changes so that the image is recreated.

Here is the code I am using...

$display = theme('textimage_image', array(
'preset' => 'bannertitle',
'text' => 'my text',
'additional_text' => NULL,
'format' => 'png',
'file_path' => 'public://users/myfolder/bannertitle'
));
echo $display;

Should this function create the textimage if it doesn't exist in 'myfolder'?
Also if I delete the image will this function recreate the textimage?

Comments

webservant316’s picture

Just how does textimage work? Is the file_path expected to be unique for each distinct text string? Or if I revise the text for a particular filepath will it update the image file? It does not appear to. So then how can I programmatically update the image file?

$display = theme('textimage_image', array(
'preset' => 'bannertitle',
'text' => 'my text - revised text',
'additional_text' => NULL,
'format' => 'png',
'file_path' => 'public://users/myfolder/bannertitle'
));
echo $display;

mondrake’s picture

Hi @webservant316

the logic for path management and theme calls in 7.x-2.x is indeed pretty clumsy :(

If I understand correctly what you are trying to do, I'd suggest to checkout 7.x-3.0-beta1 instead.

Firstly, you'll have to create an Image Style named 'bannertitle', including the textimage effects you need. Have a look at the README.txt file.

Then you'd have two options (just example code on top of my mind, not tested):

Option 1 - use theme calls

Just call the theme to render the output with your text. You do not need to care about the path where the image is stored.

    echo theme(
      'textimage_style_image',
      array(
        'style_name' => 'bannertitle',
        'text'   => array('my text'),
      ),
    ));

    echo theme(
      'textimage_style_image',
      array(
        'style_name' => 'bannertitle',
        'text'   => array('my text - revised text'),
      ),
    ));

In fact, the images will be created as 'public://textimage/bannertitle/my text.png' and 'public://textimage/bannertitle/my text - revised text.png' . Or as 'private://...' if you choose to store the images in the private file system (it's an option on the Image Style edit form). The only exception is if your filename cannot be created 'cleanly' (e.g. if your text array contains very long text or special characters); in this case the file will be stored in a special directory and the filename consist of a md5 hash; but you don't have to care - Textimage manages that for you under the hood.

Option 2 - use API calls

You want to create textimages programmatically. There's a specific API for that: you create the textimages, get the URI where they reside, and you can render them with a call to theme('image', ...). The location of the image file follows the same logic as in previous option.


    $my_text_uri = TextimageImager::getImageUri(
      'bannertitle',
      NULL,
      array('my text')
    );

    $my_revised_text_uri = TextimageImager::getImageUri(
      'bannertitle',
      NULL,
      array('my text - revised text')
    );

    echo theme(
      'image',
      array(
        'path' => $my_text_uri,
      )
    );

    echo theme(
      'image',
      array(
        'path' => $my_revised_text_uri,
      )
    );

Hope it helps... feedback appreciated.

webservant316’s picture

Seems like people will either want textimages stored per unique text OR per unique file. If you store per unique text perhaps there is not a concern about text expressions that are no longer needed.

In my case I want to allow a unique textimage for each registered user, to be used as their banner. I could use option 1 above, but if they change their banner text I will have a lot of abandoned textimages laying around. Ideally I want to store textimages per unique path/filename. Also I want to specify the path/filename because I am trying to keep each user's files in one folder. Even more ideally I would like to overwrite the path/filename if the text changes. That way I conserve file resources because each user only ever has one textimage for their banner. How can I overwrite the image only if the text changes?

Does TextimageImager::getImageUri both create the image and return the uri? Seems like you still choose the location for the image. I am trying to build a system that is prepared for thousands of textimages so I am not sure that I want them all in one folder for performance concerns.

Also your option 2 example above seems to demonstrate the revision of a text image named 'bannertitle'. However, is the image overwritten or a new image created? Your example seems to indicate that both images are still available at $my_text_uri and $my_revised_text_uri.

webservant316’s picture

7.x-3.0-beta1 = WSOD

webservant316’s picture

Hoping to help you with the feedback you need to get this working. This module has become essential to me because of wanting to allow the user to customize their own banner. I could use text itself as the banner, however, textimages have the cool feature of resizing properly with my responsive theme.

Let me know how I can help with feedback. As I look at your module is seems to be designed with the thought that you do the file management for unique text strings. Perhaps the design could be extended with the thought that the user could specify unique textimage folder/filenames to contain any string.

webservant316’s picture

7.x-2.0 worked great for me. Except this function doesn't return a new image if the text changes...

theme('textimage_image', array(
'preset' => 'Preset',
'text' => 'Text',
'additional_text' => array('Additional', 'Text'),
'format' => 'png',
// Don't include the file extension!
'file_path' => 'public://myimages/sub_folder/image-filename'
));

mondrake’s picture

7.x-3.0-beta1 = WSOD

Strange. True this version is pretty new, but this is the first report of WSOD, while some issues already are about using the module, so someone has got it installed and working. Have you tried a clean install?

Does TextimageImager::getImageUri both create the image and return the uri?

Yes - it creates the image, and returns the URI to the image created.

However, is the image overwritten or a new image created?

A new image file is created, with the new text inside the new image. So you have two files, one with text A and one with text B, under the same 'public://textimage/bannertitle' directory. This helps with caching because if you need again an image with text A, it will pick the existing one instead of re-creating it.

If you do not want to cache the textimages, and accept that they get created on the fly, you can disable caching. There's a variable for that in the theme call, and an argument in getImageUri.

    $my_text_uri = TextimageImager::getImageUri(
      'bannertitle',
      NULL,
      array('my text'),
      $extension = 'png',
      $caching = FALSE
    );

non cached images are be stored in a different directory (private://textimage_store/uncached/{hash}.{extension}), and get removed upon each cron run. getImageUri will return the URI to the uncached image. From there, you can either display the image to the user as such, or programmatically copy that file to whatever directory structure/filename you need.

module is seems to be designed with the thought that you do the file management for unique text strings.

Yes that is correct: input is a) an image style and b) some variable text. Textimage makes sure that given (a) and (b), an image file with text in (b) is generated and cached for further access.

7.x-2.0 worked great for me. Except this function doesn't return a new image if the text changes...

Can't remember how 2.0 was exactly doing this, but definitely it had problems - see e.g. #1969774: when chaining presets, cached image considers only last preset value and #1936172: Fatal error: Call to a member function realpath() on a non-object.... I suppose that's because if you specify a filename for text A and then the same filename for text B, 2.0 will anyway see that a filename exists already and return the image for text A.

webservant316’s picture

The 7.x-3.0-beta1 = WSOD was a clean install. Here is my PHP error log.
===
PHP Fatal error: Call to undefined function array_replace_recursive() in ../sites/all/modules/textimage/textimage.module on line 840
===

At this point I plan to use Option 1 explained in #2 above.

Though, I am concerned about the performance of having thousands of textimages under your file management scheme. My preference would be a simple API that specifies the text, preset, and exact location/filename of the image. The return value would be the HTML markup or fail. If the image file doesn't exist it would be created. If the text changes the image would be recreated, same filename. That would be most excellent for me.

Of course before I can use 7.x-3.0-beta1 I need a fix for the error message above.

mondrake’s picture

Thanks

array_replace_recursive() is a PHP function, available for PHP >= 5.3.0. Which version of PHP are you on? If below 5.3.0, I found an alternate implementation in stackoverflow, maybe we can include that as a patch in the module. It would be great if you could test and let me know.

My preference would be a simple API [...]

Hm. This would have serious implications on the caching mechanism, as we should store away the text currently represented in a image file - how would textimage know that the text has to be changed otherwise?
If your concern is about having 'loose' textimages on the file system, you can always put a call image_style_flush('bannertitle'); in a periodic job (i.e. cron). That would remove all the image files under public://textimage/bannertitle, and next time getImageUri is called a new image will be recreated.

webservant316’s picture

The requirement for Drupal 7 is PHP 5.2.5. You probably need to downgrade the use of that function for a Drupal 7 module. I am using PHP 5.2.17 with no plans to upgrade.

As for my simple API, that is a good point about how to figure out if the text changed. All the options that come to my mind include: 1) Database table linking the filename with the current text string, 2) EXIF data in the image itself? 3) a second file, '.banner.txt' next to the image file containing the current text (though this would require a file read each time) and 4) prepare your code to allow me to delete the file myself after I change the text and then next time the API is called since the file is no longer there is gets recreated.

Option 4 seems the easiest for you and also for me since I will know exactly were each banner image is located. It is also offers the optimal performance. Everytime a user updates their profile I will just delete their banner image. I tried to do that with 7.x-2.0 but it didn't work for some reason. I think the file storage mechanism is messed up when trying to specify your own location for the image.

mondrake’s picture

1. I'm on 5.4, no access to 5.2. Can you just test, placing the code snippet indicated in #9 from stackoverflow into textimage.module? If that solves the issue I will include it in the main repo.

2. Option 4 - use the solution in #7, using getImageUri and disabling caching. Get the URI of the textimage, copy it to whatever path/filename, delete the file from where you copied.

mondrake’s picture

Created spinoff #2134439: Installing on PHP < 5.3.0 results in WSOD errors for the WSOD issue related to usage of array_replace_recursive() .

webservant316’s picture

#11 - 1) hope to do that tommorrow.

#11 - 2) I think I understand, but my code wrapper around yours it going to look pretty clunky. If possible please consider my simple API as a feature request. It would be a simple extension to allow the caller to choose the filename and location.

mondrake’s picture

Forget about #11.1, sorry.

It looks like I should have been using drupal_array_merge_deep() instead of array_replace_recursive(). Please watch out for #2134439: Installing on PHP < 5.3.0 results in WSOD errors, I'll post a patch there tomorrow.

mondrake’s picture

Title: Folder location of textimage » Introduce a method to specify a target location for a textimage file
Version: 7.x-2.0 » 7.x-3.x-dev
Component: Miscellaneous » Code
Category: Support request » Feature request

Changed to feature request based on comment in #13.2

mondrake’s picture

Title: Introduce a method to specify a target location for a textimage file » Allow to specify a target URI for a textimage file in the API
Status: Active » Needs review

Here's a patch. It extends the getImageUri method with an additional argument, $target_uri, where you can specify the full URI where the image should be stored. The file just gets generated at the URI specified (note the URI shall be inclusive of the file extension, differently than in 2.0). If there is a file at $target_uri, it will be deleted and recreated with the specified $text.

Use like

   $my_textimage_uri = TextimageImager::getImageUri(
      'bannertitle',
      NULL,
      $text = array('my text'),
      $extension = 'png',
      $caching = FALSE,
      NULL,
      NULL,
      $target_uri = 'public://my_path/my_filename.png'
    );

$my_textimage_uri will be set to 'public://my_path/my_filename.png' upon return.

NOTE: there will be no caching of an image if you specify $target_uri. That's because, given a URI, we could overwrite that file with whatever $text.

mondrake’s picture

StatusFileSize
new6.45 KB

... and the patch ...

webservant316’s picture

Thanks.

I had originally thought the API should only create the textimage if doesn't exist and simply return the uri if it already exists. However, your solution to always delete if exists and create is better. In my use case this will save unneeded calls to your function. When my text is created or changed I will simply call your API. However, when displaying the image, if the image file already exists I just display the image. If it doesn't exist I call your function.

I will test soon.

mondrake’s picture

StatusFileSize
new1.34 KB
new6.79 KB

Reviewing, most probably we do not need to explicitly delete the file, as the lower level imagexxx() functions called by the toolkit via image_toolkit_invoke('save', ...) will anyway replace a file if already existing. Shorter patch then...

webservant316’s picture

provided that the imagexxx() functions delete and create a file by the same name.

mondrake’s picture

Of course, see the tests, I have one that generates a file for an URI, and then another one with different text for the same URI - they are passing i.e. only one file is counted in the destination directory.

webservant316’s picture

should I test with 7.x-3.0-beta1 or 7.x-3.x-dev?

mondrake’s picture

Patches are always against dev, but in this case beta1 is currently equal to dev. To cut short - my recommendation would be to test with dev.

Could you also provide feeedback to #2134439: Installing on PHP < 5.3.0 results in WSOD errors?

I'd like to go for a beta2 when these patches are confirmed - we shouldn't live with a release that ends in a WSOD or a fatal (see #2106417: Fatal error: Call to undefined function _color_pack()).

Thank you for your help here.

webservant316’s picture

Module installed properly. However, when I went to create a preset I got a
WSOD here PHP Parse error: syntax error, unexpected T_STATIC in ../textimage/classes/TextimageStyles.inc on line 34.

mondrake’s picture

Sorry for that, looks like a PHP 5.2 issue again. Will post a new patch under #2134439: Installing on PHP < 5.3.0 results in WSOD errors soon.

mondrake’s picture

Status: Needs review » Needs work

OK, the patch in #2134439: Installing on PHP < 5.3.0 results in WSOD errors passes. Unfortunately, the patch in this issue is colliding with that, so it would need to be re-rolled. To do that, I need to commit #2134439: Installing on PHP < 5.3.0 results in WSOD errors first. Can you check if you can install and run the module with the patch there?

webservant316’s picture

PHP version issues seem to be solved. however the api call as directed in #16 does not work correctly. The image was created, but was not located where I directed. Instead it was located at private://textimage_store/uncached/hashfilename.png.

Getting closer.

webservant316’s picture

Also in my code I am assuming that if empty(TextimageImager::getImageUri()) = TRUE then the image creation mission failed. Or do you have another recommendation?

webservant316’s picture

The repairs for the php 5.2 compatibility seem to have fixed #27. So I think everything is ok with your module. I still have to figure out how to use rules to grab updated text from a profile field edit after the submit, but before the change is available in load_user(). However, this new API seems to be functioning. Thanks.

I will report back one more time with final confirmation.

webservant316’s picture

the function call in #16 is creating the file in the proper location, but if the file exists the function does not appear to delete and recreate the image. your suggestion that the function always create and/or delete and create seemed the best, but the delete and recreate may not be working. I'll test further on Monday.

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new6.77 KB

#29 - I was not expecting this to be functioning yet. Can you please test with the patch here, and the new dev that will be built after the commits today.

#28 - getImageUri() returns the URI where the image is saved, or NULL if it fails. So you can simply have

  if(!$returned_uri = TextimageImager::getImageUri(...)) {
    ... [actions on failure] ...
  }

Patch here is a reroll of #19 after #2134439: Installing on PHP < 5.3.0 results in WSOD errors was committed.

Cheers

webservant316’s picture

Hope to get to this Monday.

webservant316’s picture

7.x-3.x-dev installs, configures, and operates without PHP or Drupal error.
The textimage is created in the right location with the right name. However, subsequent calls to the API will not overwrite an existing file.

webservant316’s picture

So I am just deleting the text image before I want to update and so I have the function I need. Post back if you get this figured out.

mondrake’s picture

Great! Glad it's working. So this means I have to reintroduce the explicit file deletion I removed in #19. I would like to understand why the file does not get overwritten, though. I will look into it tomorrow.

mondrake’s picture

StatusFileSize
new1.95 KB
new8.56 KB

I investigated a bit, and modified the logic of buildImage(). We may have the case that multiple requests require to access the image file at URI at the same time, and if we delete the image before we start the generation process, some of them will fail and render no image. I know it's not your use case, but still we need to look for this. So, instead of explicitly deleting the file, we let it be overwritten by the image generation process when the image is ready. So we shouldn't have the temp blackout in this case.

NOTE - while testing, I noticed that the browser may be caching the image at URI, and use a local copy even if we are rebuilding the source. This patch does nothing to prevent that, just the server side of overwriting the image file.

webservant316’s picture

Yes - I was wondering about the temp blackout case as well. Overwriting is definitely the way to go. And yes, may not be much to do about the browser cache since the image isn't changing names. Some browsers are better (FF) and some worse (IE) at refreshing their cache when needed. I just need to warn my user with a note that their change may not be evident until their browser cache is refreshed.

I will test you latest patch and report back.

mondrake’s picture

Have a look at this, just in case. It's something to be probably done at the theme('image',...) level.

webservant316’s picture

no php errors or critical Drupal errors after
1. fresh install of patched module
2. configuration of textimage
3. clean up textimage function
4. creation of configuration of image style
5. custom calls to TextimageImager::getImageUri as explained in #16

Calls to TextimageImager::getImageUri will create or recreate the image filename and location as directed. So I do not need to unlink the textimage file myself.

So it would see all is well. Thanks!

I did get these errors in my Drupal logs....
10 occurances of this specifying both public:// and private:// in alternating entries.

The file private://styles/bannertitle was not deleted, because it does not exist.

and 2 of this specifying public:// and private:// in alternating entries.

The file private://textimage was not deleted, because it does not exist.

I didn't manually delete any files before I uninstalled and reinstalled your module, but only used the interface. Though, everytime after I get things set up to my taste there has been an 'uncached' folder in the private folder that seemed unneeded so I have deleted that manually.

Anyway maybe those Drupal errors are not significant, but it seems like some code somewhere is trying to do unneeded work.

mondrake’s picture

Great! Thanks, I will commit this then.

The file private://styles/bannertitle was not deleted, because it does not exist.

... it's a Drupal core issue, see #2079315: Image style editing leads to redundant watchdog entries.

everytime after I get things set up to my taste there has been an 'uncached' folder in the private folder

Yes. That folder is (also) storing the previews of the image shown in the Textimage text effect admin UI. It's an image itself, and has to go somewhere... :) All the files in that directory get removed upon each cron run, so you shoudn't worry about that.

The file private://textimage was not deleted, because it does not exist.

Strange. If you understand more when this happens, please open a new issue, let's close this one now.

webservant316’s picture

Super. I look forward to installed the module with the patch installed. For any following this post this addition is awesome for me and allows me to use textimage banners per user. Images are ideal for this application because they can be styled to resize with responsive themes.

Thanks again @mondrake.

mondrake’s picture

Status: Needs review » Fixed

Committed and pushed to dev.

mondrake’s picture

Released 7.x-3.0-beta2

webservant316’s picture

7.x-3.0-beta2 fresh install, configured, operating. Thanks again.

sigalasgeo’s picture

Is there any way to use tokens in order to retrieve the hashed target uri? The idea is to use the path of the image in OG meta tags.
I am using text image to create image quotes but i cannot figure any way in order to retrieve the actual path of the image via tokens. you may see the site (www.posts.gr).

Thanks in advance and thanks for the great module.

mondrake’s picture

@sigalasgeo re. #45

I opened a new issue #2146495: Implement Textimage tokens to retrieve images' URI/URL. Let's continue there and leave this issue rest in peace :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

webservant316’s picture

The API doesn't work on 7.x-3.0-rc1. The text image made is always the preset default.

webservant316’s picture

sorry. my fault. it does work.