I've had a need to draw underlines in text created with the GD image
functions, specifically ImagePSText(). As far as I could tell there's no
way to do this (apart from using ImageLine(), which would be difficult to
really get right). [I'm happy to hear suggestions if I've missed the
obvious :-)]

An added complication of what I wanted to achieve was to underline only part
of the string being drawn, while maintaining kerning between characters.

The system I've come up with is to embed "control sequences" into the string
that is passed to ImagePSText(). A control sequence contains commands which
change various characteristics of the output, for instance, toggle
underlining. I'm currently using the escape character (chr(27)) to indicate
the start of a control sequence, and a semi-colon to end the sequence.

For example:
$str="Some text with an " . chr(27) . "u;underlined" . chr(27) . "u; word";

I've attached a patch for gd.c which does what I'm talking about.

I wondering whether people think this is a good way to go, and if so if I
should commit this code? If it's not a good idea, any suggestions for
drawing underlines?

Thanks,
Tim
Index: gd.c
===================================================================
RCS file: /repository/php-src/ext/gd/gd.c,v
retrieving revision 1.221.2.46
diff -u -r1.221.2.46 gd.c
--- gd.c	2 Nov 2004 16:42:13 -0000	1.221.2.46
+++ gd.c	6 Nov 2004 05:47:45 -0000
@@ -3322,6 +3322,173 @@
 }
 /* }}} */
 
+T1_OUTLINE* php_T1_GetLineOutline( T1_OUTLINE *str_path, int length, float size, float position, float thickness )
+{
+  T1_PATHSEGMENT *head;
+  T1_PATHSEGMENT *line_path;
+  T1_PATHSEGMENT *path;
+  int starty=0;
+  int endy=0;
+
+  /* I'm not too sure about these calculations.
+     Why 72/2?
+       72 because t1libs DEFAULT_RES is 72.
+	   Divide by 2 because it worked better for me
+
+	 */
+  starty = (-1) * (int) floor( (position-0.5*thickness) * size + 0.5 ) * (72.0*0.5);
+  endy   = starty - (int) floor(thickness*size+0.5) * (72.0*0.5);
+
+  head = line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->type = T1_PATHTYPE_MOVE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 0;
+  line_path->dest.x = -length;
+  line_path->dest.y = starty;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  path = line_path;
+  line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  path->link = line_path;
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->type = T1_PATHTYPE_LINE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 0;
+  line_path->dest.x = length;
+  line_path->dest.y = 0;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  path = line_path;
+  line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  path->link = line_path;
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->type = T1_PATHTYPE_LINE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 1;
+  line_path->dest.x = 0;
+  line_path->dest.y = endy - starty;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  path = line_path;
+  line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  path->link = line_path;
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->type = T1_PATHTYPE_LINE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 1;
+  line_path->dest.x = -length;
+  line_path->dest.y = 0;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  path = line_path;
+  line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  path->link = line_path;
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->flag = 64;
+  line_path->type = T1_PATHTYPE_LINE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->last = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 1;
+  line_path->dest.x = 0;
+  line_path->dest.y = starty-endy;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  path = line_path;
+  line_path = (T1_PATHSEGMENT *) malloc(sizeof(T1_PATHSEGMENT));
+  path->link = line_path;
+  memset(line_path, 0, sizeof(T1_PATHSEGMENT));
+  line_path->type = T1_PATHTYPE_MOVE;
+  line_path->link = (T1_PATHSEGMENT *)NULL;
+  line_path->last = (T1_PATHSEGMENT *)NULL;
+  line_path->references = 1;
+  line_path->dest.x = length;
+  line_path->dest.y = -starty;
+  line_path->size = sizeof(T1_PATHSEGMENT);
+
+  head->last = line_path;
+  head->flag = str_path->flag; /* hack to "close" the path */
+
+  str_path = T1_ConcatOutlines(str_path, (T1_OUTLINE *)head);
+
+  return str_path;
+}
+
+int php_gdgetcontrolsequence( char *str, int len, float *var )
+{
+	int ret;
+	char *ptr, *ptr2;
+	float f=0.0;
+
+	if( *str != 27 )
+	  return -1;
+
+	switch( str[1] )
+	{
+	case 'u':
+		/* toggle underlining */
+		return 1;
+	case 'i':
+		/* change slope (italic) */
+		ret = 2;
+		break;
+	case 'w':
+		/* change weight (bold) (not implemented, hard) */
+		ret = 3;
+		break;
+	case 'c':
+		/* change color (not implemented, possible) */
+		ret = 4;
+		break;
+	case 'x':
+		/* move in X-direction (not implemented, should be easy) */
+		ret = 5;
+		break;
+	case 'y':
+		/* move in Y-direction (not implemented, should be easy) */
+		ret = 6;
+		break;
+	case 's':
+		/* change size */
+		ret = 7;
+		break;
+	case 'f':
+		/* font (not implemented, tricky) */
+		ret = 8;
+		break;
+	case 'a':
+		/* change angle */
+		ret = 9;
+		break;
+	case 't':
+		/* change spacing (tracking, tightness) */
+		ret = 10;
+		break;
+	case 'e':
+		/* set condense (extension) */
+		ret = 11;
+		break;
+	default:
+		return -1;
+	}
+
+	ptr = str + 2;
+	ptr2 = ptr;
+	while( *ptr!='\0' && *ptr!=';' && len>0 ) {
+		ptr++;
+		len--;
+	}
+	if( *ptr == ';' )
+	{
+		*ptr = '\0';
+		f = atof( ptr2 );
+		*var = f;
+		*ptr = ';';
+	}
+
+	return ret;
+}
+
 /* {{{ proto array imagepstext(int image, string text, int font, int size, int xcoord, int ycoord [, int space, int tightness, float angle, int antialias])
    Rasterize a string over an image */
 PHP_FUNCTION(imagepstext)
@@ -3346,6 +3513,10 @@
 	char *str;
 	int str_len;
 	int argc = ZEND_NUM_ARGS();
+	int underline_length, min_ux, max_ux;
+	float control_var;
+	float size;
+	int cmd;
 
 	if (argc != 8 && argc != 12) {
 		ZEND_WRONG_PARAM_COUNT();
@@ -3425,17 +3596,99 @@
 	}
 
 	if (width) {
-		extend = T1_GetExtend(*f_ind);
-		str_path = T1_GetCharOutline(*f_ind, str[0], size, transform);
-
+	  if( str_len==0 ) {
+	    str_path = T1_GetCharOutline(*f_ind, str[0], size, transform);
 		if (!str_path) {
 			if (T1_errno) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "libt1 returned error %d", T1_errno);
 			}
 			RETURN_FALSE;
 		}
+	  } else {
+		extend = T1_GetExtend(*f_ind);
+		str_path = NULL;
 
 		for (i = 1; i < str_len; i++) {
+			if( _str[i] == (char) 27 ) {
+			  cmd = php_gdgetcontrolsequence( _str + i, len-i-1, &control_var );
+			  if( cmd == -1 ) {
+			    continue;
+			  }
+			  /* skip i past the control sequence */
+			  while( _str[i] != ';' && _str[i] != '\0' ) {
+			    i++;
+			  }
+			  if( _str[i] == '\0' ) {
+			    continue;
+			  }
+			  switch( cmd ) {
+			  case 1: /* underlining */
+			    if( mod_flag & T1_UNDERLINE ) {
+				  mod_flag &= ~T1_UNDERLINE;
+				  underline_length = 0;
+				  while( underline_path != NULL && underline_path->type == T1_PATHTYPE_MOVE ) {
+					underline_path=underline_path->link;
+				  }
+				  min_ux = 0;
+				  max_ux = 0;
+				  while( underline_path != NULL ) {
+			        underline_length+=underline_path->dest.x;
+					min_ux = min_ux < underline_length ? min_ux : underline_length;
+					max_ux = max_ux > underline_length ? max_ux : underline_length;
+					underline_path=underline_path->link;
+				  }
+				  underline_length = max_ux - min_ux;
+				  if( str_path == (T1_OUTLINE *)NULL ) {
+				    continue;
+				  }
+                  str_path = php_T1_GetLineOutline(str_path, underline_length, size, T1_GetUnderlinePosition(*f_ind), T1_GetUnderlineThickness(*f_ind));
+
+				} else {
+			      mod_flag |= T1_UNDERLINE;
+				  if( str_path != (T1_OUTLINE *)NULL )
+				    underline_path = str_path->last;
+				}
+				break;
+			case 2: /* change slope (italic) */
+			    slant = control_var;
+				T1_DeleteAllSizes( *f_ind );
+				T1_SlantFont( *f_ind, slant );
+				break;
+			case 3: /* change weight (bold) */
+				break;
+			case 4: /* change color */
+				break;
+			case 5: /* move x */
+				break;
+			case 6: /* move y */
+				break;
+			case 7: /* set size */
+				size = control_var;
+				break;
+			case 8: /* set font */
+				break;
+			case 9: /* change angle */
+				angle = control_var;
+				transform = T1_RotateMatrix(NULL, angle);
+				break;
+			case 10: /* change spacing (tracking) */
+				width = control_var;
+				break;
+			case 11: /* set condense (extension) */
+				extend = control_var;
+				T1_DeleteAllSizes( *f_ind );
+				T1_ExtendFont( *f_ind, extend );
+				break;
+			  }
+			  continue;
+			}
+			if( str_path == NULL ) {
+		      str_path = T1_GetCharOutline(*f_ind, _str[i], size, transform);
+			  if( mod_flag & T1_UNDERLINE ) {
+			    underline_path = str_path;
+			  }
+			  continue;
+			}
 			amount_kern = (int) T1_GetKerning(*f_ind, str[i-1], str[i]);
 			amount_kern += str[i-1] == ' ' ? space : 0;
 			add_width = (int) (amount_kern+width)/extend;

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to