I want to use ods rtf to output my forest plot, and there are two font styles in my "docfont", for example, "sex" is bold, "female" and "male" are normal font in my forest plot, so my question is how I can keep this style when I output this forest plot with osd rtf?
In the my rtf file, sex, female and male became one kind of font, and it seems that defining template doesn't work....
I still don't know what's the 'FixedEmphasisFont', 'FixedStrongFont', 'FixedHeadingFont', 'BatchFixedFont' and 'FixedFont', thanks very much!!
Code and active style used to generate the output always helps clarify things.
The style on the ODS RTF statement will have an impact on your contents.
How you generate you plot is important as well. Without code it is hard to make specific suggestions. And to test code we would need example data as well.
proc template; define statgraph Forest; dynamic _bandcolor _headercolor _subgroupcolor _cweights; begingraph / DESIGNHEIGHT=7.95in collation=binary; entrytitle textattrs=(size=7pt ) halign=left "&PRTNAME." halign=right "Page 1 of 1"; entrytitle textattrs=(size=7pt ) halign=left "forest plot"; discreteattrmap name='text'; value '1' / textattrs=(weight=bold size=35pt); value '2' / textattrs=(weight=normal size=9pt); enddiscreteattrmap; discreteattrvar attrvar=textid var=id attrmap="text"; layout lattice / rows=1 columns=3 columnweights=_cweights rowdatarange=union; /*--Column headers--*/ sidebar / align=top; layout lattice / rows=1 columns=3 columnweights=_cweights backgroundcolor=_headercolor opaque=true; entry textattrs=(size=10pt weight=bold) halign=left "Subgroup"; entry textattrs=(size=10pt weight=bold) halign=center " "; entry textattrs=(size=10pt weight=bold) halign=left "P50 (95% CI)"; endlayout; endsidebar; /*--Columns on the left--*/ layout overlay / yaxisopts=(&yaxisopts) walldisplay=none xaxisopts=(display=none); innermargin / align=left opaque=true; axistable y=ObsId value=_label_ / DISPLAY=(VALUES) indentweight=indent textgroup=textid; endinnermargin; endlayout; /*--Graph in the middle--*/ layout overlay / yaxisopts=(&yaxisopts) walldisplay=none xaxisopts=(display=(line ticks tickvalues) /*LABELFITPOLICY=SPLITALWAYS labelposition=center labelsplitchar="~" LABELSPLITJUSTIFY=right*/ tickvalueattrs=(size=9pt) labelattrs=(size=9pt) ); referenceline x=1; highlowplot y=obsid low=low high=high / lineattrs=(thickness=3); scatterplot y=obsid x=PFS / markerattrs=(symbol=diamondfilled color=black ) primary=true; endlayout; /*--Columns on the right--*/ layout overlay / yaxisopts=(&yaxisopts) walldisplay=none xaxisopts=(display=none); innermargin / align=left opaque=true; axistable y=obsid value=COL1 / valueattrs=(size=9pt) DISPLAY=(VALUES) ; endinnermargin; endlayout; endlayout; endgraph; end; run; proc sgrender data=forestplot template=Forest; dynamic _bandcolor='cxf0f0f0' _headercolor='cxd0d0d0' _cweights="0.3 0.4 0.3"; run;
This is my code to produce forest plot,
The picture is one part of my output if I don't use ods rtf.
If I use ods rtf, the bold fonts "Sex" and "Race" become normal. I tried to define template, but I don't know which command I can use to control their appearances.
Thanks a lot!
Example data? Can't test code without data. If you are using formats to display specific values then we need the format definition(s) as well.
Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the {i} icon or attached as text to show exactly what you have and that we can test code against.
ODS RTF destination statement with all the options you set.
Your template code involves macro variables. Without the definition of the macro variables we can't test the template at all.
Thanks for your instructions, I decided to use the Sanjay Matange's example code of forest plot to show my question, it's more convenient. Here is the code:
ods html close;
/*--To retain leading and trailing blanks, we must use nbsp instead of blank--*/
/*--For visibility, we have used '.' in place of blanks --*/
/*-- Later these '.' values are changed to nbsp 'A0'x --*/
/*--Regular leading blanks will be stripped, losing the indentation --*/
/*--Add "Id" to identify subgroup headings from values --*/
data forest;
input Id Subgroup $3-27 Count Percent Mean Low High PCIGroup Group PValue;
zero=0; one=1;
PCI_lbl='PCI Group';
grp_lbl='Group';
pval_lbl='P Value';
ObsId=_n_;
if count ne . then CountPct=put(count, 4.0) || "(" || put(percent, 3.0) || ")";
datalines;
1 Overall..................2166 100 1.3 0.9 1.5 17.2 15.6 .
1 Age....................... . . . . . . 0.05
2 ..<= 65 Yr...............1534 71 1.5 1.05 1.9 17.0 13.2 .
2 ..> 65 Yr................ 632 29 0.8 0.6 1.25 17.8 21.3 .
1 Sex....................... . . . . . . 0.13
2 ..Male...................1690 78 1.5 1.05 1.9 16.8 13.5 .
2 ..Female................. 476 22 0.8 0.6 1.3 18.3 22.9 .
1 Race or ethnic group...... . . . . . . 0.52
2 ..Nonwhite............... 428 20 1.05 0.6 1.8 18.8 17.8 .
2 ..White..................1738 80 1.2 0.85 1.6 16.7 15.0 .
1 From MI to Randomization.. . . . . . . 0.81
2 ..<= 7 days.............. 963 44 1.2 0.8 1.5 18.9 18.6 .
2 ..> 7 days...............1203 56 1.15 0.75 1.5 15.9 12.9 .
1 Infract-related artery.... . . . . . . 0.38
2 ..LAD.................... 781 36 1.4 0.9 1.9 20.1 16.2 .
2 ..Other..................1385 64 1.1 0.8 1.4 15.6 15.3 .
1 Ejection Fraction......... . . . . . . 0.48
2 ..< 50%..................1151 54 1.2 0.8 1.5 22.6 20.4 .
2 ..>= 50%................. 999 46 0.9 0.6 1.4 10.7 11.1 .
1 Diabetes.................. . . . . . . 0.41
2 ..Yes.................... 446 21 1.4 0.9 2.0 29.3 23.3 .
2 ..No.....................1720 79 1.1 0.8 1.5 14.4 13.5 .
1 Killip class.............. . . . . . . 0.39
2 ..I......................1740 81 1.2 0.8 1.6 15.2 13.1 .
2 ..II-IV.................. 413 19 0.95 0.6 1.5 25.3 26.9 .
;
run;
/*proc print;run;*/
/*--Replace '.' in subgroup with blank--*/
data forest2;
set forest;
subgroup=translate(subgroup, ' ', '.');
val=mod(_N_-1, 6);
if val eq 1 or val eq 2 or val eq 3 then ref=obsid;
/*--Separate Subgroup headers and obs into separate columns--*/
if id=1 then do;
heading=subgroup;
subgroup='';
end;
run;
/*proc print;run;*/
/*--Create font with smaller fonts for axis label, value and data--*/
proc template;
define style listingSF;
parent = Styles.Listing;
style GraphFonts from GraphFonts
"Fonts used in graph styles" /
'GraphDataFont' = ("<sans-serif>, <MTsans-serif>",7pt)
'GraphValueFont' = ("<sans-serif>, <MTsans-serif>",7pt)
'GraphLabelFont' = ("<sans-serif>, <MTsans-serif>",7pt, bold);
;
end;
run;
ods rtf file="D:\forestplot.rtf" style=Styles.Custom toc_data nocontents;
/*--Define templage for Forest Plot--*/
/*--Template uses a Layout Lattice of 5 columns--*/
proc template;
define statgraph Forest;
dynamic _bandcolor _headercolor _subgroupcolor;
begingraph;
layout lattice / columns=4 columnweights=(0.23 0.07 0.4 0.3);
/*--Column headers--*/
sidebar / align=top;
layout lattice / rows=2 columns=4 columnweights=(0.18 0.17 0.35 0.3)
backgroundcolor=_headercolor opaque=true;
entry textattrs=(size=8 weight=bold) halign=left "Subgroup";
entry textattrs=(size=8 weight=bold) " No.of Patients (%)";
entry textattrs=(size=8 weight=bold) "Hazard Ratio";
entry halign=center textattrs=(size=8 weight=bold) "4-Yr Cumulative Event Rate" ;
entry " ";
entry " ";
entry " ";
entry halign=center textattrs=(size=6) "Medical Therapy";
endlayout;
endsidebar;
/*--First Subgroup column, shows only the Y2 axis --*/
/*--Use HighLow plot to place the heading and subgroup values as HighLabels--*/
/*--Indenting is done by making the 2nd highlow bar 1 unit long --*/
/*--Highlow bar itself has thickness=0 --*/
layout overlay / walldisplay=none
xaxisopts=(display=none linearopts=(viewmin=0 viewmax=20))
yaxisopts=(reverse=true display=none tickvalueattrs=(weight=bold));
referenceline y=ref / lineattrs=(thickness=15 color=_bandcolor);
highlowplot y=obsid low=zero high=zero / highlabel=heading lineattrs=(thickness=0)
labelattrs=(size=7 weight=bold);
highlowplot y=obsid low=zero high=one / highlabel=subgroup lineattrs=(thickness=0);
endlayout;
/*--Second column showing Count and percent--*/
layout overlay / xaxisopts=(display=none)
yaxisopts=(reverse=true display=none) walldisplay=none;
referenceline y=ref / lineattrs=(thickness=15 color=_bandcolor);
scatterplot y=obsid x=zero / markercharacter=countpct
markercharacterattrs=graphvaluetext;
endlayout;
/*--Third column showing odds ratio graph--*/
layout overlay / xaxisopts=(label=' <---PCI Better---- ----Medical Therapy Better--->'
linearopts=(tickvaluepriority=true
tickvaluelist=(0.0 0.5 1.0 1.5 2.0 2.5)))
yaxisopts=(reverse=true display=none) walldisplay=none;
referenceline y=ref / lineattrs=(thickness=15 color=_bandcolor);
highlowplot y=obsid low=low high=high;
scatterplot y=obsid x=mean / markerattrs=(symbol=squarefilled);
referenceline x=1;
endlayout;
/*--Fourth column showing PCIGroup and Group columns--*/
layout overlay / x2axisopts=(display=(tickvalues) offsetmin=0.25 offsetmax=0.25)
yaxisopts=(reverse=true display=none) walldisplay=none;
referenceline y=ref / lineattrs=(thickness=15 color=_bandcolor);
scatterplot y=obsid x=pci_lbl / markercharacter=PCIGroup xaxis=x2
markercharacterattrs=graphvaluetext;
scatterplot y=obsid x=grp_lbl / markercharacter=group xaxis=x2
markercharacterattrs=graphvaluetext;
scatterplot y=obsid x=pval_lbl / markercharacter=pvalue xaxis=x2
markercharacterattrs=graphvaluetext;
endlayout;
endlayout;
entryfootnote halign=left textattrs=(size=7)
'The p-value is from the test statistic for testing the interaction between the '
'treatment and any subgroup variable';
endgraph;
end;
run;
/*--Render Forest Plot without horizontal bands--*/
*ods graphics / reset width=7in height=5in imagename='Forest_HighLow_93';
proc sgrender data=Forest2 template=Forest;
dynamic _bandcolor='white' _headercolor='white';
run;
ods rtf close;
you can see that if I use ods rtf to output my forest plot, the original bold fonts such as " Sex", "Overall","age" (parts of docfonts, not the header) became normal fonts, I don't know how to change them back to bold fonts by option "style=", do you have any ideas? Thanks a lot!
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.