So in this code I think I figured out how page breaks and separating texts from tables work. However, contents start on second instead of the first line for each page. How do I make RTF outputs start contents on first line for each page? Have this been already asked and addressed here before? Just in case, I attached the source as well as the resulting RTF.
proc import datafile="C:\Users\Johnny\grades.xlsx"
out=grades
replace;
run;
ODS LISTING CLOSE;
OPTIONS NODATE
NONUMBER
TOPMARGIN =0.5IN
LEFTMARGIN =0.5IN
RIGHTMARGIN =0.5IN
BOTTOMMARGIN=0.5IN;
/*%let ftype=PDF;*/
%let ftype=RTF;
ods noptitle;
ods tagsets.rtf FILE = "C:\Users\Johnny\test.&ftype" STYLE=BARRETTSBLUE options(doc='Help' tables_off='USERTEXT');
ods proclabel='Frequencies';
ODS ESCAPECHAR = '^';
ODS tagsets.&ftype TEXT = "^{sectd}^{STYLE[FONTSIZE=10.5pt JUST=L fontweight=bold]QUIZ}";
ODS tagsets.&ftype TEXT = " ";
PROC REPORT DATA = grades MISSING STYLE(REPORT)=[width=100% BACKGROUND=WHITE BORDERCOLOR=WHITE BORDERWIDTH=0 ASIS=OFF FONTSIZE=2] NOWD contents="quiz: &vlabel";
COLUMN quiz quiz=vallabel DUMROW N PCTN;
COMPUTE quiz;
COUNT+1;
/* IF (MOD(COUNT,2)) THEN DO;*/
/* CALL DEFINE (_ROW_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');*/
/* END;*/
if (mod(count,2))=0 then call define(_row_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');
ENDCOMP;
DEFINE quiz /"Value" GROUP ORDER=INTERNAL STYLE(COLUMN)=[WIDTH=10% JUST=C FONTSIZE=2] STYLE(HEADER)=[JUST=C FONTSIZE=2] ;
DEFINE vallabel /"Label" GROUP ID STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] format=1. NOPRINT ;
DEFINE DUMROW /COMPUTED "Label" STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] width=65;
DEFINE N /"Frequency" STYLE(COLUMN)=[WIDTH=15% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=COMMA12. ;
DEFINE PCTN /"%" STYLE(COLUMN)=[WIDTH=10% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=PERCENT7.1 ;
COMPUTE DUMROW /CHAR LENGTH = 65;
IF _BREAK_="_RBREAK_" THEN DO DUMROW = 'Total';
END;
ELSE DUMROW=PUT(vallabel, 1.);
IF DUMROW = 'Total' THEN CALL DEFINE (_ROW_, 'STYLE', "STYLE=[BACKGROUND=LIGHT GRAY FONTSTYLE=ITALIC FONTSIZE=2]");
ENDCOMP;
RBREAK AFTER / SUMMARIZE;
RUN;
ODS tagsets.&ftype TEXT = " ";
ODS tagsets.&ftype TEXT = "AND THAT'S A WRAP FOR QUIZZES";
ods startpage=now;
ODS tagsets.&ftype TEXT = "^{sectd}^{STYLE[FONTSIZE=10.5pt JUST=L fontweight=bold]GRADE}";
ODS tagsets.&ftype TEXT = " ";
PROC REPORT DATA = grades MISSING STYLE(REPORT)=[width=100% BACKGROUND=WHITE BORDERCOLOR=WHITE BORDERWIDTH=0 ASIS=OFF FONTSIZE=2] NOWD contents="GRADE: &vlabel";
COLUMN GRADE GRADE=vallabel DUMROW N PCTN;
COMPUTE GRADE;
COUNT+1;
/* IF (MOD(COUNT,2)) THEN DO;*/
/* CALL DEFINE (_ROW_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');*/
/* END;*/
if (mod(count,2))=0 then call define(_row_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');
ENDCOMP;
DEFINE GRADE /"Value" GROUP ORDER=INTERNAL STYLE(COLUMN)=[WIDTH=10% JUST=C FONTSIZE=2] STYLE(HEADER)=[JUST=C FONTSIZE=2] ;
DEFINE vallabel /"Label" GROUP ID STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] format=$1. NOPRINT ;
DEFINE DUMROW /COMPUTED "Label" STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] width=65;
DEFINE N /"Frequency" STYLE(COLUMN)=[WIDTH=15% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=COMMA12. ;
DEFINE PCTN /"%" STYLE(COLUMN)=[WIDTH=10% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=PERCENT7.1 ;
COMPUTE DUMROW /CHAR LENGTH = 65;
IF _BREAK_="_RBREAK_" THEN DO DUMROW = 'Total';
END;
ELSE DUMROW=PUT(vallabel, $1.);
IF DUMROW = 'Total' THEN CALL DEFINE (_ROW_, 'STYLE', "STYLE=[BACKGROUND=LIGHT GRAY FONTSTYLE=ITALIC FONTSIZE=2]");
ENDCOMP;
RBREAK AFTER / SUMMARIZE;
RUN;
ODS tagsets.&ftype TEXT = " ";
ODS tagsets.&ftype TEXT = "AND THAT'S A WRAP FOR GRADES";
ods startpage=now;
quit;
title;
ODS _all_ CLOSE;
I'm not exactly sure what you mean by "first line".
RTF files can have an area set aside for Header and Footer information generally. So text not in the header may appear below that reserved and unused space.
There is also a style element PARSKIP that sets space between tables. ODS TEXT would typically be a table affected by such and the setting may be in effect even for things set as Tables_off.
You can override that space setting with the TAGSETS.RTF option VSPACE='NO' to suppress applying any space before or after tables.
Many users here don't want to download Excel files because of virus potential, others have such things blocked by security software. Also if you give us Excel we have to create a SAS data set and due to the non-existent constraints on Excel data cells the result we end up with may not have variables of the same type (numeric or character) and even values.
@BigPete wrote:
So in this code I think I figured out how page breaks and separating texts from tables work. However, contents start on second instead of the first line for each page. How do I make RTF outputs start contents on first line for each page? Have this been already asked and addressed here before? Just in case, I attached the source as well as the resulting RTF.
proc import datafile="C:\Users\Johnny\grades.xlsx" out=grades replace; run; ODS LISTING CLOSE; OPTIONS NODATE NONUMBER TOPMARGIN =0.5IN LEFTMARGIN =0.5IN RIGHTMARGIN =0.5IN BOTTOMMARGIN=0.5IN; /*%let ftype=PDF;*/ %let ftype=RTF; ods noptitle; ods tagsets.rtf FILE = "C:\Users\Johnny\test.&ftype" STYLE=BARRETTSBLUE options(doc='Help' tables_off='USERTEXT'); ods proclabel='Frequencies'; ODS ESCAPECHAR = '^'; ODS tagsets.&ftype TEXT = "^{sectd}^{STYLE[FONTSIZE=10.5pt JUST=L fontweight=bold]QUIZ}"; ODS tagsets.&ftype TEXT = " "; PROC REPORT DATA = grades MISSING STYLE(REPORT)=[width=100% BACKGROUND=WHITE BORDERCOLOR=WHITE BORDERWIDTH=0 ASIS=OFF FONTSIZE=2] NOWD contents="quiz: &vlabel"; COLUMN quiz quiz=vallabel DUMROW N PCTN; COMPUTE quiz; COUNT+1; /* IF (MOD(COUNT,2)) THEN DO;*/ /* CALL DEFINE (_ROW_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');*/ /* END;*/ if (mod(count,2))=0 then call define(_row_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]'); ENDCOMP; DEFINE quiz /"Value" GROUP ORDER=INTERNAL STYLE(COLUMN)=[WIDTH=10% JUST=C FONTSIZE=2] STYLE(HEADER)=[JUST=C FONTSIZE=2] ; DEFINE vallabel /"Label" GROUP ID STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] format=1. NOPRINT ; DEFINE DUMROW /COMPUTED "Label" STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] width=65; DEFINE N /"Frequency" STYLE(COLUMN)=[WIDTH=15% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=COMMA12. ; DEFINE PCTN /"%" STYLE(COLUMN)=[WIDTH=10% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=PERCENT7.1 ; COMPUTE DUMROW /CHAR LENGTH = 65; IF _BREAK_="_RBREAK_" THEN DO DUMROW = 'Total'; END; ELSE DUMROW=PUT(vallabel, 1.); IF DUMROW = 'Total' THEN CALL DEFINE (_ROW_, 'STYLE', "STYLE=[BACKGROUND=LIGHT GRAY FONTSTYLE=ITALIC FONTSIZE=2]"); ENDCOMP; RBREAK AFTER / SUMMARIZE; RUN; ODS tagsets.&ftype TEXT = " "; ODS tagsets.&ftype TEXT = "AND THAT'S A WRAP FOR QUIZZES"; ods startpage=now; ODS tagsets.&ftype TEXT = "^{sectd}^{STYLE[FONTSIZE=10.5pt JUST=L fontweight=bold]GRADE}"; ODS tagsets.&ftype TEXT = " "; PROC REPORT DATA = grades MISSING STYLE(REPORT)=[width=100% BACKGROUND=WHITE BORDERCOLOR=WHITE BORDERWIDTH=0 ASIS=OFF FONTSIZE=2] NOWD contents="GRADE: &vlabel"; COLUMN GRADE GRADE=vallabel DUMROW N PCTN; COMPUTE GRADE; COUNT+1; /* IF (MOD(COUNT,2)) THEN DO;*/ /* CALL DEFINE (_ROW_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]');*/ /* END;*/ if (mod(count,2))=0 then call define(_row_, 'STYLE', 'STYLE=[BACKGROUND=WHITE]'); ENDCOMP; DEFINE GRADE /"Value" GROUP ORDER=INTERNAL STYLE(COLUMN)=[WIDTH=10% JUST=C FONTSIZE=2] STYLE(HEADER)=[JUST=C FONTSIZE=2] ; DEFINE vallabel /"Label" GROUP ID STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] format=$1. NOPRINT ; DEFINE DUMROW /COMPUTED "Label" STYLE(COLUMN)=[WIDTH=60% JUST=L FONTSIZE=2] STYLE(HEADER)=[JUST=L FONTSIZE=2] width=65; DEFINE N /"Frequency" STYLE(COLUMN)=[WIDTH=15% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=COMMA12. ; DEFINE PCTN /"%" STYLE(COLUMN)=[WIDTH=10% JUST=R FONTSIZE=2] STYLE(HEADER)=[JUST=R FONTSIZE=2] FORMAT=PERCENT7.1 ; COMPUTE DUMROW /CHAR LENGTH = 65; IF _BREAK_="_RBREAK_" THEN DO DUMROW = 'Total'; END; ELSE DUMROW=PUT(vallabel, $1.); IF DUMROW = 'Total' THEN CALL DEFINE (_ROW_, 'STYLE', "STYLE=[BACKGROUND=LIGHT GRAY FONTSTYLE=ITALIC FONTSIZE=2]"); ENDCOMP; RBREAK AFTER / SUMMARIZE; RUN; ODS tagsets.&ftype TEXT = " "; ODS tagsets.&ftype TEXT = "AND THAT'S A WRAP FOR GRADES"; ods startpage=now; quit; title; ODS _all_ CLOSE;
In case anyone prefers copy and paste, here's the input data:
Student | Quiz | Grade |
Joe | 1 | C |
Joe | 2 | A |
Joe | 3 | A |
Sue | 1 | A |
Sue | 2 | B |
Sue | 3 | B |
Bob | 1 | C |
Bob | 2 | B |
Bob | 3 | C |
Deb | 1 | B |
Deb | 2 | B |
Deb | 3 | A |
@ballardw Does my question now make any sense? I just tried with VSPACE='NO' but it didn't solve this issue.
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!
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.
Select SAS Training centers are offering in-person courses. View upcoming courses for: