1

As an exercise in learning LaTeX3, I would like to replace the following 7 functions, copied from xepersian.sty which do the same job in xepersian package, with a single function.

208 \DeclareDocumentCommand \setlatintextfont { O{} m O{} }
209   {
210     \__xepersian_main_setlatintextfont:nn {#1,#3} {#2}
211     \ignorespaces
212   }
213 \cs_new:Nn \__xepersian_main_setlatintextfont:nn
214  {
215   \fontspec_set_family:Nnn \l__xepersian_latinfont_family_tl {Ligatures=TeX,#1} {#2}
216   \use:x { \exp_not:n { \DeclareRobustCommand \latinfont }
217    {
218     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
219     \exp_not:N \fontfamily { \l__xepersian_latinfont_family_tl }
220     \exp_not:N \selectfont
221    }
222   }
223   \str_if_eq_x:nnT {\familydefault} {\rmdefault}
224     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
225   \__xepersian_setlatintextfont_hook:nn {#1} {#2}
226   \normalfont
227  }
228 
229 \cs_set_eq:NN \__xepersian_setlatintextfont_hook:nn   \use_none:nn


313 \DeclareDocumentCommand \setpersiansansfont { O{} m O{} }
314   {
315     \__xepersian_main_setpersiansansfont:nn {#1,#3} {#2}
316     \ignorespaces
317   }
318 \cs_new:Nn \__xepersian_main_setpersiansansfont:nn
319  {
320   \fontspec_set_family:Nnn \l__xepersian_persiansffamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
321   \tl_set_eq:NN \persiansfdefault \l__xepersian_persiansffamily_family_tl
322   \use:x { \exp_not:n { \DeclareRobustCommand \persiansffamily }
323    {
324     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
325     \exp_not:N \fontfamily { \l__xepersian_persiansffamily_family_tl }
326     \exp_not:N \selectfont
327    }
328   }
329   \str_if_eq_x:nnT {\familydefault} {\persiansfdefault}
330     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
331   \__xepersian_setpersiansansfont_hook:nn {#1} {#2}
332   \normalfont
333  }
334 
335 \cs_set_eq:NN \__xepersian_setpersiansansfont_hook:nn   \use_none:nn
336 


337 \DeclareDocumentCommand \setpersianmonofont { O{} m O{} }
338   {
339     \__xepersian_main_setpersianmonofont:nn {#1,#3} {#2}
340     \ignorespaces
341   }
342 \cs_new:Nn \__xepersian_main_setpersianmonofont:nn
343  {
344   \fontspec_set_family:Nnn \l__xepersian_persianttfamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
345   \tl_set_eq:NN \persianttdefault \l__xepersian_persianttfamily_family_tl
346   \use:x { \exp_not:n { \DeclareRobustCommand \persianttfamily }
347    {
348     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
349     \exp_not:N \fontfamily { \l__xepersian_persianttfamily_family_tl }
350     \exp_not:N \selectfont
351    }
352   }
353   \str_if_eq_x:nnT {\familydefault} {\persianttdefault}
354     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
355   \__xepersian_setpersianmonofont_hook:nn {#1} {#2}
356   \normalfont
357  }
358 
359 \cs_set_eq:NN \__xepersian_setpersianmonofont_hook:nn   \use_none:nn
360 


361 \DeclareDocumentCommand \setnavarfont { O{} m O{} }
362   {
363     \__xepersian_main_setnavarfont:nn {#1,#3} {#2}
364     \ignorespaces
365   }
366 \cs_new:Nn \__xepersian_main_setnavarfont:nn
367  {
368   \fontspec_set_family:Nnn \l__xepersian_navarfamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
369   \tl_set_eq:NN \navardefault \l__xepersian_navarfamily_family_tl
370   \use:x { \exp_not:n { \DeclareRobustCommand \navarfamily }
371    {
372     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
373     \exp_not:N \fontfamily { \l__xepersian_navarfamily_family_tl }
374     \exp_not:N \selectfont
375    }
376   }
377   \str_if_eq_x:nnT {\familydefault} {\navardefault}
378     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
379   \__xepersian_setnavarfont_hook:nn {#1} {#2}
380   \normalfont
381  }
382 
383 \cs_set_eq:NN \__xepersian_setnavarfont_hook:nn   \use_none:nn
384 


385 \DeclareDocumentCommand \setpookfont { O{} m O{} }
386   {
387     \__xepersian_main_setpookfont:nn {#1,#3} {#2}
388     \ignorespaces
389   }
390 \cs_new:Nn \__xepersian_main_setpookfont:nn
391  {
392   \fontspec_set_family:Nnn \l__xepersian_pookfamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
393   \tl_set_eq:NN \pookdefault \l__xepersian_pookfamily_family_tl
394   \use:x { \exp_not:n { \DeclareRobustCommand \pookfamily }
395    {
396     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
397     \exp_not:N \fontfamily { \l__xepersian_pookfamily_family_tl }
398     \exp_not:N \selectfont
399    }
400   }
401   \str_if_eq_x:nnT {\familydefault} {\pookdefault}
402     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
403   \__xepersian_setpookfont_hook:nn {#1} {#2}
404   \normalfont
405  }
406 
407 \cs_set_eq:NN \__xepersian_setpookfont_hook:nn   \use_none:nn
408 


409 \DeclareDocumentCommand \setsayehfont { O{} m O{} }
410   {
411     \__xepersian_main_setsayehfont:nn {#1,#3} {#2}
412     \ignorespaces
413   }
414 \cs_new:Nn \__xepersian_main_setsayehfont:nn
415  {
416   \fontspec_set_family:Nnn \l__xepersian_sayehfamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
417   \tl_set_eq:NN \sayehdefault \l__xepersian_sayehfamily_family_tl
418   \use:x { \exp_not:n { \DeclareRobustCommand \sayehfamily }
419    {
420     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
421     \exp_not:N \fontfamily { \l__xepersian_sayehfamily_family_tl }
422     \exp_not:N \selectfont
423    }
424   }
425   \str_if_eq_x:nnT {\familydefault} {\sayehdefault}
426     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
427   \__xepersian_setsayehfont_hook:nn {#1} {#2}
428   \normalfont
429  }
430 
431 \cs_set_eq:NN \__xepersian_setsayehfont_hook:nn   \use_none:nn
432 


433 \DeclareDocumentCommand \setiranicfont { O{} m O{} }
434   {
435     \__xepersian_main_setiranicfont:nn {#1,#3} {#2}
436     \ignorespaces
437   }
438 \cs_new:Nn \__xepersian_main_setiranicfont:nn
439  {
440   \fontspec_set_family:Nnn \l__xepersian_iranicfamily_family_tl {Script=Persian,Ligatures=PersianTeX,#1} {#2}
441   \tl_set_eq:NN \iranicdefault \l__xepersian_iranicfamily_family_tl
442   \use:x { \exp_not:n { \DeclareRobustCommand \iranicfamily }
443    {
444     \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
445     \exp_not:N \fontfamily { \l__xepersian_iranicfamily_family_tl }
446     \exp_not:N \selectfont
447    }
448   }
449   \str_if_eq_x:nnT {\familydefault} {\iranicdefault}
450     { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
451   \__xepersian_setiranicfont_hook:nn {#1} {#2}
452   \normalfont
453  }
454 
455 \cs_set_eq:NN \__xepersian_setiranicfont_hook:nn   \use_none:nn

The following is my code which I have replaced all of the above in xepersian.sty:

\cs_set:Nn \__xepersian_declare_set_fonts:nnnnn
  {
  \exp_args:Nc \DeclareDocumentCommand { set #1 font } { O{} m O{} }
    {
      \cs:w __xepersian_main_set #1 font :nn \cs_end: {##1,##3} {##2}
      \ignorespaces
    }
  \exp_args:Nc \cs_new:Nn { __xepersian_main_set #1 font :nn }
    {
      \exp_args:Ncnx \fontspec_set_family:Nnn { l__xepersian_ #2 #3 _family_tl } {#5,##1} {##2}
      \exp_args:Ncc \tl_set_eq:NN { #2 default } { l__xepersian_ #2 #3 _family_tl }
      \use:x { \exp_not:n { \exp_args:Nc \DeclareRobustCommand { #2 #3 } }
        {
          \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
          \exp_not:N \fontfamily { \cs:w l__xepersian_ #2 #3 _family_tl \cs_end: }
          \exp_not:N \selectfont
        }
      }
      \str_if_eq_x:nnT {\familydefault} { \cs:w #4 default \cs_end: }
        { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
      \cs:w __xepersian_set #1 font_hook :nn \cs_end: {##1} {##2}
      \normalfont
    }
  \exp_args:Nc \cs_set_eq:NN { __xepersian_set #1 font_hook :nn }   \use_none:nn
  }

\__xepersian_declare_set_fonts:nnnnn { latintext } { latin } { font } { rm } { Ligatures=TeX }

\seq_new:N \l__my_xepersian_styles_seq
\seq_set_split:Nnn \l__my_xepersian_styles_seq { , }
  { 
    % { { latintext }   { latin }     { font }   { rm }        { Ligatures=TeX } } ,
    { { persiansans } { persiansf } { family } { persiansf } { Script=Persian,Ligatures=PersianTeX } } ,
    { { persianmono } { persiantt } { family } { persiantt } { Script=Persian,Ligatures=PersianTeX } } ,
    { { iranic }      { iranic }    { family } { iranic }    { Script=Persian,Ligatures=PersianTeX } } ,
    { { navar }       { navar }     { family } { navar }     { Script=Persian,Ligatures=PersianTeX } } ,
    { { pook }        { pook }      { family } { pook }      { Script=Persian,Ligatures=PersianTeX } } ,
    { { sayeh }       { sayeh }     { family } { sayeh }     { Script=Persian,Ligatures=PersianTeX } }
  }

\seq_map_inline:Nn \l__my_xepersian_styles_seq
  {
    \__xepersian_declare_set_fonts:nnnnn  #1 
  }

As I am a beginner in LaTeX3, I would like to know if I can improve the quality and efficiency of my code or perhaps simplify it. My MWE is:

\documentclass{article}
\usepackage{xepersian}
\settextfont{Amiri}
\setlatintextfont[Scale=1.44]{Times New Roman}
\setpersianmonofont[Scale=1.44]{Amiri}
\setpersiansansfont[Scale=1.44]{Amiri}
\setiranicfont[Scale=1.44]{Amiri}
\setnavarfont[Scale=1.44]{Amiri}
\setpookfont[Scale=1.44]{Amiri}
\setsayehfont[Scale=1.44]{Amiri}
\pagestyle{empty}
\begin{document}
\lr{This is a test for numbers: 0 1 2 3 4 5 6 7 8 9.} \\
این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹. \\
\persianttfamily \textpersiantt{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\persiansffamily \textpersiansf{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\iranicfamily \textiranic{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\navarfamily \textnavar{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\pookfamily \textpook{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\sayehfamily \textsayeh{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.}
\end{document}

The file xepersian.sty modified by me can be downloaded from Pastebin.

Edited on 2018-12-28: Applied the suggested corrections

With special thanks to TeXnician and Joseph for their help, the new version of the function \__xepersian_declare_set_fonts:nnnnn in which I have applied all of the corrections suggested by them, is:

\cs_generate_variant:Nn \fontspec_set_family:Nnn { c }
\prg_generate_conditional_variant:Nnn  \str_if_eq:nn { Vv } { T, F, TF }
\cs_set:Nn \__xepersian_declare_set_fonts:nnnnn
  {
  \exp_args:Nc \DeclareDocumentCommand { set #1 font } { O{} m O{} }
    {
      \use:c { __xepersian_main_set #1 font:nn }  {##1,##3} {##2}
      \ignorespaces
    }
  \exp_args:Nc \cs_new:Nn { __xepersian_main_set #1 font :nn }
    {
      \fontspec_set_family:cnn { l__xepersian_ #2 #3 _family_tl } {#5,##1} {##2}
      \tl_set_eq:cc { #2 default } { l__xepersian_ #2 #3 _family_tl }
      \use:x { \exp_not:n { \exp_args:Nc \DeclareRobustCommand { #2 #3 } }
        {
          \exp_not:N \fontencoding { \g__fontspec_nfss_enc_tl }
          \exp_not:N \fontfamily { \use:c { l__xepersian_ #2 #3 _family_tl } }
          \exp_not:N \selectfont
        }
      }
      \str_if_eq:VvT {\familydefault} { #4 default }
        { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
      \use:c { __xepersian_set #1 font_hook:nn } {##1} {##2}
      \normalfont
    }
  \cs_set_eq:cN { __xepersian_set #1 font_hook :nn }   \use_none:nn
  }

The modified xepersian.sty is renamed to hmxepersian.sty. The prefix hm in the filename indicates that it can now load fonts with HM in their names.

Also, my new working example is as follows:

% Tested with: xepersian-22.5, Revision 49428 on TeXLive
\documentclass{article}
\usepackage{hmxepersian}
\settextfont[Scale=1.2]{HM XKayhan}
\setlatintextfont[Scale=1.2]{Psychedelia HM}
\setpersianmonofont[Scale=1.2]{DejaVu Sans Mono}
\setpersiansansfont[Scale=1.2]{DejaVu Sans}
\setiranicfont[Scale=1.2]{HM XKayhanOb Oblique}
\setnavarfont[Scale=1.2]{HM XKayhan Navaar}
\setpookfont[Scale=1.2]{HM XKayhan Pook}
\setsayehfont[Scale=1.2]{HM XKayhan Sayeh}
\pagestyle{empty}
\begin{document}
\lr{This is a test for numbers: 0 1 2 3 4 5 6 7 8 9.} \\
این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹. \\
\persianttfamily \textpersiantt{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\persiansffamily \textpersiansf{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\iranicfamily \textiranic{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\navarfamily \textnavar{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\pookfamily \textpook{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.} \\
\sayehfamily \textsayeh{این یک آزمایش اعداد است: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹.}
\end{document}
  • 1
    Could you please make your example a MWE (at least the working part, i.e. compilability, should be given)? – TeXnician Dec 27 '18 at 8:35
  • @TeXnician, Thanks, I added my MWE and a link to the file xepersian.sty modified by me. – Tisha Shusho Dec 27 '18 at 12:39
5

As your example is not compilable and therefore not easy to test, I will provide only some remarks:

  • You are using \exp_args to transform exactly one N type argument into c type, as in

    \exp_args:Ncnx \fontspec_set_family:Nnn { l__xepersian_ #2 #3 _family_tl } {#5,##1} {##2}
    

    Instead you should generate a variant of \fontspec_set_family that does exactly this expansion trickery for you.

    \cs_generate_variant:Nn \fontspec_set_family:Nnn { c }
    

    This can be used as \fontspec_set_family:cnn { l__xepersian_ #2 #3 _family_tl } {#5,##1} {##2}.

  • Another thing concerning variants: There are predefined ones. So when you use \exp_args:Ncc \tl_set_eq:NN you are actually asking for the predefined variant \tl_set_eq:cc and you could use

    \tl_set_eq:cc { #2 default } { l__xepersian_ #2 #3 _family_tl }
    

    And this also holds true for \cs_set_eq:

    \cs_set_eq:cN { __xepersian_set #1 font_hook :nn } \use_none:nn
    
  • You are using cs:w … \cs_end: as \csname\endcsname construction, although you could use \use:c { __xepersian_main_set #1 font:nn } at that point.

  • \str_if_eq_x is deprecated. Seemingly you want to compare the values hold by the two command sequences as arguments (each argument one time expanded and then a comparison of both arguments). For that purpose you could \prg_generate_conditional_variant and then use \str_if_eq:VvT or \tl_if_eq:VvT which would be more idiomatic as in

    \prg_generate_conditional_variant:Nnn  \str_if_eq:nn { Vv } { T, F, TF }
    \str_if_eq:VvT \familydefault { #4 default } { … }
    

    Alternatively you could change to \str_if_eq:ee and turn

    \str_if_eq_x:nnT {\familydefault} { \cs:w #4 default \cs_end: }
      { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
    \cs:w __xepersian_set #1 font_hook :nn \cs_end: {##1} {##2}
    

    into

    \str_if_eq:eeT {\familydefault} { \use:c { #4 default } }
      { \tl_set_eq:NN \encodingdefault \g__fontspec_nfss_enc_tl }
    \use:c { __xepersian_set #1 font_hook:nn } {##1} {##2}
    

Please note that I did not consider speed here. These are just some points from a first look on your code (cleanliness and readability).

| improve this answer | |
  • I'd use \str_if_eq:VvT in place of \str_if_eq_x:nnT (generating if required): semantically, we don't want arbitrary expansion, we want to compare the value of one variable with the value from the name of a second. (Of course, it's very slightly slower than \str_if_eq:eeT.) – Joseph Wright Dec 27 '18 at 9:00
  • @JosephWright Ah, okay, I suggested the \tl_if_eq:VV version, but you might be right that the string comparison should be the preferred approach. I will change that. – TeXnician Dec 27 '18 at 9:02
  • tl comparison would be slower: there are two assignments done. In general, we tend to use string comparisons as almost always that's semantically what you want, too. – Joseph Wright Dec 27 '18 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.