From ad2fb6937b965bd564a098589b72b8659b14c0cf Mon Sep 17 00:00:00 2001 From: Joaquim Date: Tue, 24 Mar 2026 12:38:08 +0000 Subject: [PATCH] Support @~ (Symbol font toggle) in -Sq quoted line labels 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 " --- src/PSL_label.ps | Bin 32666 -> 36293 bytes src/PSL_strings.h | 170 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 145 insertions(+), 25 deletions(-) diff --git a/src/PSL_label.ps b/src/PSL_label.ps index 0fd9c07f91f13a11804bd5a6ac612250a7039383..37bec39f3f26466803515c27d6e339a5518eb8b3 100644 GIT binary patch delta 3916 zcmcIn&x;&I6eflkc2xAB>}FT+8Cf?0lbu~Of*~Oi4J4Qlh@g1L(A!fp-KM9z(^Zq% z-C@#yMBnu8O$hA8gL=}NAd-L=@BR&fpx=8{{cCr2ArkknJzZ7r)qC&z-dELM{d4T+ zub+GL-r|V`{wwY-PV*`GftUHoI5xCFUcXP;WE81*Xab7N!on(D4U~V6!U1^(6P`A- zDMp$yHS)r^L-?DrS?y}53|)Aa)O~SRkJ~MHc4PAbrgQS*{*0yrFVbp;O%$HiA$Goo zu=;f6At~*9srrEQy)dQFyhA1#4kJZ_B*sgeAdIha3d?;|wtv`CZ#$;hmk)Rua)5jQcWq`_3ml zuA zN|CpvqMn}Yee?d4rB};}GgiVDEZ%!?`>*AEl|B}Fobx7Lrbx`{R4L68U-ieC(&sy? z^l6~9q5!M|eN3Qgc9B;1$13&}ohdP6U@g z8RFmUIvp62XPQep5!0&0Is{zYm@gPO2;In;LF5i$A_FaDY3<4yU7`=~T)loBVuZ|`MZ;jf9&ydt@?mWWp>n3}cC^(H2|@O} ze;EfSZ{y+r{KknOHV{E&573h7*f^iA+|C0YHlmhHm+f36eUTDc$)h(aEP#6qLq2!0RK89>4stHkF(F`r7xJyPu;~< zZ`qabu(;uwKBi;5hu3EgVO55E8%7YYTnW1o*7e$TM}H)0&knTwKcRLAXqr1}UPIY&05u8w=ppGS zxvord87MpJD5P5KdPYF4r#b87q`nh8O_NzAomh>u59v9ZnIx7bX?eMy4`}ZC@Yx~y zbb#guI@YzPnY856U3yst58UG#bJC0O&>yFwW7Xj&8tHTu9}uN3shq#7nvHLrRXu#j zX4U+W8mF35re0H$1SY?9#&e>icp5=s3D53G8X$spp$u@XYSoT-u|q3 k9B;qyGS{n7*W$NXzW3|y+ObYOvG@1%`_}u9vpz>% delta 275 zcmX>)n`zd6#to+8%nAzClh;YAO|}xRo_teMftQn0RlzeaxhOR^HLql{9EbE|Mkys# zkf>W`UW!6VMyf)2W=cthLVg;Ulbn%Ql$czSTBOG{SxqW#a+_2Hmw`f7X>m!Vf-l$P zH&XT@ASDXL8TsY4Tnb>2nZ`BQRoZ2;zKq1=#nN`HwF)4SSJKWvkuWH)PsVWaPXQ}N zuF3yo0wxE@dH}^!Qqv}1W>?>QL3X_~e{o4sW?nkPEd}`no4YNqNNg?-`YN@#yE2p+ hL;6ik1^4E>j@688X^A<-sS2BwyY|4@FT3wB0sxBcU^xH) diff --git a/src/PSL_strings.h b/src/PSL_strings.h index 2eaf4bd78f4..5fc337150ae 100644 --- a/src/PSL_strings.h +++ b/src/PSL_strings.h @@ -166,32 +166,47 @@ static char *PSL_label_str = " % same point.\n" "\n" "/setchar % ``setchar'' sets the next\n" -" { /char str charcount 1 getinterval def % character in the string along\n" +" % character in the string along\n" " % the path and then updates the\n" " % amount of path we have\n" " % exhausted.\n" -" /charcount charcount 1 add def % Increment the character count.\n" -" /charwidth char stringwidth pop def % Find the width of the character.\n" -" V cpx cpy itransform T % Translate to the current\n" +" { charcount 1 add str length lt % Check if at least 2 chars remain\n" +" {str charcount get 64 eq str charcount 1 add get 126 eq and} {false} ifelse\n" +" { % Found @~ escape: skip it, toggle font, no rendering\n" +" /charcount charcount 2 add def\n" +" /psl_ct_sym psl_ct_sym not def\n" +" psl_ct_sym {\n" +" /psl_ct_origfont currentfont def\n" +" /Symbol findfont PSL_get_fontsize scalefont setfont\n" +" } {\n" +" psl_ct_origfont setfont\n" +" } ifelse\n" +" }\n" +" { % Normal character rendering\n" +" /char str charcount 1 getinterval def\n" +" /charcount charcount 1 add def\n" +" /charwidth char stringwidth pop def\n" +" V cpx cpy itransform T % Translate to the current\n" " % position in user space.\n" -" dy dx atan R % Rotate the x-axis to coincide\n" +" dy dx atan R % Rotate the x-axis to coincide\n" " % with the current segment.\n" -" 0 justy M\n" -" PSL_font_F { % Show text normally, if requested\n" -" char show}\n" -" if\n" -" PSL_font_FO { % Fill first then outline, if requested\n" -" currentpoint N M V char true charpath fs U V char false charpath S U char E 0 G\n" -" } if\n" -" PSL_font_OF { % Outline text then fill, if requested\n" -" currentpoint N M V char false charpath S U V char true charpath fs U char E 0 G\n" -" } if\n" -" 0 justy neg G currentpoint transform\n" -" /cpy exch def /cpx exch def % Update the current position\n" -" % before we restore ourselves to\n" +" 0 justy M\n" +" PSL_font_F { % Show text normally, if requested\n" +" char show}\n" +" if\n" +" PSL_font_FO { % Fill first then outline, if requested\n" +" currentpoint N M V char true charpath fs U V char false charpath S U char E 0 G\n" +" } if\n" +" PSL_font_OF { % Outline text then fill, if requested\n" +" currentpoint N M V char false charpath S U V char true charpath fs U char E 0 G\n" +" } if\n" +" 0 justy neg G currentpoint transform\n" +" /cpy exch def /cpx exch def % Update the current position\n" +" % before we restore ourselves to\n" " % the untransformed state.\n" -" U /setdist setdist charwidth add def % Increment the distance we have\n" -" } def % covered by setting characters.\n" +" U /setdist setdist charwidth add def % Increment the distance we have\n" +" } ifelse % covered by setting characters.\n" +" } def\n" "end\n" "\n" "% PSL LABEL CLIP FUNCTIONS\n" @@ -336,7 +351,7 @@ static char *PSL_label_str = " 0 1 PSL_m1\n" " { /i exch def\n" " PSL_fnt_tmp i get cvx exec % Get and set this label's font attributes\n" -" PSL_width_tmp i PSL_str_tmp i get stringwidth pop put % Compute width and store in the array\n" +" PSL_width_tmp i PSL_str_tmp i get PSL_label_sw put % Compute width and store in the array\n" " } for\n" "} def\n" "\n" @@ -475,6 +490,7 @@ static char *PSL_label_str = " PSL_drawbox % Want to draw outline of box\n" " {V PSL_setboxpen S U} if N\n" " } if\n" +" /psl_ct_sym false def\n" " PSL_CT_placeline psl_label PSL_gap_x PSL_just PSL_height psl_depth PSL_pathtext\n" "} def\n" "\n" @@ -629,6 +645,110 @@ static char *PSL_label_str = " PSL_eoclip N % Set the new clip path, increment clip counter and clear path\n" "} def\n" "\n" +"% Escape-aware label rendering procedures.\n" +"% These handle the @~ escape sequence (Symbol font toggle) in label strings.\n" +"% When @~ is found, the string is split into segments that alternate between\n" +"% the current font and the Symbol font at the same size.\n" +"\n" +"/PSL_split_esc { % (string) PSL_split_esc => array of substrings split on @~\n" +" /psl_src exch def\n" +" /psl_parts 20 array def\n" +" /psl_np 0 def\n" +" /psl_pos 0 def\n" +" {\n" +" /psl_found false def\n" +" psl_pos 1 psl_src length 2 sub {\n" +" /psl_i exch def\n" +" psl_src psl_i get 64 eq % '@' = ASCII 64\n" +" psl_src psl_i 1 add get 126 eq and % '~' = ASCII 126\n" +" {\n" +" psl_parts psl_np psl_src psl_pos psl_i psl_pos sub getinterval put\n" +" /psl_np psl_np 1 add def\n" +" /psl_pos psl_i 2 add def\n" +" /psl_found true def\n" +" exit\n" +" } if\n" +" } for\n" +" psl_found not {exit} if\n" +" } loop\n" +" psl_pos psl_src length lt {\n" +" psl_parts psl_np psl_src psl_pos psl_src length psl_pos sub getinterval put\n" +" /psl_np psl_np 1 add def\n" +" } if\n" +" psl_parts 0 psl_np getinterval\n" +"} def\n" +"\n" +"/PSL_has_esc { % (string) PSL_has_esc => bool -- true if string contains @~\n" +" /psl_ts exch def\n" +" /psl_he false def\n" +" 0 1 psl_ts length 2 sub {\n" +" /psl_ti exch def\n" +" psl_ts psl_ti get 64 eq psl_ts psl_ti 1 add get 126 eq and\n" +" {/psl_he true def exit} if\n" +" } for\n" +" psl_he\n" +"} def\n" +"\n" +"/PSL_get_fontsize { % => fontsize -- extract size from current font\n" +" currentfont /FontMatrix get 0 get 1000 mul\n" +"} def\n" +"\n" +"/PSL_show_esc { % (string) PSL_show_esc => -- -- show with @~ font toggling\n" +" PSL_split_esc\n" +" /psl_origfont currentfont def\n" +" /psl_sym false def\n" +" /psl_fsz PSL_get_fontsize def\n" +" { /psl_seg exch def\n" +" psl_seg length 0 gt {psl_seg show} if\n" +" /psl_sym psl_sym not def\n" +" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n" +" {psl_origfont setfont} ifelse\n" +" } forall\n" +" psl_origfont setfont\n" +"} def\n" +"\n" +"/PSL_charpath_esc { % (string) PSL_charpath_esc => -- -- false charpath with @~ toggling\n" +" PSL_split_esc\n" +" /psl_origfont currentfont def\n" +" /psl_sym false def\n" +" /psl_fsz PSL_get_fontsize def\n" +" { /psl_seg exch def\n" +" psl_seg length 0 gt {psl_seg false charpath} if\n" +" /psl_sym psl_sym not def\n" +" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n" +" {psl_origfont setfont} ifelse\n" +" } forall\n" +" psl_origfont setfont\n" +"} def\n" +"\n" +"/PSL_sw_esc { % (string) PSL_sw_esc => width -- stringwidth with @~ toggling\n" +" PSL_split_esc\n" +" /psl_origfont currentfont def\n" +" /psl_sym false def\n" +" /psl_fsz PSL_get_fontsize def\n" +" /psl_tw 0 def\n" +" { /psl_seg exch def\n" +" psl_seg length 0 gt {/psl_tw psl_tw psl_seg stringwidth pop add def} if\n" +" /psl_sym psl_sym not def\n" +" psl_sym {/Symbol findfont psl_fsz scalefont setfont}\n" +" {psl_origfont setfont} ifelse\n" +" } forall\n" +" psl_origfont setfont\n" +" psl_tw\n" +"} def\n" +"\n" +"/PSL_label_show { % (string) PSL_label_show => --\n" +" dup PSL_has_esc {PSL_show_esc} {show} ifelse\n" +"} def\n" +"\n" +"/PSL_label_charpath { % (string) PSL_label_charpath => --\n" +" dup PSL_has_esc {PSL_charpath_esc} {false charpath} ifelse\n" +"} def\n" +"\n" +"/PSL_label_sw { % (string) PSL_label_sw => width\n" +" dup PSL_has_esc {PSL_sw_esc} {stringwidth pop} ifelse\n" +"} def\n" +"\n" "/PSL_ST_prepare_text % Compute various dimensions and coordinates for one label\n" "{ % The current label has index psl_k\n" " /psl_xp PSL_txt_x psl_k get def % Get text placement x coordinate\n" @@ -640,7 +760,7 @@ static char *PSL_label_str = " /PSL_just PSL_label_justify psl_k get def % Get text justification (1-11)\n" " /PSL_justx PSL_just 4 mod 1 sub 2 div neg def % This is 0, -0.5, or -1 for relative x-shift \n" " /PSL_justy PSL_just 4 idiv 2 div neg def % This is 0, -0.5, or -1 for relative y-shift \n" -" /psl_SW psl_label stringwidth pop def % Width of current label space\n" +" /psl_SW psl_label PSL_label_sw def % Width of current label space\n" " /psl_boxW psl_SW PSL_gap_x 2 mul add def % Width of current label space including clearance\n" " /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" " /psl_y0 PSL_justy PSL_height mul def %\n" @@ -680,7 +800,7 @@ static char *PSL_label_str = "{\n" " V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n" " psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n" -" psl_label dup sd neg 0 exch G show % Place the text, adjust vertically for any depth below baseline\n" +" psl_label dup sd neg 0 exch G PSL_label_show % Place the text, adjust vertically for any depth below baseline\n" " U % Undo damage to coordinate system\n" "} def\n" "\n" @@ -688,7 +808,7 @@ static char *PSL_label_str = "{\n" " V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n" " psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n" -" 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" +" 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" " U % Undo damage to coordinate system\n" "} def\n" "\n" @@ -696,7 +816,7 @@ static char *PSL_label_str = "{\n" " V psl_xp psl_yp T psl_angle R % Set origin at text point and rotate the coordinate system to follow baseline text\n" " psl_SW PSL_justx mul psl_y0 M % Goto LL point on label\n" -" 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" +" 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" " U % Undo damage to coordinate system\n" "} def\n" "\n"