BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
nevla
Fluorite | Level 6

I have been creating a macro that creates some ODS output along with a Table of Contents.

My macro has a switch that allows users to select RTF, PDF or Word output (there are reasons for having the option of RTF and DOCX separately).

The macro has an option to insert custom text for the TOC title line.

 

If i'm using ODS RTF I can manipulate the template style I am using and change the title within ContentTitle:

 

style ContentTitle from ContentTitle /
  pretext = ""
  posttext="Line1(*ESC*){newline}Line2"
  color = dark blue;

Notice I want multiple lines in the title, so i use the ODS escape command {newline}.

 

 

However, when I try and do the same for ODS WORD, no formatting is honored

I have tried the following (creating the string as a macro variable to insert into the template):

 

data _null;
*call symputx('testtitle', catx('0A'x, "Line1", "Line2"));
call symputx('testtitle', "Line1(*ESC*){newline}Line2");
*call symputx('testtitle', "Line1\nLine2");
run;

proc template;
   define style styles.test_docx;
      parent=styles.word;
      style ContentTitle from ContentTitle /
         content = "&testtitle." 
         /*protectspecialcharacters=on*/ 
         /*asis=on*/
         /*contenttype="text/plain"*/
         ;
      end;
run;

ODS Escape sequences did not work, neither did ASCII like break characters or the simple "\n" (which weirdly printed out as "¥n"). I tried some other options I dug out of the log, like "protectspecialchars", "asis", etc but nothing seems to want to make a line break in an ODS WORD TOC.

 

Does anyone have a solution before I contact the Technical guys?

Full testing program attached.

 

 

 

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
nevla
Fluorite | Level 6

Solved!

I had exhausted all the SAS tricks I knew, and focussed instead on manipulating the underlying XML of the resulting DOCX file.

After experimenting with several possible solutions, I discovered that the title that you set in the ODS template is written directly to the WORD XML, with no interpretation at all.

Line breaks are ignored because they have no meaning in XML, and so to insert a line break I had to use WORD XML languate instead:

proc template;
  define style styles.test_docx;
    parent=styles.word;
      style ContentTitle from ContentTitle /
        content = "Title1<w:br />Title2" ;
  end;
run;

So I can add something to substutute "(*ESC*){newline}" with "<w:br />" in the input parameters, or use some keyword a user can specify to mean a line break that i replace as required.

 

 

View solution in original post

3 REPLIES 3
ballardw
Super User

Unless you are dealing with 1000's of lines of code it is best to just paste the code into a text box opened on the forum with the </> icon above the message window. That way no one has to download a file from an unknown source and we call all follow along:

 

*--- set outpout area ---;
%let output_area=<Insert path here>;

*--- Define the output tables ---;
%macro outputstuff(type=);

   ods proclabel="Table 1";
   proc print data=sashelp.class;
   run;
   ods &type. startpage=now;
   ods proclabel="Table 2";
   proc print data=sashelp.class;
   run;
   ods &type. startpage=now;
   ods proclabel="Table 3";
   proc print data=sashelp.class;
   run;

%mend outputstuff;


*---------*
| ODS RTF |
*---------*;
 
*--- Change title of TOC ---;
proc template;
   define style styles.test_rtf;
      parent=styles.rtf;
      style ContentTitle from ContentTitle /
         pretext = ""
         posttext="Line1(*ESC*){newline}Line2"
         color = dark blue
         ;
      end;
run;

*--- Generate output ---;
ods rtf file="&output_area./sas_bug.rtf"
        contents toc_data
        style=styles.test_rtf;

   %outputstuff(type=rtf);

ods rtf close;


*----------*
| ODS WORD |
*----------*;

*--- Change title of TOC ---;
data _null;
*call symputx('testtitle', catx('0A'x, "Line1", "Line2"));
call symputx('testtitle', "Line1(*ESC*){newline}Line2");
*call symputx('testtitle', "Line1\nLine2");

run;

proc template;
   define style styles.test_docx;
      parent=styles.word;
    style ContentTitle from ContentTitle /
         content = "&testtitle." 
         /*protectspecialcharacters=on*/ 
         /*asis=on*/
         /*contenttype="text/plain"*/
         ;
      end;
run;

*--- Generate output ---;
ods word file="&output_area./sas_bug.docx"
         options(contents="on" toc_data="on")
         style=styles.test_docx;

   %outputstuff(type=word);

ods word close;

Unless there is more manipulation involved you should consider using a %let instead of data _null_.

%let testtitle=Line1(*ESC*){newline}Line2;

 

May want to use Proc template to look closely at the Styles.word.

In my install it doesn't inherit much from Styles.default ( through Printer )as the Styles.RTF does. Which is likely why what works with RTF doesn't with Word, the "parent" values aren't there to modify.

 

Plus my install still shows ODS Word as "preproduction", which sometimes means all the features aren't quite stable yet. I know that I can't get Word to render lots of tables correctly that have no problems in RTF (or PDF or HTML)

nevla
Fluorite | Level 6

Thanks @ballardw , I wasn't sure of the etiquette when wishing to post a program.

 

Regarding the %let over a symputx – I did it like that so I could construct test strings using catx (like the included one where I was trying to insert an ascii ‘0A’x which i dodn't know would work in a %let). I haven't tried the byte function yet, but suspect it may have the same effect (but i'll try that now!)

 

As for the PROC TEMPLATE, I have deconstructed all the styles in the past, and indeed built new ones entirely from scratch with no inheritance. What I couldn’t find in any of them, and indeed in any manual, is the right command that would trigger a line break within an ODS Word TOC title.

And it is a little frustrating that ODS WORD is still preproduction after over 5 years but it is odd it doesn’t seems to conform to the usual ODS commands.

 

Worst case scenario is I do post-processing on the DOCX to insert a line break where I want, but that’s like cracking a walnut with a hammer – I’d rather find a simpler, more elegant solution 😊

nevla
Fluorite | Level 6

Solved!

I had exhausted all the SAS tricks I knew, and focussed instead on manipulating the underlying XML of the resulting DOCX file.

After experimenting with several possible solutions, I discovered that the title that you set in the ODS template is written directly to the WORD XML, with no interpretation at all.

Line breaks are ignored because they have no meaning in XML, and so to insert a line break I had to use WORD XML languate instead:

proc template;
  define style styles.test_docx;
    parent=styles.word;
      style ContentTitle from ContentTitle /
        content = "Title1<w:br />Title2" ;
  end;
run;

So I can add something to substutute "(*ESC*){newline}" with "<w:br />" in the input parameters, or use some keyword a user can specify to mean a line break that i replace as required.

 

 

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 3 replies
  • 335 views
  • 0 likes
  • 2 in conversation