# GMLscripts.com

Discuss and collaborate on GML scripts

You are not logged in.

## #1 2013-05-20 08:29:13

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### draw_text_rtf

I started a suggestion on bugs.yoyogames.com http://bugs.yoyogames.com/view.php?id=11044, requesting a GML function that would allow a formatted string to be drawn to the screen.  This would be a more powerful version of the draw_text function, in that it would allow the user to include formatting syntax in the string argument, allowing for portions of the string to be made bold, italic, change color, or change font.

User Miah_84 created a GML script called draw_text_rtf, which provides much of this functionality.  I've modified the script slightly.

xot asked that a thread be started to discuss the script so that its development can be continued... currently a feature that I would like to see added to the function is to allow formatting syntax to enable the use of named variables for color, like so:

str = "[b]This[/b] [i]string[/i] has some [c_name=c_red]RED[/c_name] [b][i]text[/i][/b] in [c=0000FF]it[/c]."
draw_text_rtf(x, y, str, f_normal, f_bold, f_italic, f_bold_italic)

would result in a string being drawn to the screen, like so:

This string has some RED text in it.

The script, in its current state, is pasted below:

#define draw_text_rtf
/*
Original draw_text_rtf script by Miah_84.  Additions by csanyk.  This script may be used or modified freely, no attribution needed.

draw_text_rtf(x, y, string, regular_font, bold_font, italic_font, bold_italic_font)

Description: Draws a formatted string using BBCode-like syntax for internal formatting within the string.

Returns: void

Caution:  Only works for halign==left.  Not intended to draw centered or right-aligned text.

argument0 = the x position of the string
argument1 = the y position of the string
argument2 = the string.
argument3 = the regular font
argument4 = the bold font
argument5 = the italic font
argument6 = the bold italic font

The string may contain the following syntax:

[b]bold text[/b]
[i]italic text[/i]
[c=RRGGBB]colored text[/c]
TODO: [c_name=named_color]colored text using named color[/c_name] (not yet implemented)
*/

/*
local var definitions

next_x, next_y      : the x and y position of the next substring of the formatted RTF draw
rtf_x, rtf_y        : the x and y position of the rtf string
str                 : the raw rtf string, with syntax formatting awaiting parsing;
f_r, f_b, f_i, f_bi : the index of regular, bold, italic, and bold italic fonts
mode                : a control flag that sets the mode that the parser is currently operating in, eg regular, bold, italic, bold&italic
original_color      : temp storage of the original drawing color, so that the function can revert back to it when needed
i, j                : for loop iterator, re-used several times throughout the function
sc3                 : a length-3 substring of str, used to parse the opening tags of the formatting syntax
sc4                 : a length-4 substring of str, used to parse the closing tags of the formatting syntax
sa, sb              : sub-strings of str, which when concatenated together, yield a string equal to str,
//                    omitting a formatting tag.
//                    Used in building the parsed rtf string
h_r, h_g, h_b       : the hex values of a RGB color, broken into the red, green, and blue values
d_r, d_g, d_b       : the dec values of a RGB color, broken into the red, green, and blue values
p1, p2              : the first and second digits of a 2-digit hex value for a given RGB color channel
w                   : "which" color does the hex belong to. A temporary variable used to build the color for the color markup syntax
col_map             : a ds_map of the rtf string containing info about the color formatting
rtf_map             : a ds_map of the rtf string containing info about the bold and italic formatting

The ds_maps are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color for each character.
*/
var next_x, next_y, rtf_x, rtf_y, str, f_r, f_b, f_i, f_bi, mode, original_color, c, i, j, sc3, sc4, sa, sb, h_r, h_b, h_g, d_r, d_g, d_g, p1, p2, w;

next_x = 0;
next_y = 0;

rtf_x  = argument0;
rtf_y  = argument1;
str    = argument2;

f_r    = argument3;
f_b    = argument4;
f_i    = argument5;
f_bi   = argument6;

//modes
//1 = reg
//2 = bold
//3 = italic
//4 = bold & italic
mode     = 1;
original_color = draw_get_color();
c        = original_color;
rtf_map  = ds_map_create();
col_map  = ds_map_create();

for(i = 0; i <= string_length(str); i++){
sc3 = string_copy(str, i, 3);
sc4 = string_copy(str, i, 4);
if(sc3 == "[b]"){
if(mode == 1){mode = 2;}
if(mode == 3){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/b]"){
if(mode == 2){mode = 1;}
if(mode == 4){mode = 3;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc3 == "[i]"){
if(mode == 1){mode = 3;}
if(mode == 2){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/i]"){
if(mode == 3){mode=1;}
if(mode == 4){mode=2;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
ds_map_add(col_map, i - 1, c);
if(sc3 == "[c="){
h_r = string_copy(str, i + 3, 2);
h_g = string_copy(str, i + 5, 2);
h_b = string_copy(str, i + 7, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
//TODO: Can't this be done using hex_to_dec() or something?
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1); //* power(16,0)
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2) * 16; //power(16,1)
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + h_r + h_g + h_b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc4 == "[/c]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
}

for(i = 1; i <= string_length(str); i++){
if(string_copy(str, i, 1) == "#"){next_x = 0; next_y += string_height("#");}
draw_set_color(ds_map_find_value(col_map, i));
switch(ds_map_find_value(rtf_map, i)){
case 1:
draw_set_font(f_r);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 2:
draw_set_font(f_b);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 3:
draw_set_font(f_i);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 4:
draw_set_font(f_bi);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
}//end switch
}

//reset color
draw_set_color(original_color);
//free up the ds_maps
ds_map_destroy(rtf_map);
ds_map_destroy(col_map);

Last edited by csanyk (2013-05-24 07:57:35)

Offline

## #2 2013-05-20 10:38:29

xot
Registered: 2007-08-18
Posts: 1,201

### Re: draw_text_rtf

Thanks for posting this, csanyk.

I haven't played with this much, but I saw what looks like a couple of bugs while I was looking over the code.

First up is the creation of ds_map data structures without destroying them at the end of the script. That's an obvious memory leak. Are ds_maps even the best choice here? Since everything is being done on a single character basis, ds_lists seem like a more natural fit. That said, doing everything on a single character basis is a major drawback to this script ... but it IS simple and simple is good. A better solution would probably require a lot of changes.

Second is the for loop that processes the tags. It starts indexing from zero when GM strings start at one.

This may not be a bug exactly, but I'm bothered by the way the index i is decremented in the loop. It seems a little clumsy. I haven't analyzed it here but that sort of thing can lead to infinite loops if not handled carefully. I wonder if this is even necessary. I also think using string_delete rather than piecing together substrings would simplify things.

It seems like the hex conversion could be done a little more neatly. I'd rather avoid requiring hex_to_dec() / hex_to_color() scripts due to the general overhead of calling scripts. It's unfortunate that GM can't convert hex to decimal using the real() function.

The use of named color constants could be handled with some simple pre-processing, although it's not exactly elegant and wouldn't handle custom color constants.

string_replace_all(str, "[c=c_black]", "[c=#000000]");
string_replace_all(str, "[c=c_red]", "[c=#ff0000]");
etc...

I think I'll play with this some more today, see what I can come up with.

Abusing forum power since 1986.

Offline

## #3 2013-05-20 11:25:10

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

Something to keep in mind, I'd prefer not to limit the named colors to the built-in constants.  I frequently use variables to store color names, like:

c_shirt = c_red;

or

c_my_custom_color = make_color_rgb(r, g, b);

and then use c_shirt to provide a more semantically meaningful named color for a property, such as the color of the avatar's shirt.  So, ideally, I'd like to be able to pass in any variable or constant and (provided that it stores a value of a GML color) the function works.  Doing string_replace() to convert the built-in names is a quick & dirty fix, but isn't a step in the direction of a general solution.

Offline

## #4 2013-05-20 13:17:14

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

Trying to be helpful, added a block comment to the script, providing a "key" to understanding the purpose of all the local vars used in the script.

Disclaimer:  Since I didn't write the original code, I may be missing something, subtle or obvious, about these interpretations I've made of the original script by Miah_84.  The ones I wasn't sure about, I put :? instead of :, to signify my uncertainty that I fully understand them.

Offline

## #5 2013-05-21 00:57:46

xot
Registered: 2007-08-18
Posts: 1,201

### Re: draw_text_rtf

Regarding the use of and constants or variables for colors, I don't think it is possible to convert the string representing the name and get the value associated with it. That requires functionality which has been removed from GameMaker. At best one would need to create a ds_map of the colors keyed to the string names.

Abusing forum power since 1986.

Offline

## #6 2013-05-21 10:32:04

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

xot wrote:

Regarding the use of and constants or variables for colors, I don't think it is possible to convert the string representing the name and get the value associated with it. That requires functionality which has been removed from GameMaker. At best one would need to create a ds_map of the colors keyed to the string names.

That's unfortunate:(  While I really like the performance improvements that come with moving away from interpreted code, I really appreciated the power of self-modifying code, and was just starting to get into it when GM:S came out.  It was one of the really neat (and under-appreciated) features of Old GameMaker that made it interesting and cool, and that I could point to and say "See, THIS is why it's not a toy!"

Offline

## #7 2013-05-23 13:34:03

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

Csanyk, A set of arrays/ds_list will be fine for color names and still allow for [c= instead of [c_name= . I will read more through your post when I have a little more time

Last edited by Miah_84 (2013-05-24 19:57:23)

Offline

## #8 2013-05-23 14:02:06

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

Miah_84 wrote:

Csanyk, your example has [c=# , this is not correct, it will cause a line break. A set of arrays/ds_map (list doesn't have the same options that map has) will be fine for color names and still allow for [c= instead of [c_name= . I will read more through your post when I have a little more time

That's rather weird, because I don't remember inserting any # characters into the script!  I thought those were there all along?  In any case, there's no need for them, and they do cause additional headaches with the formatting, so they can come out.

Update: Oh, I see where I had it in the comments I added to the script.  Thanks for catching that, I've corrected it.

Last edited by csanyk (2013-05-23 14:11:24)

Offline

## #9 2013-05-23 17:10:41

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

Line and lines can be removed,  I overlooked them.

Here are the :? You have. You basically got them already.

w is used as "which" color does the hex belong to. A temp variable.

col map and rtf map are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color for each character.

Last edited by Miah_84 (2013-05-24 19:57:51)

Offline

## #10 2013-05-23 20:41:20

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

Miah_84 wrote:

str = "This string has some [c_name=c_red]RED[/c_name] text in [c=#0000FF]it[/c]." is where the confusion is.

Line and lines can be removed,  I overlooked them.

Here are the :? You have. You basically got them already.

w is used as "which" color does the hex belong to. A temp variable.

col map and rtf map are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color for each character.

Thanks for clearing those up. I'm glad to know I was pretty much right about everything, too:)

Offline

## #11 2013-05-23 21:03:11

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

Been thinking about this problem with the named color variables.  It appears that there's no way to convert a string to a variable name in GM:S.  It could be done in GM8.1 and earlier, but I want to use this script in Studio, so that doesn't really help me.  A version of this for GM8.1 would be a good project fork though.

So what can we do in Studio?  We have scripts that can convert a GML color value into RGB, so could we do something like this?

named_color = make_color_rgb(RRGGBB);

...

colorful_string = "Here's a string that I want to add some colored text to, right [c=" + string(color_to_rgb(named_color)) + "here[/c]";

draw_text_rgb(colorful_string);

SLIGHTLY annoying to have to do that, but at least do-able.

xot, Miah, anyone else have a better idea for a way to do it in GM:S?

Last edited by csanyk (2013-05-23 21:03:31)

Offline

## #12 2013-05-24 00:07:36

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

That's not a bad idea, but I'm going to try a different approach to this. let me see what i can manage. also, using hextodec() could be done, but i was originally trying to make this not dependent on any other scripts for two reason..... first, its easier to track problems/follow flow in 1 script. second, i dont know who made the hextodec or if they allow it for commercial use/ royalty free use, so this prevents any questionable issues for users that want to make this part of a commercial game. the last thing i want is for a script i make to depend on someone else giving their consent if someone can use something or not. I feel that if i take the time to make it all in one then there is no worry or question about this for anyone.

Last edited by Miah_84 (2013-05-24 19:58:13)

Offline

## #13 2013-05-24 02:18:56

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

I have implemented [RGB=blah,blah,blah] and I started working on the [c=c_ColorName] section and I was getting my color codes from http://i.msdn.microsoft.com/dynimg/IC210551.png for this, but several are not coming up correctly in GM. look at DodgeBlue for example (1E90FF) it is nothing like the MSDN color.  Some are perfect, but some are way off. any suggestions? Im thinking about visually comparing the MSDN and omitting the ones that don't match correctly. Below is the code that I have put together so far, which includes the [RGB]

<code omitted to save scrolling since csanyk post it again below>

SIDE NOTE: looks like they have finally brought back array sizes using array_length_1d(), array_length_2d() in version 1.1.1008 beta. soon i will be able to use arrays properly again, that could end up being quite useful here at some point.

Last edited by Miah_84 (2013-05-24 19:48:34)

Offline

## #14 2013-05-24 08:31:06

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

[rgb=r,g,b] is a very nice enhancement. nice work!

I'm not sure why the MSDN named colors aren't matching.  How are you creating them?  I didn't see that in your code, if it's there I must have glossed over it. Can you post the code + a screen capture of what GM is rendering?  I only see the .png from MSDN, but don't have your output to compare it with.

GML has its own named color constants, which I'm not sure if this list from the YYG wiki is complete or not, but could the name you're looking at be in conflict with an existing constant?  I'd be surprised if YYG defined DodgeBlue already, but it's something to check... otherwise are you certain that you have the color value correct?

I'm sure users of this script may want to import named colors from other frameworks, such as .NET's named colors, or web colors.  If you're thinking to pre-create a lengthy extension of the named GML colors, then you need to worry about the pre-defined color matching the color from the source namespace that you're appropriating the name from.  Otherwise, I think getting the color values to look right for a given name should be a concern for the end-user.

My thought is to make the function as flexible as possible by allowing the user to define a color variable of arbitrary name, and then use it in this script.  In my own coding style, I like to name colors with the prefix "c_", following the GML color constant convention, and then naming the color, either by naming the color itself (eg "aquamarine") OR naming how I'm using the color (eg, c_sweater, c_racecar, c_sunset, c_player2, c_radar_blip, etc.)

I did a little reformatting to your rgb additions to match the coding style that I've been using throughout the rest of the script.  DISCLAIMER Caution: I don't have access to GM:S on the computer I'm posting from, so have not tested to verify that the changes didn't break anything.

/*
Original draw_text_rtf script by Miah_84.  Additions by csanyk.  This script may be used or modified freely, no attribution needed.

draw_text_rtf(x, y, string, regular_font, bold_font, italic_font, bold_italic_font)

Description: Draws a formatted string using BBCode-like syntax for internal formatting within the string.

Returns: void

Caution:  Only works for halign==left.  Not intended to draw centered or right-aligned text.

argument0 = the x position of the string
argument1 = the y position of the string
argument2 = the string.
argument3 = the regular font
argument4 = the bold font
argument5 = the italic font
argument6 = the bold italic font

The string may contain the following syntax:

[b]bold text[/b]
[i]italic text[/i]
[c=RRGGBB]colored text[/c] (RR, GG, BB are hex values)
[rgb=R,G,B]colored text[/rgb] (R,G,B are integers)
TODO: [c=c_ColorName]colored text using named color[/c](not yet implemented)
*/

/*
local var definitions

next_x, next_y      : the x and y position of the next substring of the formatted RTF draw
rtf_x, rtf_y        : the x and y position of the rtf string
str                 : the raw rtf string, with syntax formatting awaiting parsing;
f_r, f_b, f_i, f_bi : the index of regular, bold, italic, and bold italic fonts
mode                : a control flag that sets the mode that the parser is currently operating in, eg regular, bold, italic, bold&italic
original_color      : temp storage of the original drawing color, so that the function can revert back to it when needed
i, j                : for loop iterator, re-used several times throughout the function
sc3                 : a lowercase length-3 substring of str, used to parse tags of the formatting syntax
sc4                 : a lowercase length-4 substring of str, used to parse tags of the formatting syntax
sc5                 : a lowercase length-5 substring of str, used to parse tags of the formatting syntax
sc6                 : a lowercase length-6 substring of str, used to parse tags of the formatting syntax
sa, sb              : sub-strings of str, which when concatenated together, yield a string equal to str,
//                    omitting a formatting tag.
//                    Used in building the parsed rtf string
h_r, h_g, h_b       : the hex values of a RGB color, broken into the red, green, and blue values
d_r, d_g, d_b       : the dec values of a RGB color, broken into the red, green, and blue values
rgb                 : sub-strings of str to get values for r,g,b
r,g,b               : sub-strings of rgb for individual red, green, blue values
p1, p2              : the first and second digits of a 2-digit hex value for a given RGB color channel
w                   : "which" color does the hex belong to. A temporary variable used to build the color for the color markup syntax
col_map             : a ds_map of the rtf string containing info about the color formatting
rtf_map             : a ds_map of the rtf string containing info about the bold and italic formatting

The ds_maps are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color for each character.
*/
var next_x, next_y, rtf_x, rtf_y, str, f_r, f_b, f_i, f_bi, mode, original_color, c, i, j, sc3, sc4, sc5, sc6, sa, sb, h_r, h_b, h_g, d_r, d_g, d_g, p1, p2, w, rgb, r, g, b;

next_x = 0;
next_y = 0;

rtf_x  = argument0;
rtf_y  = argument1;
str    = argument2;

f_r    = argument3;
f_b    = argument4;
f_i    = argument5;
f_bi   = argument6;

//modes
//1 = reg
//2 = bold
//3 = italic
//4 = bold & italic
mode     = 1;
original_color = draw_get_color();
c        = original_color;
rtf_map  = ds_map_create();
col_map  = ds_map_create();

for(i = 0; i <= string_length(str); i++){
sc3 = string_lower(string_copy(str, i, 3));
sc4 = string_lower(string_copy(str, i, 4));
sc5 = string_lower(string_copy(str, i, 5));
sc6 = string_lower(string_copy(str, i ,6));
if(sc3 == "[b]"){
if(mode == 1){mode = 2;}
if(mode == 3){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/b]"){
if(mode == 2){mode = 1;}
if(mode == 4){mode = 3;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc3 == "[i]"){
if(mode == 1){mode = 3;}
if(mode == 2){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/i]"){
if(mode == 3){mode=1;}
if(mode == 4){mode=2;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
ds_map_add(col_map, i - 1, c);
if(sc3 == "[c=" && sc5 != "[c=c_"){
h_r = string_copy(str, i + 3, 2);
h_g = string_copy(str, i + 5, 2);
h_b = string_copy(str, i + 7, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1);
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2) * 16;
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + h_r + h_g + h_b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc4 == "[/c]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc5 == "[rgb="){
rgb = string_copy(str, i, 17);
rgb = string_copy(rgb, 0, string_pos("]", rgb) - 1);
rgb = string_replace(rgb, "[rgb=", "");
r = string_copy(rgb,0,string_pos(", ", rgb) - 1);
rgb = string_replace(rgb, r + ",", "");
g = string_copy(rgb, 0, string_pos(",", rgb) - 1);
rgb = string_replace(rgb, g + ",", "");
b = rgb;
c = make_color_rgb(real(r), real(g), real(b));
ds_map_replace(col_map, i, c);
str = string_replace(str, "[rgb=" + r + "," + g + "," + b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc6 == "[/rgb]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 6, string_length(str) - i - 5);
str = sa + sb;
i--;
}
}

for(i = 1; i <= string_length(str); i++){
if(string_copy(str, i, 1) == "#"){next_x = 0; next_y += string_height("#");}
draw_set_color(ds_map_find_value(col_map, i));
switch(ds_map_find_value(rtf_map, i)){
case 1:
draw_set_font(f_r);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 2:
draw_set_font(f_b);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 3:
draw_set_font(f_i);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 4:
draw_set_font(f_bi);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
}//end switch
}

//reset color
draw_set_color(original_color);
//free up the ds_maps
ds_map_destroy(rtf_map);
ds_map_destroy(col_map);

Last edited by csanyk (2013-05-24 08:42:14)

Offline

## #15 2013-05-24 19:40:38

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

IMPORTANT >>>>>> I MADE A MISTAKE IN THE HEXTODEC TRANSLATION. THE P1 SHOULD BE MULTIPLIED BY 16, AND P2 SHOULD NOT. PLEASE FIX THIS IN YOUR PREVIOUS POSTS

MSDN wasnt matching because i'm a goof. I was grabbing the hex wrong in my project. FFAABB for example was showing R: FF G: FA B: AA ...i did a single advance instead of a double...the MSDN and GM do match. I will implement this probably tonight. namespace wont be an issue since GM uses all lowercase such as c_red and this code will be case sensitive so c_Red (we will need to explain that this is case sensitive in the example/description area). here is the project that i made to test this

I DIDN'T DESTROY THE LISTS OR DECLARE ANYTHING BECAUSE THIS WAS A TEST PROJECT

Object1 create:

names = ds_list_create() // c_blah name
hv = ds_list_create() //hex value for c_blah
item = 0

Object1 draw

h_r = string_copy(ds_list_find_value(hv,item), 1, 2);
h_g = string_copy(ds_list_find_value(hv,item), 3, 2);
h_b = string_copy(ds_list_find_value(hv,item), 5, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1)*16;
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2);
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);

draw_set_color(c)
draw_rectangle(100,100,300,300,false)

draw_set_color(c_black)
draw_text(10,16,"item: " + string(item))
draw_text(10,32,"name: " + string(ds_list_find_value(names,item)))
draw_text(10,48,"hex: " + string(ds_list_find_value(hv,item)))
draw_text(10,64,"RGB: " + string(d_r) + "," + string(d_g) + "," + string(d_b))

draw_rectangle(100,100,300,300,true)

Object1 Left_Press

item -= 1
if(item=-1){item=ds_list_size(names)-1}

Object1 Right Press

item += 1
if(item>=ds_list_size(names)){item=0}

Last edited by Miah_84 (2013-05-25 11:44:50)

Offline

## #16 2013-05-24 21:03:16

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

Here is the one that includes the [c=c_NamedColor]. Please double check that I formatted all the things how you normally do and added all the description. I tried to get them all, but i might have overlooked something.

/*
Original draw_text_rtf script by Miah_84.  Additions by csanyk.  This script may be used or modified freely, no attribution needed.

draw_text_rtf(x, y, string, regular_font, bold_font, italic_font, bold_italic_font)

Description: Draws a formatted string using BBCode-like syntax for internal formatting within the string.

Returns: void

Caution:  Only works for halign==left.  Not intended to draw centered or right-aligned text.

argument0 = the x position of the string
argument1 = the y position of the string
argument2 = the string.
argument3 = the regular font
argument4 = the bold font
argument5 = the italic font
argument6 = the bold italic font

The string may contain the following syntax:

[b]bold text[/b]
[i]italic text[/i]
[c=RRGGBB]colored text[/c]
[c=c_NamedColor]colored text using named color[/c] (IMPORTANT: c_NamedColor is UPPER/lower CASE SENSITIVE. see the MSDN list here: http://i.msdn.microsoft.com/dynimg/IC210551.png)
*/

/*
local var definitions

next_x, next_y      : the x and y position of the next substring of the formatted RTF draw
rtf_x, rtf_y        : the x and y position of the rtf string
str                 : the raw rtf string, with syntax formatting awaiting parsing;
f_r, f_b, f_i, f_bi : the index of regular, bold, italic, and bold italic fonts
mode                : a control flag that sets the mode that the parser is currently operating in, eg regular, bold, italic, bold&italic
original_color      : temp storage of the original drawing color, so that the function can revert back to it when needed
i, j                : for loop iterator, re-used several times throughout the function
sc3                 : a lowercase length-3 substring of str, used to parse tags of the formatting syntax
sc4                 : a lowercase length-4 substring of str, used to parse tags of the formatting syntax
sc5                 : a lowercase length-5 substring of str, used to parse tags of the formatting syntax
sc6                 : a lowercase length-6 substring of str, used to parse tags of the formatting syntax
sa, sb              : sub-strings of str, which when concatenated together, yield a string equal to str,
//                    omitting a formatting tag.
//                    Used in building the parsed rtf string
h_r, h_g, h_b       : the hex values of a RGB color, broken into the red, green, and blue values
d_r, d_g, d_b       : the dec values of a RGB color, broken into the red, green, and blue values
rgb                 : sub-strings of str to get values for r,g,b
r,g,b               : sub-strings of rgb for individual red, green, blue values
p1, p2              : the first and second digits of a 2-digit hex value for a given RGB color channel
w                   : "which" color does the hex belong to. A temporary variable used to build the color for the color markup syntax
col_map             : a ds_map of the rtf string containing info about the color formatting
rtf_map             : a ds_map of the rtf string containing info about the bold and italic formatting
names               : a ds_list of the names used for c_NamedColor
hv                  : a ds_list of the hex values associated with the names ds_list
cn                  : a substring of str that gets the c_NamedColor value
hex_str             : a string from ds_list hv that corresponds with c_NamedColor from the ds_list names

The ds_maps are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color for each character.
*/
var next_x, next_y, rtf_x, rtf_y, str, f_r, f_b, f_i, f_bi, mode, original_color, c, i, j, sc3, sc4, sc5, sc6, sa, sb, h_r, h_b, h_g, d_r, d_g, d_g, p1, p2, w, rgb, r, g, b, names, hv, cn, hex_str;

next_x = 0;
next_y = 0;

rtf_x  = argument0;
rtf_y  = argument1;
str    = argument2;

f_r    = argument3;
f_b    = argument4;
f_i    = argument5;
f_bi   = argument6;

//modes
//1 = reg
//2 = bold
//3 = italic
//4 = bold & italic
mode     = 1;
original_color = draw_get_color();
c        = original_color;
rtf_map  = ds_map_create();
col_map  = ds_map_create();
names    = ds_list_create();
hv       = ds_list_create(); //hex values

for(i = 0; i <= string_length(str); i++){
sc3 = string_lower(string_copy(str, i, 3));
sc4 = string_lower(string_copy(str, i, 4));
sc5 = string_lower(string_copy(str, i, 5));
sc6 = string_lower(string_copy(str, i ,6));
if(sc3 == "[b]"){
if(mode == 1){mode = 2;}
if(mode == 3){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 = "[/b]"){
if(mode == 2){mode = 1;}
if(mode == 4){mode = 3;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc3 == "[i]"){
if(mode == 1){mode = 3;}
if(mode == 2){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/i]"){
if(mode == 3){mode=1;}
if(mode == 4){mode=2;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
ds_map_add(col_map, i - 1, c);
if(sc3 == "[c=" && sc5 != "[c=c_"){
h_r = string_copy(str, i + 3, 2);
h_g = string_copy(str, i + 5, 2);
h_b = string_copy(str, i + 7, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1) * 16;
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2);
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + h_r + h_g + h_b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc5 == "[c=c_"){
cn = string_copy(str,i,32)//random length, long enough to cover all names

cn = string_replace(cn,"[c=","")
cn = string_copy(cn,0,string_pos("]",cn)-1)

for(j=0;j<ds_list_size(names);j++){
if(cn == ds_list_find_value(names,j)){
hex_str = ds_list_find_value(hv,j)
}
}
h_r = string_copy(hex_str, 1, 2);
h_g = string_copy(hex_str, 3, 2);
h_b = string_copy(hex_str, 5, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1) * 16;
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2);
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + cn + "]", ""); //only replaces the first one, not all
i--;
}
if(sc4 == "[/c]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc5 == "[rgb="){
rgb = string_copy(str,i,17)
rgb = string_copy(rgb,0,string_pos("]",rgb)-1)
rgb = string_replace(rgb,"[rgb=","")
r = string_copy(rgb,0,string_pos(",",rgb)-1)
rgb = string_replace(rgb,r+",","")
g = string_copy(rgb,0,string_pos(",",rgb)-1)
rgb = string_replace(rgb,g+",","")
b = rgb
c = make_color_rgb(real(r), real(g), real(b));
ds_map_replace(col_map, i, c);
str = string_replace(str,"[rgb=" + r + "," + g + "," + b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc6 == "[/rgb]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 6, string_length(str) - i - 5);
str = sa + sb;
i--;
}
}

for(i = 1; i <= string_length(str); i++){
if(string_copy(str, i, 1) == "#"){next_x = 0; next_y += string_height("#");}
draw_set_color(ds_map_find_value(col_map, i));
switch(ds_map_find_value(rtf_map, i)){
case 1:
draw_set_font(f_r);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 2:
draw_set_font(f_b);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 3:
draw_set_font(f_i);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 4:
draw_set_font(f_bi);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
}//end switch
}

//reset color
draw_set_color(original_color);
//free up the ds_maps and ds_lists
ds_map_destroy(rtf_map);
ds_map_destroy(col_map);
ds_list_destroy(names);
ds_list_destroy(hv);

Last edited by Miah_84 (2013-05-25 11:45:27)

Offline

## #17 2013-05-25 12:13:19

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

I am looking at the potential of surfaces with this. do either of you know if there is a function that can check if a variable is a surface or if it was even declared? for example:

if(surface_was_created(rtf_surf) = false){ //looking for something like this
if(is_declared(rtf_surf) = false){//looking for something like this also
//initialize the surface
}
}else{
if(surface_exists(rtf_surf)){ //was the surface already created?
//draw the surface. saves processing
}else{
//make/rebuild the surface
}
}

surface_exists() does not do what im looking for and i dont think they have built something to do this. This would allow for the script to not have to require a previously initialized rtf_surf = surface_create(blah)

Last edited by Miah_84 (2013-05-25 12:16:59)

Offline

## #18 2013-05-25 12:58:36

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

I have a lot going on this weekend due to it being a holiday, but I will try to play with your updated scripts soon and test them out.  Thanks for all the enhancements!

I am not sure that creating the surface within the script makes a lot of sense.  I could be wrong, but this is what I understand from my experience with using them.

The speed benefit from using surfaces comes primarily from not having to re-draw everything to the screen in each step.

The best case performance increase (compose drawing to surface once, use the surface many).  You can draw the surface one time, and as long as it doesn't need to change, you can just draw_surface() each step instead of doing all the draw calls that compose the image that you're storing on the surface.

The next best case is to compose an initial image, and then append to it as needed by drawing additional stuff when the need arises.  For example, you can draw a blood splatter sprite to the surface, and as more and more violence happens, you can update the blood_surface by drawing additional sprites to it, without clearing the old drawing.

The next best thing is to draw to create the surface once, and then re-compose the surface each step, clearing the surface and re-drawing each time the surface needs to change.  This is still faster because drawing to video memory is still much faster than drawing directly to the screen.  But it is not quite as fast as composing the surface once because you have to re-do all those draw calls to re-compose the image every time it changes.  If the image doesn't necessarily change each step, it can still be a huge performance bump.

Creating and destroying the surface every time you need to use a draw function, and completely re-doing the composition each draw_function call is probably the least advantageous method of using surfaces.

So, for ease of use, your idea to make the surface creation internal to the function if the surface doesn't exist already seems like it'd be convenient, but both the check and the creation/destruction of the surface would slow the script down, and prevent the use of the more optimal approaches I outlined above.

Creating the surface *outside* of the draw_text_rtf() script would be tricky, because in order to guarantee you have the right size surface to fit all the text, you need to know the total height and width of the finished rendering of the RTF text.  GML has string_width and string_height functions for this, but you'd need to add up all the sub-strings that build the rtf string, and get their proper measurements (with the current draw_set_font matching the font used to draw each substring).  So it's pretty non-trivial to get the size right.

I'm really not sure the best solution for this, but it might be to set a surface to match the size of the view or the window or the room, and just worry about drawing the part of the rtf text that would be visible in-view each step.

Hope this helps...

Offline

## #19 2013-05-25 13:00:26

Miah_84
Member
Registered: 2013-05-23
Posts: 14

### Re: draw_text_rtf

Implemented Underline. Please check over the formatting/layout/description of the local variables so that it is consistent with the way you make your scripts. what else are we trying to add to this? I think we have covered several things already and I am not sure what else to add now. Thanks for the suggestion csanyk, this has been a fun side project.

/*
Original draw_text_rtf script by Miah_84.  Additions by csanyk.  This script may be used or modified freely, no attribution needed.

draw_text_rtf(x, y, string, regular_font, bold_font, italic_font, bold_italic_font)

Description: Draws a formatted string using BBCode-like syntax for internal formatting within the string.

Returns: void

Caution:  Only works for halign==left.  Not intended to draw centered or right-aligned text.

argument0 = the x position of the string
argument1 = the y position of the string
argument2 = the string.
argument3 = the regular font
argument4 = the bold font
argument5 = the italic font
argument6 = the bold italic font

The string may contain the following syntax:

[b]bold text[/b]
[i]italic text[/i]
[c=RRGGBB]colored text[/c]
[c=c_NamedColor]colored text using named color[/c] (IMPORTANT: c_NamedColor is UPPER/lower CASE SENSITIVE. see the MSDN list here: http://i.msdn.microsoft.com/dynimg/IC210551.png)
[u]underlined text[/u]
*/

/*
local var definitions

next_x, next_y      : the x and y position of the next substring of the formatted RTF draw
rtf_x, rtf_y        : the x and y position of the rtf string
str                 : the raw rtf string, with syntax formatting awaiting parsing;
f_r, f_b, f_i, f_bi : the index of regular, bold, italic, and bold italic fonts
mode                : a control flag that sets the mode that the parser is currently operating in, eg regular, bold, italic, bold&italic
original_color      : temp storage of the original drawing color, so that the function can revert back to it when needed
i, j                : for loop iterator, re-used several times throughout the function
sc3                 : a lowercase length-3 substring of str, used to parse tags of the formatting syntax
sc4                 : a lowercase length-4 substring of str, used to parse tags of the formatting syntax
sc5                 : a lowercase length-5 substring of str, used to parse tags of the formatting syntax
sc6                 : a lowercase length-6 substring of str, used to parse tags of the formatting syntax
sa, sb              : sub-strings of str, which when concatenated together, yield a string equal to str,
//                    omitting a formatting tag.
//                    Used in building the parsed rtf string
h_r, h_g, h_b       : the hex values of a RGB color, broken into the red, green, and blue values
d_r, d_g, d_b       : the dec values of a RGB color, broken into the red, green, and blue values
rgb                 : sub-strings of str to get values for r,g,b
r,g,b               : sub-strings of rgb for individual red, green, blue values
p1, p2              : the first and second digits of a 2-digit hex value for a given RGB color channel
w                   : "which" color does the hex belong to. A temporary variable used to build the color for the color markup syntax
col_map             : a ds_map of the rtf string containing info about the color formatting
rtf_map             : a ds_map of the rtf string containing info about the bold and italic formatting
names               : a ds_list of the names used for c_NamedColor
hv                  : a ds_list of the hex values associated with the names ds_list
cn                  : a substring of str that gets the c_NamedColor value
hex_str             : a string from ds_list hv that corresponds with c_NamedColor from the ds_list names
ul                  : "underline" boolean for use with [u] and [/u] to be used in ul_map
ul_map              : a ds_map for the underline variable that keeps a list of underline

The ds_maps are used in the last for loop to actually build the markup sentence. Each character is assigned a color and font style using these. this helps to layout the placement for the next character and provide the correct color and format for each character.
*/
var next_x, next_y, rtf_x, rtf_y, str, f_r, f_b, f_i, f_bi, mode, original_color, c, i, j, sc3, sc4, sc5, sc6, sa, sb, h_r, h_b, h_g, d_r, d_g, d_g, p1, p2, w, rgb, r, g, b, names, hv, cn, hex_str, ul, ul_map;

next_x = 0;
next_y = 0;

rtf_x  = argument0;
rtf_y  = argument1;
str    = argument2;

f_r    = argument3;
f_b    = argument4;
f_i    = argument5;
f_bi   = argument6;

//modes
//1 = reg
//2 = bold
//3 = italic
//4 = bold & italic
mode     = 1;
original_color = draw_get_color();
c        = original_color;
rtf_map  = ds_map_create();
col_map  = ds_map_create();
ul       = false;
ul_map   = ds_map_create();
names    = ds_list_create();
hv       = ds_list_create();

for(i = 0; i <= string_length(str); i++){
sc3 = string_lower(string_copy(str, i, 3));
sc4 = string_lower(string_copy(str, i, 4));
sc5 = string_lower(string_copy(str, i, 5));
sc6 = string_lower(string_copy(str, i ,6));
if(sc3 == "[b]"){
if(mode == 1){mode = 2;}
if(mode == 3){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 = "[/b]"){
if(mode == 2){mode = 1;}
if(mode == 4){mode = 3;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc3 == "[i]"){
if(mode == 1){mode = 3;}
if(mode == 2){mode = 4;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/i]"){
if(mode == 3){mode=1;}
if(mode == 4){mode=2;}
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
ds_map_add(col_map, i - 1, c);
if(sc3 == "[c=" && sc5 != "[c=c_"){
h_r = string_copy(str, i + 3, 2);
h_g = string_copy(str, i + 5, 2);
h_b = string_copy(str, i + 7, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1) * 16;
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2);
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + h_r + h_g + h_b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc5 == "[c=c_"){
cn = string_copy(str,i,32)//random length, long enough to cover all names

cn = string_replace(cn,"[c=","")
cn = string_copy(cn,0,string_pos("]",cn)-1)

for(j=0;j<ds_list_size(names);j++){
if(cn == ds_list_find_value(names,j)){
hex_str = ds_list_find_value(hv,j)
}
}
h_r = string_copy(hex_str, 1, 2);
h_g = string_copy(hex_str, 3, 2);
h_b = string_copy(hex_str, 5, 2);
for(j = 0; j <= 2; j++){
switch(j){
case 0: w = h_r; break;
case 1: w = h_g; break;
case 2: w = h_b; break;
}
p1 = string_lower(string_char_at(w, 1));
if(p1 == "a"){p1 = "10";}
if(p1 == "b"){p1 = "11";}
if(p1 == "c"){p1 = "12";}
if(p1 == "d"){p1 = "13";}
if(p1 == "e"){p1 = "14";}
if(p1 == "f"){p1 = "15";}
dec = real(p1) * 16;
p2 = string_lower(string_char_at(w, 2));
if(p2 == "a"){p2 = "10";}
if(p2 == "b"){p2 = "11";}
if(p2 == "c"){p2 = "12";}
if(p2 == "d"){p2 = "13";}
if(p2 == "e"){p2 = "14";}
if(p2 == "f"){p2 = "15";}
dec += real(p2);
switch(j){
case 0: d_r = dec; break;
case 1: d_g = dec; break;
case 2: d_b = dec; break;
}
}
c = make_color_rgb(d_r, d_g, d_b);
ds_map_replace(col_map, i, c);
str = string_replace(str,"[c=" + cn + "]", ""); //only replaces the first one, not all
i--;
}
if(sc4 == "[/c]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
if(sc5 == "[rgb="){
rgb = string_copy(str,i,17)
rgb = string_copy(rgb,0,string_pos("]",rgb)-1)
rgb = string_replace(rgb,"[rgb=","")
r = string_copy(rgb,0,string_pos(",",rgb)-1)
rgb = string_replace(rgb,r+",","")
g = string_copy(rgb,0,string_pos(",",rgb)-1)
rgb = string_replace(rgb,g+",","")
b = rgb
c = make_color_rgb(real(r), real(g), real(b));
ds_map_replace(col_map, i, c);
str = string_replace(str,"[rgb=" + r + "," + g + "," + b + "]", ""); //only replaces the first one, not all
i--;
}
if(sc6 == "[/rgb]"){
c = original_color;
ds_map_replace(col_map, i, c);
sa = string_copy(str, 0, i - 1);
sb = string_copy(str, i + 6, string_length(str) - i - 5);
str = sa + sb;
i--;
}
if(sc3 == "[u]"){
ds_map_replace(ul_map,i,!ul);
ul = !ul;
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+3, string_length(str) - i - 2);
str = sa + sb;
i--;
}
if(sc4 == "[/u]"){
ds_map_replace(ul_map,i,!ul);
ul = !ul;
sa = string_copy(str, 0, i-1);
sb = string_copy(str, i+4, string_length(str) - i - 3);
str = sa + sb;
i--;
}
}

for(i = 1; i <= string_length(str); i++){
if(string_copy(str, i, 1) == "#"){next_x = 0; next_y += string_height("#");}
draw_set_color(ds_map_find_value(col_map, i));
if(ds_map_find_value(ul_map,i)){
draw_line(rtf_x + next_x, rtf_y + next_y + string_height("|"), rtf_x + next_x + string_width(string_copy(str,i,1)), rtf_y + next_y + string_height("|"))
}
switch(ds_map_find_value(rtf_map, i)){
case 1:
draw_set_font(f_r);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 2:
draw_set_font(f_b);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 3:
draw_set_font(f_i);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
case 4:
draw_set_font(f_bi);
draw_text(rtf_x + next_x, rtf_y + next_y, string_copy(str, i, 1));
next_x += string_width(string_copy(str, i, 1));
break;
}//end switch
}

//reset color
draw_set_color(original_color);
//free up the ds_maps and ds_lists
ds_map_destroy(rtf_map);
ds_map_destroy(col_map);
ds_map_destroy(ul_map);
ds_list_destroy(names);
ds_list_destroy(hv);

Last edited by Miah_84 (2013-05-25 13:08:32)

Offline

## #20 2013-05-26 01:20:34

csanyk
Member
From: NE Ohio
Registered: 2011-04-07
Posts: 13
Website

### Re: draw_text_rtf

That's all the features I was really looking for in a GML script, short of a native function that implemented true rich text or hypertext or CSS.  I think bold, italic, colors, underline is a really solid set of formatting options... maybe a modified strikethrough, or superscript/subscript if you wanted to do a victory lap around this problem   Or versions of this script that did the same thing, only for centered and right aligned text.

But, nah, seriously, this is quite featureful and useful as it is, and I wouldn't want to see it bloated up too much.

I still need to play with it and test it out now; I'll let you know if I make any further modifications or run into problems that I can't fix.

Offline