Skip to content

Commit 2194754

Browse files
authored
Support @~ (Symbol font toggle) in -Sq quoted line labels (#8952)
The @~ escape sequence for switching to the Symbol font (Greek letters etc.) was ignored in -Sq quoted line labels. The label text was rendered via PostScript's show operator in PSL_label.ps, which doesn't understand GMT's @~ escape sequences. Add PostScript procedures (PSL_split_esc, PSL_has_esc, PSL_show_esc, PSL_charpath_esc, PSL_sw_esc) that split label strings on @~ boundaries and toggle between the current font and Symbol font for each segment. Wrapper functions (PSL_label_show, PSL_label_charpath, PSL_label_sw) dispatch to the escape-aware versions only when @~ is present, so labels without escapes follow the exact same code path as before. For curved text (+v), modify setchar to detect @~ at the current position and toggle fonts without rendering, allowing the next real character to be placed in the correct font along the path. Fixes both straight baseline labels (PSL_ST_place_label*) and curved baseline labels (PSL_pathtext/setchar), as well as width calculations (PSL_ST_prepare_text, PSL_CT_calcstringwidth). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
1 parent 8ce1960 commit 2194754

2 files changed

Lines changed: 145 additions & 25 deletions

File tree

src/PSL_label.ps

3.54 KB
Binary file not shown.

src/PSL_strings.h

Lines changed: 145 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -166,32 +166,47 @@ static char *PSL_label_str =
166166
" % same point.\n"
167167
"\n"
168168
"/setchar % ``setchar'' sets the next\n"
169-
" { /char str charcount 1 getinterval def % character in the string along\n"
169+
" % character in the string along\n"
170170
" % the path and then updates the\n"
171171
" % amount of path we have\n"
172172
" % exhausted.\n"
173-
" /charcount charcount 1 add def % Increment the character count.\n"
174-
" /charwidth char stringwidth pop def % Find the width of the character.\n"
175-
" V cpx cpy itransform T % Translate to the current\n"
173+
" { charcount 1 add str length lt % Check if at least 2 chars remain\n"
174+
" {str charcount get 64 eq str charcount 1 add get 126 eq and} {false} ifelse\n"
175+
" { % Found @~ escape: skip it, toggle font, no rendering\n"
176+
" /charcount charcount 2 add def\n"
177+
" /psl_ct_sym psl_ct_sym not def\n"
178+
" psl_ct_sym {\n"
179+
" /psl_ct_origfont currentfont def\n"
180+
" /Symbol findfont PSL_get_fontsize scalefont setfont\n"
181+
" } {\n"
182+
" psl_ct_origfont setfont\n"
183+
" } ifelse\n"
184+
" }\n"
185+
" { % Normal character rendering\n"
186+
" /char str charcount 1 getinterval def\n"
187+
" /charcount charcount 1 add def\n"
188+
" /charwidth char stringwidth pop def\n"
189+
" V cpx cpy itransform T % Translate to the current\n"
176190
" % position in user space.\n"
177-
" dy dx atan R % Rotate the x-axis to coincide\n"
191+
" dy dx atan R % Rotate the x-axis to coincide\n"
178192
" % with the current segment.\n"
179-
" 0 justy M\n"
180-
" PSL_font_F { % Show text normally, if requested\n"
181-
" char show}\n"
182-
" if\n"
183-
" PSL_font_FO { % Fill first then outline, if requested\n"
184-
" currentpoint N M V char true charpath fs U V char false charpath S U char E 0 G\n"
185-
" } if\n"
186-
" PSL_font_OF { % Outline text then fill, if requested\n"
187-
" currentpoint N M V char false charpath S U V char true charpath fs U char E 0 G\n"
188-
" } if\n"
189-
" 0 justy neg G currentpoint transform\n"
190-
" /cpy exch def /cpx exch def % Update the current position\n"
191-
" % before we restore ourselves to\n"
193+
" 0 justy M\n"
194+
" PSL_font_F { % Show text normally, if requested\n"
195+
" char show}\n"
196+
" if\n"
197+
" PSL_font_FO { % Fill first then outline, if requested\n"
198+
" currentpoint N M V char true charpath fs U V char false charpath S U char E 0 G\n"
199+
" } if\n"
200+
" PSL_font_OF { % Outline text then fill, if requested\n"
201+
" currentpoint N M V char false charpath S U V char true charpath fs U char E 0 G\n"
202+
" } if\n"
203+
" 0 justy neg G currentpoint transform\n"
204+
" /cpy exch def /cpx exch def % Update the current position\n"
205+
" % before we restore ourselves to\n"
192206
" % the untransformed state.\n"
193-
" U /setdist setdist charwidth add def % Increment the distance we have\n"
194-
" } def % covered by setting characters.\n"
207+
" U /setdist setdist charwidth add def % Increment the distance we have\n"
208+
" } ifelse % covered by setting characters.\n"
209+
" } def\n"
195210
"end\n"
196211
"\n"
197212
"% PSL LABEL CLIP FUNCTIONS\n"
@@ -336,7 +351,7 @@ static char *PSL_label_str =
336351
" 0 1 PSL_m1\n"
337352
" { /i exch def\n"
338353
" PSL_fnt_tmp i get cvx exec % Get and set this label's font attributes\n"
339-
" PSL_width_tmp i PSL_str_tmp i get stringwidth pop put % Compute width and store in the array\n"
354+
" PSL_width_tmp i PSL_str_tmp i get PSL_label_sw put % Compute width and store in the array\n"
340355
" } for\n"
341356
"} def\n"
342357
"\n"
@@ -475,6 +490,7 @@ static char *PSL_label_str =
475490
" PSL_drawbox % Want to draw outline of box\n"
476491
" {V PSL_setboxpen S U} if N\n"
477492
" } if\n"
493+
" /psl_ct_sym false def\n"
478494
" PSL_CT_placeline psl_label PSL_gap_x PSL_just PSL_height psl_depth PSL_pathtext\n"
479495
"} def\n"
480496
"\n"
@@ -629,6 +645,110 @@ static char *PSL_label_str =
629645
" PSL_eoclip N % Set the new clip path, increment clip counter and clear path\n"
630646
"} def\n"
631647
"\n"
648+
"% Escape-aware label rendering procedures.\n"
649+
"% These handle the @~ escape sequence (Symbol font toggle) in label strings.\n"
650+
"% When @~ is found, the string is split into segments that alternate between\n"
651+
"% the current font and the Symbol font at the same size.\n"
652+
"\n"
653+
"/PSL_split_esc { % (string) PSL_split_esc => array of substrings split on @~\n"
654+
" /psl_src exch def\n"
655+
" /psl_parts 20 array def\n"
656+
" /psl_np 0 def\n"
657+
" /psl_pos 0 def\n"
658+
" {\n"
659+
" /psl_found false def\n"
660+
" psl_pos 1 psl_src length 2 sub {\n"
661+
" /psl_i exch def\n"
662+
" psl_src psl_i get 64 eq % '@' = ASCII 64\n"
663+
" psl_src psl_i 1 add get 126 eq and % '~' = ASCII 126\n"
664+
" {\n"
665+
" psl_parts psl_np psl_src psl_pos psl_i psl_pos sub getinterval put\n"
666+
" /psl_np psl_np 1 add def\n"
667+
" /psl_pos psl_i 2 add def\n"
668+
" /psl_found true def\n"
669+
" exit\n"
670+
" } if\n"
671+
" } for\n"
672+
" psl_found not {exit} if\n"
673+
" } loop\n"
674+
" psl_pos psl_src length lt {\n"
675+
" psl_parts psl_np psl_src psl_pos psl_src length psl_pos sub getinterval put\n"
676+
" /psl_np psl_np 1 add def\n"
677+
" } if\n"
678+
" psl_parts 0 psl_np getinterval\n"
679+
"} def\n"
680+
"\n"
681+
"/PSL_has_esc { % (string) PSL_has_esc => bool -- true if string contains @~\n"
682+
" /psl_ts exch def\n"
683+
" /psl_he false def\n"
684+
" 0 1 psl_ts length 2 sub {\n"
685+
" /psl_ti exch def\n"
686+
" psl_ts psl_ti get 64 eq psl_ts psl_ti 1 add get 126 eq and\n"
687+
" {/psl_he true def exit} if\n"
688+
" } for\n"
689+
" psl_he\n"
690+
"} def\n"
691+
"\n"
692+
"/PSL_get_fontsize { % => fontsize -- extract size from current font\n"
693+
" currentfont /FontMatrix get 0 get 1000 mul\n"
694+
"} def\n"
695+
"\n"
696+
"/PSL_show_esc { % (string) PSL_show_esc => -- -- show with @~ font toggling\n"
697+
" PSL_split_esc\n"
698+
" /psl_origfont currentfont def\n"
699+
" /psl_sym false def\n"
700+
" /psl_fsz PSL_get_fontsize def\n"
701+
" { /psl_seg exch def\n"
702+
" psl_seg length 0 gt {psl_seg show} if\n"
703+
" /psl_sym psl_sym not def\n"
704+
" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n"
705+
" {psl_origfont setfont} ifelse\n"
706+
" } forall\n"
707+
" psl_origfont setfont\n"
708+
"} def\n"
709+
"\n"
710+
"/PSL_charpath_esc { % (string) PSL_charpath_esc => -- -- false charpath with @~ toggling\n"
711+
" PSL_split_esc\n"
712+
" /psl_origfont currentfont def\n"
713+
" /psl_sym false def\n"
714+
" /psl_fsz PSL_get_fontsize def\n"
715+
" { /psl_seg exch def\n"
716+
" psl_seg length 0 gt {psl_seg false charpath} if\n"
717+
" /psl_sym psl_sym not def\n"
718+
" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n"
719+
" {psl_origfont setfont} ifelse\n"
720+
" } forall\n"
721+
" psl_origfont setfont\n"
722+
"} def\n"
723+
"\n"
724+
"/PSL_sw_esc { % (string) PSL_sw_esc => width -- stringwidth with @~ toggling\n"
725+
" PSL_split_esc\n"
726+
" /psl_origfont currentfont def\n"
727+
" /psl_sym false def\n"
728+
" /psl_fsz PSL_get_fontsize def\n"
729+
" /psl_tw 0 def\n"
730+
" { /psl_seg exch def\n"
731+
" psl_seg length 0 gt {/psl_tw psl_tw psl_seg stringwidth pop add def} if\n"
732+
" /psl_sym psl_sym not def\n"
733+
" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n"
734+
" {psl_origfont setfont} ifelse\n"
735+
" } forall\n"
736+
" psl_origfont setfont\n"
737+
" psl_tw\n"
738+
"} def\n"
739+
"\n"
740+
"/PSL_label_show { % (string) PSL_label_show => --\n"
741+
" dup PSL_has_esc {PSL_show_esc} {show} ifelse\n"
742+
"} def\n"
743+
"\n"
744+
"/PSL_label_charpath { % (string) PSL_label_charpath => --\n"
745+
" dup PSL_has_esc {PSL_charpath_esc} {false charpath} ifelse\n"
746+
"} def\n"
747+
"\n"
748+
"/PSL_label_sw { % (string) PSL_label_sw => width\n"
749+
" dup PSL_has_esc {PSL_sw_esc} {stringwidth pop} ifelse\n"
750+
"} def\n"
751+
"\n"
632752
"/PSL_ST_prepare_text % Compute various dimensions and coordinates for one label\n"
633753
"{ % The current label has index psl_k\n"
634754
" /psl_xp PSL_txt_x psl_k get def % Get text placement x coordinate\n"
@@ -640,7 +760,7 @@ static char *PSL_label_str =
640760
" /PSL_just PSL_label_justify psl_k get def % Get text justification (1-11)\n"
641761
" /PSL_justx PSL_just 4 mod 1 sub 2 div neg def % This is 0, -0.5, or -1 for relative x-shift \n"
642762
" /PSL_justy PSL_just 4 idiv 2 div neg def % This is 0, -0.5, or -1 for relative y-shift \n"
643-
" /psl_SW psl_label stringwidth pop def % Width of current label space\n"
763+
" /psl_SW psl_label PSL_label_sw def % Width of current label space\n"
644764
" /psl_boxW psl_SW PSL_gap_x 2 mul add def % Width of current label space including clearance\n"
645765
" /psl_x0 psl_SW PSL_justx mul def % (psl_x0,psl_y0) is rotated/adjusted text LL point on inside rectangle relative to psl_xp,psl_yp\n"
646766
" /psl_y0 PSL_justy PSL_height mul def %\n"
@@ -680,23 +800,23 @@ static char *PSL_label_str =
680800
"{\n"
681801
" V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n"
682802
" psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n"
683-
" psl_label dup sd neg 0 exch G show % Place the text, adjust vertically for any depth below baseline\n"
803+
" psl_label dup sd neg 0 exch G PSL_label_show % Place the text, adjust vertically for any depth below baseline\n"
684804
" U % Undo damage to coordinate system\n"
685805
"} def\n"
686806
"\n"
687807
"/PSL_ST_place_label_FO % Just place the current label by filling it, then outlining it\n"
688808
"{\n"
689809
" V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n"
690810
" psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n"
691-
" psl_label dup sd neg 0 exch G false charpath V fs U S N % Place the text, adjust vertically for any depth below baseline\n"
811+
" psl_label dup sd neg 0 exch G PSL_label_charpath V fs U S N % Place the text, adjust vertically for any depth below baseline\n"
692812
" U % Undo damage to coordinate system\n"
693813
"} def\n"
694814
"\n"
695815
"/PSL_ST_place_label_OF % Just place the current label by drawing outline, then filling it\n"
696816
"{\n"
697817
" V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n"
698818
" psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n"
699-
" psl_label dup sd neg 0 exch G false charpath V S U fs N % Place the text, adjust vertically for any depth below baseline\n"
819+
" psl_label dup sd neg 0 exch G PSL_label_charpath V S U fs N % Place the text, adjust vertically for any depth below baseline\n"
700820
" U % Undo damage to coordinate system\n"
701821
"} def\n"
702822
"\n"

0 commit comments

Comments
 (0)