6
6
* AUTHOR(S): James Westervelt, US Army CERL
7
* Updated by Huidae Cho, Markus Neteler
8
9
* PURPOSE: display text in active frame
10
11
* COPYRIGHT: (C) 2001 by the GRASS Development Team
12
13
* This program is free software under the GNU General Public
13
* License (>=v2). Read the file COPYING that comes with GRASS
14
* License (>=v2). Read the file COPYING that comes with GRASS
16
17
*****************************************************************************/
21
* Draw text in a text window. Text lines come from stdin.
23
* .C color_name change color
24
* .S size change text size
25
* .B 0 bold (double print) off
26
* .B 1 bold (double print) on
27
* .F name change font to name
22
* .F {font|path}[:charset] font
23
* .C {color_name|RR:GG:BB|0xRRGGBB} color
24
* .G {color_name|RR:GG:BB|0xRRGGBB} background color
25
* .S [+|-]size[p] text size
28
* .B {0|1} bold (double print) off/on
29
* .A {ll|lc|lr|cl|cc|cr|ul|uc|ur} align (TODO)
30
* .R [+|-]rotation[r] rotation
31
* +/-: relative rotation
33
* .I linespacing linespacing
34
* .X [+|-]x[%|p] x: relative to x origin
38
* .Y [+|-]y[%|p] y: relative to y origin
42
* .L {0|1} linefeed off/on
43
* .E [+|-]east[%|p] x origin: geographic coordinates
47
* .N [+|-]north[%|p] y origin: geographic coordinates
31
57
#include <stdlib.h>
32
58
#include <string.h>
33
59
#include <unistd.h>
34
60
#include <grass/gis.h>
35
61
#include <grass/display.h>
36
#include <grass/raster.h>
37
62
#include <grass/colors.h>
38
63
#include <grass/glocale.h>
65
#define BACKWARD_COMPATIBILITY
66
#define DEFAULT_COLOR "gray"
73
static void set_color(char *);
74
static int get_coordinates(double *, double *, double *, double *,
75
struct rectinfo, char **, char, char);
76
static void draw_text(char *, double *, double *, double, char *, double, char, int, int, int);
40
78
int main(int argc, char **argv)
42
char *cmd_ptr, *max_buff;
47
int R, G, B, color = 0;
48
int cur_dot_row, cur_dot_column;
80
struct GModule *module;
85
struct Option *fgcolor;
86
struct Option *bgcolor;
89
struct Option *rotation;
91
struct Option *linespacing;
94
struct Option *charset;
106
/* options and flags */
56
struct GModule *module;
57
struct Option *opt1, *opt2, *opt3, *opt4, *opt5;
62
/* Initialize the GIS calls */
128
double orig_x, orig_y;
129
double prev_x, prev_y;
132
int do_background, fg_color, bg_color;
134
/* initialize the GIS calls */
63
135
G_gisinit(argv[0]);
65
137
module = G_define_module();
66
module->keywords = _("display, cartography");
138
G_add_keyword(_("display"));
139
G_add_keyword(_("cartography"));
67
140
module->description =
68
141
_("Draws text in the active display frame on the graphics monitor using the current font.");
70
opt1 = G_define_option();
72
opt1->type = TYPE_DOUBLE;
75
opt1->options = "0-100";
143
opt.text = G_define_option();
144
opt.text->key = "text";
145
opt.text->type = TYPE_STRING;
146
opt.text->required = NO;
147
opt.text->description = _("Text to display");
149
opt.size = G_define_option();
150
opt.size->key = "size";
151
opt.size->type = TYPE_DOUBLE;
152
opt.size->required = NO;
153
opt.size->answer = "5";
154
opt.size->options = "0-100";
155
opt.size->description =
77
156
_("Height of letters in percentage of available frame height");
79
opt2 = G_define_option();
81
opt2->type = TYPE_STRING;
82
opt2->answer = "gray";
158
opt.fgcolor = G_define_option();
159
opt.fgcolor->key = "color";
160
opt.fgcolor->type = TYPE_STRING;
161
opt.fgcolor->answer = DEFAULT_COLOR;
162
opt.fgcolor->required = NO;
163
opt.fgcolor->description =
85
164
_("Text color, either a standard GRASS color or R:G:B triplet");
86
opt2->gisprompt = GISPROMPT_COLOR;
88
opt3 = G_define_option();
91
opt3->type = TYPE_INTEGER;
92
opt3->options = "1-1000";
165
opt.fgcolor->gisprompt = "old_color,color,color";
167
opt.bgcolor = G_define_option();
168
opt.bgcolor->key = "bgcolor";
169
opt.bgcolor->type = TYPE_STRING;
170
opt.bgcolor->required = NO;
171
opt.bgcolor->description =
172
_("Text background color, either a standard GRASS color or R:G:B triplet");
173
opt.bgcolor->gisprompt = "old_color,color,color";
175
opt.line = G_define_option();
176
opt.line->key = "line";
177
opt.line->required = NO;
178
opt.line->type = TYPE_INTEGER;
179
opt.line->options = "1-1000";
180
opt.line->description =
94
181
_("The screen line number on which text will begin to be drawn");
96
opt4 = G_define_option();
98
opt4->key_desc = "x,y";
99
opt4->type = TYPE_DOUBLE;
100
opt4->options = "0-100";
183
opt.at = G_define_option();
185
opt.at->key_desc = "x,y";
186
opt.at->type = TYPE_DOUBLE;
187
opt.at->required = NO;
188
opt.at->description =
103
189
_("Screen position at which text will begin to be drawn (percentage, [0,0] is lower left)");
105
opt5 = G_define_option();
106
opt5->key = "rotation";
107
opt5->type = TYPE_DOUBLE;
110
opt5->description = _("Rotation angle in degrees (counter-clockwise)");
112
flag_b = G_define_flag();
114
flag_b->description = _("Use bold text");
117
/* Check command line */
191
opt.align = G_define_option();
192
opt.align->key = "align";
193
opt.align->type = TYPE_STRING;
194
opt.align->required = NO;
195
opt.align->answer = "ll";
196
opt.align->options = "ll,lc,lr,cl,cc,cr,ul,uc,ur";
197
opt.align->description = _("Text alignment");
199
opt.rotation = G_define_option();
200
opt.rotation->key = "rotation";
201
opt.rotation->type = TYPE_DOUBLE;
202
opt.rotation->required = NO;
203
opt.rotation->answer = "0";
204
opt.rotation->description =
205
_("Rotation angle in degrees (counter-clockwise)");
207
opt.linespacing = G_define_option();
208
opt.linespacing->key = "linespacing";
209
opt.linespacing->type = TYPE_DOUBLE;
210
opt.linespacing->required = NO;
211
opt.linespacing->answer = "1.25";
212
opt.linespacing->description = _("Line spacing");
214
opt.font = G_define_option();
215
opt.font->key = "font";
216
opt.font->type = TYPE_STRING;
217
opt.font->required = NO;
218
opt.font->description = _("Font name");
220
opt.path = G_define_standard_option(G_OPT_F_INPUT);
221
opt.path->key = "path";
222
opt.path->required = NO;
223
opt.path->description = _("Path to font file");
224
opt.path->gisprompt = "old,font,file";
226
opt.charset = G_define_option();
227
opt.charset->key = "charset";
228
opt.charset->type = TYPE_STRING;
229
opt.charset->required = NO;
230
opt.charset->description =
231
_("Text encoding (only applicable to TrueType fonts)");
233
opt.input = G_define_standard_option(G_OPT_F_INPUT);
234
opt.input->required = NO;
235
opt.input->description = _("Input file");
237
flag.p = G_define_flag();
239
flag.p->description = _("Screen position in pixels ([0,0] is top left)");
241
flag.g = G_define_flag();
243
flag.g->description = _("Screen position in geographic coordinates");
245
flag.b = G_define_flag();
247
flag.b->description = _("Use bold text");
249
flag.r = G_define_flag();
251
flag.r->description = _("Use radians instead of degrees for rotation");
253
flag.s = G_define_flag();
255
flag.s->description = _("Font size is height in pixels");
258
/* check command line */
118
259
if (G_parser(argc, argv))
121
if (opt3->answer && opt4->answer)
262
/* parse and check options and flags */
264
if ((opt.line->answer && opt.at->answer) ||
265
(flag.p->answer && flag.g->answer))
122
266
G_fatal_error(_("Please choose only one placement method"));
127
"\nPlease enter text instructions. Enter EOF (ctrl-d) on last line to quit\n");
129
sscanf(opt1->answer, "%f", &size);
131
if (R_open_driver() != 0)
132
G_fatal_error(_("No graphics device selected"));
134
/* Parse and select text color */
135
if (sscanf(opt2->answer, "%d:%d:%d", &R, &G, &B) == 3) {
136
if (R >= 0 && R < 256 && G >= 0 && G < 256 && B >= 0 && B < 256) {
137
R_RGB_color(R, G, B);
138
color = 1; /* to make success test below happy */
142
color = D_translate_color(opt2->answer);
143
R_standard_color(color);
147
G_fatal_error(_("[%s]: No such color"), opt2->answer);
149
rotation = atof(opt5->answer);
151
if (D_get_cur_wind(window_name))
152
G_fatal_error(_("No current window"));
154
if (D_set_cur_wind(window_name))
155
G_fatal_error(_("Current window not available"));
157
/* Figure out where to put text */
158
D_get_screen_window(&t, &b, &l, &r);
159
R_set_window(t, b, l, r);
161
dots_per_line = (int)(size / 100.0 * (float)(b - t));
162
tsize = (int)(.8 * (float)dots_per_line);
166
sscanf(opt3->answer, "%d", &start_line);
170
cur_dot_row = t + (start_line - 1) * dots_per_line;
171
cur_dot_column = l + 5;
174
x = atof(opt4->answers[0]);
175
y = atof(opt4->answers[1]);
176
if (x < 0 || x > 100 || y < 0 || y > 100)
177
G_fatal_error(_("value [%.0f,%.0f] out of range [0-100]"), x, y);
179
cur_dot_row = t + (int)((b - t) * (100. - y) / 100.);
180
cur_dot_column = l + (int)((r - l) * x / 100.);
183
R_text_size(tsize, tsize);
184
R_text_rotation((float)rotation);
191
wind_file_name = G_tempfile();
192
if ((wind_file = fopen(wind_file_name, "w")) == NULL)
193
G_fatal_error(_("Unable to open temporary file <%s>"),
196
/* Do the plotting */
197
while (fgets(buff, 512, stdin)) {
198
fprintf(wind_file, "%s", buff);
200
for (cmd_ptr = buff + 2; *cmd_ptr == ' '; cmd_ptr++) ;
201
G_squeeze(cmd_ptr); /* added 6/91 DBS @ CWU */
202
switch (buff[1] & 0x7F) {
206
case 'C': /* color */
207
if (sscanf(cmd_ptr, "%d:%d:%d", &R, &G, &B) == 3) {
208
if (R >= 0 && R < 256 && G >= 0 && G < 256 && B >= 0 &&
210
R_RGB_color(R, G, B);
213
else if (color = D_translate_color(cmd_ptr))
214
R_standard_color(color);
217
if (sscanf(cmd_ptr, "%f", &size)) {
218
dots_per_line = (int)(size / 100.0 * (float)(b - t));
219
tsize = (int)(.8 * (float)dots_per_line);
220
R_text_size(tsize, tsize);
224
if (!sscanf(cmd_ptr, "%d", &bold))
227
case 'R': /* rotation */
228
if (sscanf(cmd_ptr, "%lf", &rotation)) {
229
R_text_rotation((float)rotation);
238
cur_dot_row += dots_per_line;
239
R_move_abs(cur_dot_column, cur_dot_row);
242
R_move_abs(cur_dot_column, 1 + cur_dot_row);
244
R_move_abs(cur_dot_column + 1, cur_dot_row);
255
G_malloc(strlen(wind_file_name) + strlen(G_recreate_command()) + 4);
256
sprintf(max_buff, "%s < %s", G_recreate_command(), wind_file_name);
257
D_add_to_list(max_buff);
260
R_text_rotation(0.0); /* reset */
268
text = opt.text->answer;
270
line = (opt.line->answer ? atoi(opt.line->answer) : 1);
272
/* calculate rotation angle in radian */
273
rotation = atof(opt.rotation->answer);
275
rotation *= M_PI / 180.0;
276
rotation = fmod(rotation, 2.0 * M_PI);
278
rotation += 2.0 * M_PI;
280
strncpy(align, opt.align->answer, 2);
281
linespacing = atof(opt.linespacing->answer);
283
bold = flag.b->answer;
287
if (opt.font->answer)
288
D_font(opt.font->answer);
289
else if (opt.path->answer)
290
D_font(opt.path->answer);
292
if (opt.charset->answer)
293
D_encoding(opt.charset->answer);
297
/* figure out where to put text */
298
D_get_src(&win.t, &win.b, &win.l, &win.r);
301
size = atof(opt.size->answer);
303
#ifdef BACKWARD_COMPATIBILITY
304
size = atof(opt.size->answer) / 100.0 * (win.b - win.t) / linespacing;
306
size = atof(opt.size->answer) / 100.0 * (win.b - win.t);
309
fg_color = D_parse_color(opt.fgcolor->answer, TRUE);
310
if (opt.bgcolor->answer) {
312
bg_color = D_parse_color(opt.bgcolor->answer, TRUE);
313
if (bg_color == 0) /* ie color="none" */
317
set_color(opt.fgcolor->answer);
321
if (opt.at->answer) {
322
if (get_coordinates(&x, &y, &east, &north,
323
win, opt.at->answers,
324
flag.p->answer, flag.g->answer))
325
G_fatal_error(_("Invalid coordinates"));
330
x = win.l + (size * linespacing + 0.5) - size; /* d.text: +5 */
331
y = win.t + line * (size * linespacing + 0.5);
337
D_text_size(size, size);
338
D_text_rotation(rotation * 180.0 / M_PI);
347
draw_text(text, &x2, &y2, size, align, rotation, bold, do_background, fg_color, bg_color);
351
D_text_rotation(0.0);
353
D_save_command(G_recreate_command());
359
if (!opt.input->answer || strcmp(opt.input->answer, "-") == 0)
362
cmd_fp = fopen(opt.input->answer, "r");
364
G_fatal_error(_("Unable to open input file <%s>"), opt.input->answer);
367
if (isatty(fileno(cmd_fp)))
369
_("\nPlease enter text instructions. Enter EOF (ctrl-d) on last line to quit\n"));
371
set_x = set_y = set_l = 0;
374
/* do the plotting */
375
while (fgets(buf, sizeof(buf), cmd_fp)) {
379
buf_len = strlen(buf) - 1;
380
for (; buf[buf_len] == '\r' || buf[buf_len] == '\n'; buf_len--) ;
381
buf[buf_len + 1] = 0;
383
if (buf[0] == '.' && buf[1] != '.') {
387
G_squeeze(buf); /* added 6/91 DBS @ CWU */
388
for (buf_ptr = buf + 2; *buf_ptr == ' '; buf_ptr++) ;
389
buf_len = strlen(buf_ptr);
391
switch (buf[1] & 0x7f) {
394
if ((ptr = strchr(buf_ptr, ':')))
403
fg_color = D_parse_color(buf_ptr, 1);
406
/* background color */
407
bg_color = D_parse_color(buf_ptr, 1);
413
if (strchr("+-", buf_ptr[0]))
416
if (buf_ptr[buf_len - 1] != 'p')
417
#ifdef BACKWARD_COMPATIBILITY
418
d *= (win.b - win.t) / 100.0 / linespacing;
420
d *= (win.b - win.t) / 100.0;
422
size = d + (i ? size : 0);
423
D_text_size(size, size);
427
bold = (atoi(buf_ptr) ? 1 : 0);
431
strncpy(align, buf_ptr, 2);
436
if (strchr("+-", buf_ptr[0]))
439
if (buf_ptr[buf_len - 1] != 'r')
441
d += (i ? rotation : 0.0);
442
rotation = fmod(d, 2.0 * M_PI);
444
rotation += 2.0 * M_PI;
445
D_text_rotation(rotation * 180.0 / M_PI);
449
linespacing = atof(buf_ptr);
456
if (strchr("+-", buf_ptr[0]))
459
if (buf_ptr[buf_len - 1] == '%')
461
d *= (win.r - win.l) / 100.0;
462
else if (buf_ptr[buf_len - 1] != 'p')
464
d = (d - 1) * size * linespacing + 0.5;
465
x = prev_x = d + (i ? x : orig_x);
472
if (strchr("+-", buf_ptr[0]))
475
if (buf_ptr[buf_len - 1] == '%')
477
d = win.b - d * (win.b - win.t) / 100.0;
478
else if (buf_ptr[buf_len - 1] != 'p')
480
d *= size * linespacing + 0.5;
481
y = prev_y = d + (i ? y : orig_y);
486
linefeed = (atoi(buf_ptr) ? 1 : 0);
490
if (strchr("+-", buf_ptr[0]))
493
if (buf_ptr[buf_len - 1] == '%')
494
d *= (win.r - win.l) / 100.0;
495
else if (buf_ptr[buf_len - 1] != 'p')
497
x = prev_x = orig_x = d + (i ? orig_x : win.l);
501
if (strchr("+-", buf_ptr[0]))
504
if (buf_ptr[buf_len - 1] == '%')
505
d *= (win.b - win.t) / 100.0;
506
else if (buf_ptr[buf_len - 1] != 'p')
508
y = prev_y = orig_y = d + (i ? orig_y : win.t);
514
if (buf[0] == '.' && buf[1] == '.')
517
if (!first_text && (linefeed || set_l)) {
518
/* if x is not given, increment x */
521
(size * linespacing + 0.5) * sin(rotation);
522
/* if y is not given, increment y */
525
(size * linespacing + 0.5) * cos(rotation);
529
set_x = set_y = set_l = first_text = 0;
531
draw_text(buf_ptr, &x, &y, size, align, rotation, bold, do_background, fg_color, bg_color);
540
D_text_rotation(0.0);
547
static void set_color(char *tcolor)
551
if (sscanf(tcolor, "%d:%d:%d", &r, &g, &b) == 3 ||
552
sscanf(tcolor, "0x%02x%02x%02x", &r, &g, &b) == 3) {
553
if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256) {
554
D_RGB_color(r, g, b);
558
color = D_translate_color(tcolor);
560
G_warning(_("[%s]: No such color. Use '%s'"), tcolor,
562
color = D_translate_color(DEFAULT_COLOR);
572
get_coordinates(double *x, double *y, double *east, double *north,
573
struct rectinfo win, char **at, char pixel,
584
e = D_d_to_u_col(*x);
585
n = D_d_to_u_row(*y);
588
*x = D_u_to_d_col(e);
589
*y = D_u_to_d_row(n);
592
*x = win.l + (win.r - win.l) * e / 100.0;
593
*y = win.t + (win.b - win.t) * (100.0 - n) / 100.0;
594
e = D_d_to_u_col(*x);
595
n = D_d_to_u_row(*y);
609
static void draw_text(char *text, double *x, double *y, double size, char *align,
610
double rotation, char bold, int do_background, int fg_color, int bg_color)
617
/* TODO: get text dimension */
618
/* R_get_text_box() does not work with rotation and returns a little bit
619
* bigger dimension than actual text size */
621
D_text_rotation(0.0);
623
D_get_text_box(text, &t, &b, &l, &r);
626
D_text_rotation(rotation * 180.0 / M_PI);
632
/* D_text() does not draw " ". */
637
/* D_text() does not draw " ". */
643
if (strcmp(align, "ll") != 0) {
672
pl = *x - size/2; /* some pixels margin for both sides */
674
pr = *x + w + size/2;
675
pb = *y - h - size/2;
676
D_use_color(bg_color);
677
D_box_abs(pl, pt, pr, pb); /* draw the box */
678
D_use_color(fg_color); /* restore */
685
D_pos_abs(*x, *y + 1);
687
D_pos_abs(*x + 1, *y);