BookmarkSubscribeRSS Feed
Jerry8989
Calcite | Level 5

Hello,

 

I'm using ODS to create a pdf file and I only want specific bookmarks to be created.  I'm not sure how to do it for specific parts of my pdf document. 

 

I don't want bookmarks for the first data _null_.   It currently has the following 2 level bookmark:

The Datastep procedure

|_ Data NULL Table

 

For the second data _null_ I want the top level bookmark to say "PRODUCTS" and then a bookmark for each item name.

Is this possible with the approach I am using?  I did it this way because I needed the information in a specific layout.

 

 

Here is the code:

ods listing ; 
title ; 
footnote ;
options orientation=portrait 
    topmargin=.45in bottommargin=.4in rightmargin=.4in leftmargin=.4in 
    pageno=1 nodate nonumber nocenter 
  ;
ods listing close ;
ods escapechar='^' ; 
ods pdf file = "&path.\prods.pdf" BOOKMARKGEN=YES bookmarklist=show nocontents ;
title j=c font='Courier New' height=9pt "PRODUCT LIST"  ;
title2 ' ' ;
title3 ' ' ;
footnote j=c height=8pt  font='Courier New' 'Page ^{thispage} of ^{lastpage}' ;

ods pdf text="^10n" ;
ods pdf text="^S={font_size=20pt font_face='Times New Roman' fontweight=bold just=center}2019 PRODUCTS^S={}" ;
ods pdf startpage=now ;

*Create product name list by year ;

data _null_ ;     
  set ProdData1 end=lastObs ;
  by year, ItemID, ItemName  ;  
  
  if _n_ = 1 then do ;
    declare odsout obj() ;    
    obj.table_start(overrides: 'frame=void rules=none cellpadding=0in cellspacing=.06in ' ) ;
    obj.body_start() ;
    obj.row_start() ;
    obj.format_cell(text: 'Product Index', column_span: 2, overrides: "backgroundcolor=white just=center font_face='Courier New' fontweight=bold") ;
    obj.row_end() ;   
  end ;
  if first.year then do ;     
    obj.row_start() ;
    obj.format_cell(text: year, column_span: 2, overrides: "backgroundcolor=white just=left font_face='Courier New' fontweight=bold") ;
    obj.row_end() ;   
  end ;
  if first.ItemID then do ;
    obj.row_start() ;
    obj.format_cell(text: ItemName, column_span: 2, overrides: "marginleft=.50in backgroundcolor=white just=left font_face='Courier New' fontweight=bold") ;
    obj.row_end() ;     
  end ;     
  if lastObs then do ;
    obj.body_end() ;
    obj.table_end() ; 
  end ; 
run ; 


*List out product details ;

data _null_ ;
  set ProdData2 ;
  by year, ItemID, ItemName  ; 
  
  if _n_ = 1 then do ;
    declare odsout obj() ;    
  end ;  
  if first.itemname then do ;    
    obj.table_start(overrides: 'frame=void rules=none cellpadding=0in cellspacing=.06in ' ) ;
      obj.body_start() ;        
        obj.row_start() ;
          obj.format_cell(text: ProductDesc, column_span: 2,  overrides: "cellwidth=6.5in backgroundcolor=white just=left fontweight=bold font_face='Courier New' font_size=9pt ") ;               
        obj.row_end() ;
        obj.row_start() ;
          obj.format_cell(text: Price, column_span: 2, overrides: "backgroundcolor=white just=left font_face='Courier New' font_size=9pt ") ;
        obj.row_end() ;
        if length(strip(warningtxt)) > 1 then do ;
          obj.row_start() ;
            obj.format_cell(text: warningtxt, column_span: 2, overrides: "backgroundcolor=white just=left font_face='Courier New' ") ;
          obj.row_end() ;       
        end ;               
  end ;    
  if last.itemname then do ;   
    obj.body_end() ;
    obj.table_end() ;
  end ;
run ; 
11 REPLIES 11
Cynthia_sas
Diamond | Level 26

Hi:

  Is that code working for you? In running a test with SASHELP.CLASS, since you did not post any data, my version of SAS does NOT allow commas in the BY statement:

no_comma_by.png

 

I assumed that you copied and pasted working code instead of typing it all, so I'm curious how you're getting output.

 

Cynthia

 

Jerry8989
Calcite | Level 5

Hi Cynthia,

 

No that was a mistake on my part.  There should be no commas in the order by.  I'm actually a little bit closer now.  

Within the second data _null_ I added "Label:ItemName" within the table_start() row.  This shows me each item now like I want.

The only issue is that when I press on the bookmark it takes me to that section but just under the item name so I can't see the item name just the description.   Getting the second part to show the item name and have it show the start of the item on the page is more important then excluding the bookmarks for the first section.  Thank you for your help.

 

*List out product details ;

data _null_ ;
  set ProdData2 ;
  by year, ItemID, ItemName  ; 
  
  if _n_ = 1 then do ;
    declare odsout obj() ;    
  end ;  
  if first.itemname then do ;    
    obj.table_start(Label:Itemname, overrides: 'frame=void rules=none cellpadding=0in cellspacing=.06in ' ) ;
      obj.body_start() ;        
        obj.row_start() ;
          obj.format_cell(text: ProductDesc, column_span: 2,  overrides: "cellwidth=6.5in backgroundcolor=white just=left fontweight=bold font_face='Courier New' font_size=9pt ") ;               
        obj.row_end() ;
        obj.row_start() ;
          obj.format_cell(text: Price, column_span: 2, overrides: "backgroundcolor=white just=left font_face='Courier New' font_size=9pt ") ;
        obj.row_end() ;
        if length(strip(warningtxt)) > 1 then do ;
          obj.row_start() ;
            obj.format_cell(text: warningtxt, column_span: 2, overrides: "backgroundcolor=white just=left font_face='Courier New' ") ;
          obj.row_end() ;       
        end ;               
  end ;    
  if last.itemname then do ;   
    obj.body_end() ;
    obj.table_end() ;
  end ;
run ; 
Cynthia_sas
Diamond | Level 26
Hi:
Can you mock up a test that uses one of the SASHELP datasets? Without data, no one can run your code without guessing what the data structure looks like and making some fake data.
Cynthia
Jerry8989
Calcite | Level 5

Hi Cynthia,

 

This code uses sashelp.shoes to create the pdf file using ods.  This file has the same issues I'm having when clicking bookmarks they don't display the title of that bookmark.

 

proc sql ;
create table shoes
as
select region, subsidiary, product, stores, sales, inventory, returns	
from sashelp.shoes
order by region, subsidiary, product
;
quit ;

ods listing ; 
title ; 
footnote ;
options orientation=portrait 
    topmargin=.45in bottommargin=.4in rightmargin=.4in leftmargin=.4in 
    pageno=1 nodate nonumber nocenter 
  ;
ods listing close ;
ods escapechar='^' ; 
ods pdf file = "c:\temp\mock.pdf" BOOKMARKGEN=YES bookmarklist=show nocontents ;

title j=c font='Courier New' height=9pt "Mock Up"  ;
title2 ' ' ;
title3 ' ' ;
footnote j=c height=8pt  font='Courier New' 'Page ^{thispage} of ^{lastpage}' ;

ods pdf text="^10n" ;
ods pdf text="^S={font_size=20pt font_face='Times New Roman' fontweight=bold just=center}Shoes^S={}" ;

ods pdf startpage=now ;
ods proclabel "SHOES" ;
data _null_ ;
	set shoes ;
	by region subsidiary product ;

	if _n_ = 1 then do ;
	declare odsout obj() ;    
	end ; 

	if first.region then do ;    
	  obj.table_start(Label:region, overrides: "frame=void rules=none cellpadding=0in cellspacing=.06in just=left fontweight=bold font_face='Courier New' font_size=12pt") ;
	    obj.body_start() ;        
	      obj.row_start() ;
	        obj.format_cell(text: region, column_span: 2,  overrides: "cellwidth=6.5in backgroundcolor=white just=left fontweight=bold font_face='Courier New' font_size=9pt ") ;
	      obj.row_end() ;
	end ;     
	if first.subsidiary then do ;
	      obj.row_start() ;
	        obj.format_cell(text: subsidiary, column_span: 2, overrides: "marginleft=.25in backgroundcolor=white just=left font_face='Courier New' font_size=9pt ") ;
	      obj.row_end() ; 
	end ;

	obj.row_start() ;
    	obj.format_cell(text: product, column_span: 2, overrides: "marginleft=.50in backgroundcolor=white just=left font_face='Courier New' font_size=9pt ") ;
  	obj.row_end() ;
	obj.row_start() ;
		obj.format_cell(text: 'Stores: ', overrides: "cellwidth=1.5in backgroundcolor=white just=right font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: stores, overrides: "cellwidth=.25in backgroundcolor=white just=left font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: 'Sales: ', overrides: "cellwidth=.50in backgroundcolor=white just=right font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: put(sales,DOLLAR12.) , overrides: "cellwidth=1in backgroundcolor=white just=left font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: 'Inventory: ', overrides: "cellwidth=.75in backgroundcolor=white just=right font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: put(inventory,DOLLAR12.) , overrides: "cellwidth=1in backgroundcolor=white just=left font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: 'Returns: ', overrides: "cellwidth=.75in backgroundcolor=white just=right font_face='Courier New' font_size=9pt") ;
		obj.format_cell(text: put(returns,DOLLAR12.) , overrides: "cellwidth=1in backgroundcolor=white just=left font_face='Courier New' font_size=9pt") ;
	obj.row_end() ;

	if last.region then do ;   
	    obj.body_end() ;
	  obj.table_end() ;
	end ;
run ; 

ods pdf close ;
ods listing ;

Thank you

Cynthia_sas
Diamond | Level 26

Hi:

  When I run your code, I see the bookmark takes me to approximately the first row of data under the region. So for example, Middle East is in the middle of the page and the bookmark takes me to the first row for Boot, as shown below:

_1_bookmark_take_middle_page.png

 

But if I make each region start a new page, then the bookmark takes me to the top of the page:

_2_bookmark_to_top.png

 

I used obj.page(); to insert the page break. If you want to have the region start in the middle of a page like that and change the default behavior so the bookmark takes you to the exact Middle East row (for example), you might need to work with Tech Support.

 

Cynthia

Jerry8989
Calcite | Level 5

Hi Cynthia,

 

Thank you so much for your reply.  

 

I'm unable to do a new page for every main heading in the process I am working on.  In some cases there are 5K or more main headings.

 

The first example is what tricked me to think it worked at first, because it does work in some instances.  Example 2 is the issue I'm having and I need to have it show where the region could be anywhere on the page.  I have to have it go right to the region text and display the main heading. 

 

I've never worked with tech support, is that a pay option?  

 

Thank you

Cynthia_sas
Diamond | Level 26
Hi:
Tech Support is typically free for usage related questions, as long as you can provide us with your site license number. You can contact Tech Support by following the instructions here: https://support.sas.com/en/technical-support.html

Cynthia
Jerry8989
Calcite | Level 5

Thank you Cynthia.  I will report back what I find out.

Jerry8989
Calcite | Level 5

Hi Cynthia,

 

I did get in contact with Tech Support and I'm waiting a response back from them.  In the mean time I went to a SAS user group conference yesterday and they had a session on Proc Report.  Could I use Proc Report to create this same pdf?  I"m starting to work with it and it seems difficult to get data on separate rows and style it like I need too.

 

Thank you,

Jerry

Cynthia_sas
Diamond | Level 26

Hi: Getting the information on different lines, as you show isn't an issue, that part is easy with BY group for REGION and COMPUTE blocks for Subsidiary and Product. The challenge is that the bookmarks work the same way -- as with the RWI -- the link in the bookmark takes you to a point -under- the beginning of that region. I only got a few regions to make the output more manageable:

use_report.png

Clicking Canada took me to the report lines underneath Canada.

 

 

  Cynthia

proc sql ;
create table shoes
as
select region, subsidiary, product, stores, sales, inventory, returns   
from sashelp.shoes
where region in ('Asia' 'Eastern Europe' 'Western Europe' 'Pacific' 'Canada')
order by region, subsidiary, product
;
quit ;
 
ods pdf file='c:\temp\mock_with_rep.pdf' style=journal startpage=no uniform;
proc report data=shoes noheader contents=''
  style(report)={rules=none frame=void};
  by region;
  column region subsidiary product stores;
  define region / group noprint;
  define subsidiary / group noprint;
  define product / group noprint;
  define stores / sum ' ' f=2.0  style(column)={leftmargin=.75in pretext='Stores: '};
  break before region / page contents='';
  compute before subsidiary/ style=Header{color=green just=left leftmargin=.25in};
    line Subsidiary $50.;
  endcomp;
  compute before product / style=Header{color=purple just=left leftmargin=.50in};
    line product $50.;
  endcomp;
run;
ods pdf close;
Jerry8989
Calcite | Level 5

Hi Cynthia,

 

Thank you so much for the information.  It does cause the same issue.  I can't guarantee that the book mark will always be at the start of the page.  I sent my code to SAS Tech Support on Friday.  I hope to hear back from them soon and I will post what they say.

 

Thank you 

hackathon24-white-horiz.png

2025 SAS Hackathon: There is still time!

Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 11 replies
  • 2966 views
  • 0 likes
  • 2 in conversation