MyAnimeList Dynamic Signature
Added by astuffedtiger on May 16, 2017 19:44
*  saka's minimal signature script - v1.21 (anime/manga merged)
*  This code may be reused under the terms of the Creative Commons License (Attribution+NonCommercial+ShareAlike)
// specify the rss feed you want to use (see the bottom left of your MAL profile page)
$url_anime = "".$user;
$url_manga = "".$user;
// let's configure the cache
$cachedpath = "cache/sig.png"; // a temporary copy of your signature will be stored here at the end
check_cache(10); // time to cache signature in minutes, comment out this line for testing but put it back afterward!
// IMPORTANT: YOUR 'cache' DIRECTORY MUST EXIST AND BE WRITABLE ON THE SERVER!!! (chmod 777 or change file properties using FTP)
//     * By default, the image only updates at most every ten minutes, but for testing/building your sig you may want to disable the cache by
//       commenting out the check_cache(...) line above (just add two slashes at the beginning of the line). Once your sig is working how
//       you'd like, put the cache check back in. I do not recommend leaving the cache disabled or setting it for less than 5 minutes, since
//       too low a setting can bombard MAL with lots of RSS requests and may also piss off your host.
//     * You can set your forum signature to use [img]http://yourdomain/signature/sig.php[/img], and this file will output your signature image, or
//       you can alternatively link to [img]http://yourdomain/signature/cache/sig.png[/img] and have a cronjob visit the php url every few minutes
// try to correct environment path
$anibuffer = file_get_contents($url_anime);
$mangabuffer = file_get_contents($url_manga);
if ( !($anibuffer) or !($mangabuffer) ) die("Could not download RSS feed");
// lets fix the status in the manga feeds so they make sense, and so we can differentiate from anime (silly Xinil)
$mangabuffer = strtr($mangabuffer, array('>Plan to Watch'=>'>Plan to Read','>Watching'=>'>Reading','>Rewatching'=>'>Re-Reading','>On-Hold'=>'>Reading On Hold','>Completed'=>'>Finished Reading') );
// easiest to parse if we just merge the feeds together and parse all at once
$buffer = $anibuffer . $mangabuffer;
// now we have to sanitize the information we saved to the buffer (no newlines/tabs and replace xml entities)
$buffer = strtr($buffer, array("\n" => '', "\r" => '', "\t" => '', '&lt;' => '<', '&gt;'=>'>', '&amp;' => '&', '&quot;' => '"', '&apos;'=>"'") );
// these lines just extract the anime title and status information into $titles[] and $status[] arrays, plus other info
preg_match_all("/<item><title>([^<]*)<\/title>/i", $buffer, $titlematches);
preg_match_all("/<description>([^<]*) - ([\d?]+) of ([\d?]+) (episodes?|chapters?)<\/description>/i", $buffer, $statusmatches);
preg_match_all("/<pubDate>([^<]*)<\/pubDate>/i", $buffer, $timematches);
$titles = $titlematches[1]; // $anititles is now an array of titles
$status = $statusmatches[1]; // $anistatus is now an array of statuses
$current = $statusmatches[2]; // $current is now an array of all the current episodes/chapters
$totals = $statusmatches[3]; // $totals is now an array of all the episode/chapter totals
$units = $statusmatches[4]; // $units is now an array of 'episode(s)' or 'chapter(s)'
$timestamps = $timematches[1]; // $timestamps is now an array of dates watched/read
// let's  go through the whole list and format it how we want
for($i = 0; $i < count($titles); $i++) {
	// convert timestamps to unix timestamps (numbers) so we can sort them properly later (don't change this line)
	$timestamps[$i] = strtotime($timestamps[$i]);
	// just remove all that junk at the end
	$titles[$i] = preg_replace('/ - (TV|Movie|ONA|OVA|OAD|Special|Manga|Manhwa|Manhua|Novel|One Shot|Doujin|OEL)$/', '', $titles[$i]);
	// limit the titles to 21 characters; adjust this to your needs
	$titles[$i] = textlimit($titles[$i],32);
	// FORMAT THE STATUS VALUES - you can change the format as you like
	if ($status[$i] == "Watching" or $status[$i] == "Rewatching" or $status[$i] == "Reading" or $status[$i] == "Re-Reading") {
		$status[$i] = "$status[$i] at $current[$i] of $totals[$i] $units[$i]";
	} else { // "Completed, "Plan to Watch", "Dropped", or "On Hold"
		$status[$i] = "$status[$i]"; // doesn't really do anything, but feel free to modify
	$status[$i] = strtolower($status[$i]); // make all the statuses lowercase
// sort all of the arrays by the timestamps, so that the most recent entries are first
$sigimage = open_image("jimmy-sig-2011.png"); // load your background image
$font = 'completeinhim.ttf'; // if you use another font, make sure you copy the *.ttf file into the same directory
// let's define a font color - the last three arguments are the red, green, and blue values (so 0,0,0 = black and 255,255,255 = white)
$colour = imagecolorallocate($sigimage,0,0,0);
//$colour2 = imagecolorallocate($sigimage,0,0,0);
// draw the text - the template is imagettftext(image, font size, angle, x-pos, y-pos, font color, fontfile, text output, 'c' or 'l' or 'r')
// OVERLAY ANOTHER IMAGE over the font and background (optional)
// finally, let's output our pretty signature image to the browser
header("Content-type: image/png");
@imagepng($sigimage, $cachedpath); // try to save a copy of our signature to the cache location we set earlier
// Don't modify below here... just a few helping functions that can be called in the above code
// textlimit($string, $length) takes any $string you pass it and resturns it shortened to $length characters (use it to limit title length)
function textlimit($string, $length=25) {
	return (strlen(trim($string))>$length ? trim( substr(trim($string),0,$length-3) )."..." : $string);
// textlimitpx($string, $pixels, $font) returns the shortened $string that fits within exactly $pixels width horizontally when using $font and $size
function textlimitpx($string, $pixels, $font, $size) {
	for($k = strlen(trim($string)); $k > 0; $k--) {
		$box = imagettfbbox($size,0,$font,textlimit($string,$k));
		$w = $box[2] - $box[0];
		if ($w <= $pixels) break;
	return textlimit($string,$k);
// overlay_image($baseimage,$overlaypath,$x,$y) opens the image at $imagepath and overlays it onto $sigimage at position ($x, $y)
// most image types should work, but 24-bit/true color PNG is recommended if you need transparency
function overlay_image($overlaypath,$x=0,$y=0) {
	global $sigimage;
	$overlay = open_image($overlaypath); // open any image
	imagecopy($sigimage, $overlay, $x, $y, 0, 0, imagesx($overlay), imagesy($overlay)); // overlay onto our base image
	@imagedestroy($overlay); // clean up memory, since we don't need the overlay image anymore
// open_image($path) will load an image into memory so we can work with it, and return an error if we fail
function open_image($path) {
	$image = @imagecreatefromstring(file_get_contents($path));
	if (!$image) die("could not open image ($path) make sure it exists");
	imagealphablending($image,true); imagesavealpha($image,true); // preserve transparency
	return $image;
// check_cache($minutes) returns a cached image and stops execution if $minutes has not passed since the last update
function check_cache($minutes) {
	global $cachedpath;
	if ( !( is_writable($cachedpath) or is_writable(dirname($cachedpath)) and !file_exists($cachedpath) ) )
		die("The cache is not writable; please change it to 777 permissions using FTP.\n\$cachedpath = {$cachedpath}");
	if ( time() - @filemtime($cachedpath) < 60*$minutes ) {
		header("Content-type: image/png");
		echo file_get_contents($cachedpath);
// imagettftextalign() is basically a wrapper for imagettftext() to add the ability to center/right-align text to a point
// the $align argument can be 'c' for center, 'r' for right align, or 'l' for left align (default)
function imagettftextalign(&$img,$size,$angle,$x,$y,&$c,$font,$string,$align='l') {
	$box = imagettfbbox($size,$angle,$font,$string);
	$w = $box[2] - $box[0];
	$h = $box[3] - $box[1];
	switch (strtolower($align)) {
		case 'r': $x -= $w; $y -= $h; break;
		case 'c': $x -= $w/2; $y -= $h/2; break;