Skip to content

Commit 7900c66

Browse files
authored
Allow composite character with two characters from different font/charsets (#4112)
* Allow composite char from different font/charsets See #4108 for context. This PR implements the option of having the 2nd character be surrounded by a font-switching sequence. * Update postscriptlight.c * Update text_notes.rst_ * Add test and update docs * Update postscriptlight.c
1 parent 6ee2397 commit 7900c66

File tree

4 files changed

+142
-10
lines changed

4 files changed

+142
-10
lines changed

doc/rst/source/text_notes.rst_

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ This only applies to text inside a script or that otherwise is processed
99
by DOS. Data files that are opened and read by the module do not need
1010
such duplication.
1111

12+
Composite Characters
13+
--------------------
14+
15+
If the two characters that are to be combined come from different
16+
fonts (say, Symbol and Times), then it is allowed for the *second*
17+
character (but not the first) to be surrounded by escape codes to
18+
temporarily change the font for that character. For instance, the
19+
sequence "@!\\227@~\\145@~" will print the epsilon character (octal code
20+
\\145 from the Symbol font) with time-derivative indicated by the
21+
over-printed dot (octal code \\227 from the current font, assuming the
22+
ISOLatin1 character set).
23+
1224
Limitations
1325
-----------
1426

src/postscriptlight.c

Lines changed: 119 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ static struct PSL_FONT PSL_standard_fonts[PSL_N_STANDARD_FONTS] = {
333333
#define PSL_SUB_DOWN 0.25 /* Baseline shift down in font size for subscript */
334334
#define PSL_SUP_UP_LC 0.35 /* Baseline shift up in font size for superscript after lowercase letter */
335335
#define PSL_SUP_UP_UC 0.35 /* Baseline shift up in font size for superscript after uppercase letter */
336+
#define PSL_ASCII_ES 27 /* ASCII code for escape (used to prevent +? strings in plain text from being seen as modifiers) */
336337
#if 0
337338
/* These are potential revisions to some of the settings above but remains to be tested */
338339
#define PSL_SUBSUP_SIZE 0.58 /* Relative size of sub/sup-script to normal size */
@@ -4852,6 +4853,45 @@ int PSL_plottextbox (struct PSL_CTRL *PSL, double x, double y, double fontsize,
48524853
return (PSL_NO_ERROR);
48534854
}
48544855

4856+
void psl_got_composite_fontswitch (struct PSL_CTRL *PSL, char *text) {
4857+
/* If a composite character is made from two different fonts then we need to flag these.
4858+
* E.g., Epsilon time-derivative = @!\277@~145@~ using current and Symbol font.
4859+
* Here we need to switch to symbol font for one char, from whatever font we are using.
4860+
* We look for such cases and count the occurrences, plus replace the font changing code
4861+
* @ (either @~ or @%font% with ASCII escape (27)). */
4862+
size_t k;
4863+
int n = 0, step;
4864+
for (k = 0; k < strlen (text); k++) {
4865+
if (text[k] != '@') continue;
4866+
/* Start of an escape sequence */
4867+
k++;
4868+
if (text[k] != '!') continue; /* Not a composite character request */
4869+
k++; /* Step to start of character1 */
4870+
if (text[k] == '\\') k += 4; else k++; /* Skip the octal or regular first character */
4871+
if (text[k] != '@') continue; /* No font switching in the composite glyph */
4872+
/* Here we do have such a thing, and we need to avoid the regular string splitting at @ in PSL_plottext and PSL_deftextdim */
4873+
text[k] = PSL_ASCII_ES; /* Replace @ with ASCII ESC code for now */
4874+
k++; /* Font code type is ~ or % */
4875+
if (text[k] == '~') { /* Symbol font */
4876+
k++; /* Step to character2 */
4877+
step = 1; /* Since we will toggle back with @~ */
4878+
}
4879+
else { /* Some random font switch */
4880+
k++; /* Step past first % */
4881+
while (text[k] != '%') k++; /* Skip past the font name or number */
4882+
step = 2; /* Since we will toggle back with @%% */
4883+
k++; /* Step to character2 */
4884+
}
4885+
if (text[k] == '\\') k += 4; else k++; /* Skip the octal or regular second character */
4886+
if (text[k] != '@') /* Not ideal, user error presumably */
4887+
PSL_message (PSL, PSL_MSG_WARNING, "Warning: psl_got_composite_fontswitch expected a font-change at end of composite character 2\n");
4888+
else /* Get passed the font return code */
4889+
text[k] = PSL_ASCII_ES; /* Skip to end of text section */
4890+
n++; /* Found one of these cases */
4891+
}
4892+
if (n) PSL_message (PSL, PSL_MSG_DEBUG, "psl_got_composite_fontswitch found %d composite characters with different fonts/char sets\n", n);
4893+
}
4894+
48554895
int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char *text) {
48564896
/* Will calculate the dimension of the given text string.
48574897
* Because of possible escape sequences we need to examine the string
@@ -4866,9 +4906,9 @@ int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char
48664906
* depth or both width and height on the PostScript stack.
48674907
*/
48684908

4869-
char *tempstring = NULL, *piece = NULL, *piece2 = NULL, *ptr = NULL, *string = NULL, *plast = NULL, previous[BUFSIZ] = {""};
4870-
int dy, font, sub_on, super_on, scaps_on, symbol_on, font_on, size_on, color_on, under_on, old_font, last_chr, kase = PSL_LC;
4871-
bool last_sub = false, last_sup = false, supersub;
4909+
char *tempstring = NULL, *piece = NULL, *piece2 = NULL, *ptr = NULL, *string = NULL, *plast = NULL, previous[BUFSIZ] = {""}, c;
4910+
int dy, font, font2, sub_on, super_on, scaps_on, symbol_on, font_on, size_on, color_on, under_on, old_font, last_chr, kase = PSL_LC;
4911+
bool last_sub = false, last_sup = false, supersub, composite;
48724912
double orig_size, small_size, size, scap_size, ustep[2], dstep;
48734913

48744914
if (strlen (text) >= (PSL_BUFSIZ-1)) {
@@ -4891,6 +4931,8 @@ int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char
48914931
return (PSL_NO_ERROR);
48924932
}
48934933

4934+
psl_got_composite_fontswitch (PSL, string);
4935+
48944936
/* Here, we have special request for Symbol font and sub/superscript
48954937
* @~ toggles between Symbol font and default font
48964938
* @%<fontno>% switches font number <fontno>; give @%% to reset
@@ -4904,14 +4946,14 @@ int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char
49044946
piece = PSL_memory (PSL, NULL, 2 * PSL_BUFSIZ, char);
49054947
piece2 = PSL_memory (PSL, NULL, PSL_BUFSIZ, char);
49064948

4907-
font = old_font = PSL->current.font_no;
4949+
font = font2 = old_font = PSL->current.font_no;
49084950
orig_size = size = fontsize;
49094951
small_size = size * PSL->current.subsupsize; /* Sub-script/Super-script set at given fraction of font size */
49104952
scap_size = size * PSL->current.scapssize; /* Small caps set at given fraction of font size */
49114953
ustep[PSL_LC] = PSL->current.sup_up[PSL_LC] * size; /* Super-script baseline raised by given fraction of font size for lower case*/
49124954
ustep[PSL_UC] = PSL->current.sup_up[PSL_UC] * size; /* Super-script baseline raised by given fraction of font size for upper case */
49134955
dstep = PSL->current.sub_down * size; /* Sub-script baseline lowered by given fraction of font size */
4914-
sub_on = super_on = scaps_on = symbol_on = font_on = size_on = color_on = under_on = false;
4956+
sub_on = super_on = scaps_on = symbol_on = font_on = size_on = color_on = under_on = composite = false;
49154957
supersub = (strstr (string, "@-@+") || strstr (string, "@+@-")); /* Check for sub/super combo */
49164958
tempstring = PSL_memory (PSL, NULL, strlen(string)+1, char); /* Since strtok steps on it */
49174959
strcpy (tempstring, string);
@@ -4925,13 +4967,51 @@ int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char
49254967
}
49264968

49274969
while (ptr) {
4928-
if (ptr[0] == '!') { /* Composite character */
4970+
if (ptr[0] == '!') { /* Composite character. Only use the second character to measure width */
49294971
ptr++;
49304972
if (ptr[0] == '\\') /* Octal code */
49314973
ptr += 4;
49324974
else
49334975
ptr++;
4934-
strncpy (piece, ptr, 2 * PSL_BUFSIZ);
4976+
/* Watch out for escaped font change before 2nd character */
4977+
if (ptr[0] == PSL_ASCII_ES) { /* Have a font change on either side of 2nd character */
4978+
ptr++;
4979+
if (ptr[0] == '~') /* Toggle the symbol font */
4980+
font2 = PSL_SYMBOL_FONT;
4981+
else { /* Font switching with @%font% ...@%% */
4982+
ptr++;
4983+
font2 = psl_getfont (PSL, ptr);
4984+
while (*ptr != '%') ptr++;
4985+
}
4986+
ptr++; /* Now at start of 2nd character */
4987+
}
4988+
else /* No 2nd font */
4989+
font2 = font;
4990+
if (ptr[0] == '\\') { /* Octal code */
4991+
c = ptr[4];
4992+
ptr[4] = '\0'; /* Temporary chop at end of this code */
4993+
}
4994+
else {
4995+
c = ptr[1];
4996+
ptr[1] = '\0'; /* Temporary chop at end of char */
4997+
}
4998+
strncpy (piece, ptr, 2 * PSL_BUFSIZ); /* Picked character2 */
4999+
if (ptr[0] == '\\') { /* Octal code */
5000+
ptr[4] = c; /* Restore code */
5001+
ptr += 4;
5002+
}
5003+
else {
5004+
ptr[1] = c; /* Restore char */
5005+
ptr++;
5006+
}
5007+
if (font2 != font) { /* Skip past the font switcher */
5008+
ptr++; /* Step over the implicit @ (ASCII 27) */
5009+
if (font2 == PSL_SYMBOL_FONT)
5010+
ptr++; /* Move past the ~ */
5011+
else
5012+
ptr += 2; /* Move past the %% */
5013+
}
5014+
composite = true; /* Flag this case */
49355015
}
49365016
else if (ptr[0] == '~') { /* Symbol font toggle */
49375017
symbol_on = !symbol_on;
@@ -5028,6 +5108,10 @@ int PSL_deftextdim (struct PSL_CTRL *PSL, const char *dim, double fontsize, char
50285108
PSL_command (PSL, "PSL_last_width 0 G "); /* Rewind position to orig baseline */
50295109
last_sub = last_sup = false;
50305110
}
5111+
if (ptr && composite) {
5112+
strcat (piece, ptr);
5113+
composite = false;
5114+
}
50315115
PSL_command (PSL, "%d F%d (%s) FP ", psl_ip (PSL, size), font, piece);
50325116
last_chr = ptr[strlen(piece)-1];
50335117
if (!super_on && (last_chr > 0 && last_chr < 255)) kase = (islower (last_chr)) ? PSL_LC : PSL_UC;
@@ -5103,7 +5187,7 @@ int PSL_plottext (struct PSL_CTRL *PSL, double x, double y, double fontsize, cha
51035187
const char *justcmd[12] = {"", "bl ", "bc ", "br ", "", "ml ", "mc ", "mr ", "", "tl ", "tc ", "tr "};
51045188
/* PS strings to be used dependent on "justify%4". Empty string added for unused value. */
51055189
const char *align[4] = {"0", "-2 div", "neg", ""};
5106-
int dy, i = 0, j, font, x_just, y_just, upen, ugap;
5190+
int dy, i = 0, j, font, font2, x_just, y_just, upen, ugap;
51075191
int sub_on, super_on, scaps_on, symbol_on, font_on, size_on, color_on, under_on, old_font, n_uline, start_uline, stop_uline, last_chr, kase = PSL_LC;
51085192
bool last_sub = false, last_sup = false, supersub;
51095193
double orig_size, small_size, size, scap_size, ustep[2], dstep, last_rgb[4] = {0.0, 0.0, 0.0, 0.0};
@@ -5148,6 +5232,8 @@ int PSL_plottext (struct PSL_CTRL *PSL, double x, double y, double fontsize, cha
51485232
return (PSL_NO_ERROR);
51495233
}
51505234

5235+
psl_got_composite_fontswitch (PSL, string);
5236+
51515237
/* For more difficult cases we use the PSL_deftextdim machinery to get the size of the font box */
51525238

51535239
if (justify > 1) {
@@ -5189,7 +5275,6 @@ int PSL_plottext (struct PSL_CTRL *PSL, double x, double y, double fontsize, cha
51895275
last_chr = ptr[strlen(ptr)-1];
51905276
ptr = strtok_r (NULL, "@", &plast);
51915277
kase = ((last_chr > 0 && last_chr < 255) && islower (last_chr)) ? PSL_LC : PSL_UC;
5192-
51935278
}
51945279

51955280
font = old_font = PSL->current.font_no;
@@ -5216,6 +5301,21 @@ int PSL_plottext (struct PSL_CTRL *PSL, double x, double y, double fontsize, cha
52165301
piece[0] = ptr[0]; piece[1] = 0;
52175302
ptr++;
52185303
}
5304+
/* Watch out for escaped font change before 2nd character */
5305+
if (ptr[0] == PSL_ASCII_ES) { /* Have a font change on either side of 2nd character */
5306+
ptr++;
5307+
if (ptr[0] == '~') /* Toggle the symbol font */
5308+
font2 = PSL_SYMBOL_FONT;
5309+
else { /* Font switching with @%font% ...@%% */
5310+
ptr++;
5311+
font2 = psl_getfont (PSL, ptr);
5312+
psl_encodefont (PSL, font);
5313+
while (*ptr != '%') ptr++;
5314+
}
5315+
ptr++; /* Now at start of 2nd character */
5316+
}
5317+
else
5318+
font2 = font;
52195319
if (ptr[0] == '\\') { /* Octal code again */
52205320
strncpy (piece2, ptr, 4U);
52215321
piece2[4] = 0;
@@ -5225,8 +5325,17 @@ int PSL_plottext (struct PSL_CTRL *PSL, double x, double y, double fontsize, cha
52255325
piece2[0] = ptr[0]; piece2[1] = 0;
52265326
ptr++;
52275327
}
5328+
if (font2 != font) { /* Skip past the font switcher */
5329+
ptr++; /* Step over the implicit @ (ascii 27) */
5330+
if (font2 == PSL_SYMBOL_FONT)
5331+
ptr++; /* Move past the ~ */
5332+
else
5333+
ptr += 2; /* Move past the %% */
5334+
}
52285335
/* Try to center justify these two character to make a composite character - may not be right */
5229-
PSL_command (PSL, "%d F%d (%s) E exch %s -2 div dup 0 G\n", psl_ip (PSL, size), font, piece2, op[mode]);
5336+
PSL_command (PSL, "%d F%d (%s) E exch %s -2 div dup 0 G\n", psl_ip (PSL, size), font2, piece2, op[mode]);
5337+
if (font2 != font) /* Must switch font in the call */
5338+
PSL_command (PSL, "%d F%d\n", psl_ip (PSL, size), font);
52305339
PSL_command (PSL, "(%s) E -2 div dup 0 G exch %s sub neg dup 0 lt {pop 0} if 0 G\n", piece, op[mode]);
52315340
strncpy (piece, ptr, 2 * PSL_BUFSIZ);
52325341
}

test/pstext/composite.ps

21.9 KB
Binary file not shown.

test/pstext/composite.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
# Test gmt pstext with composite characters.
3+
ps=composite.ps
4+
5+
cat << EOF > tmp
6+
8 2 Writing @!\227\145 in Times-Roman
7+
8 6 Writing @!\227@~\145@~ in Symbol
8+
8 10 Writing @!\250@%Times-Italic%s@%% in Times-Italic
9+
8 14 University of Hawaii at M@!a\225noa
10+
EOF
11+
gmt pstext tmp -R0/16/0/16 -Jx1c -B0 -P -F+f32p,Times-Roman+jCM > $ps

0 commit comments

Comments
 (0)