PDF Multi-Level Bookmarks via SAS

Similar documents
A SAS Macro Utility to Modify and Validate RTF Outputs for Regional Analyses Jagan Mohan Achi, PPD, Austin, TX Joshua N. Winters, PPD, Rochester, NY

Run your reports through that last loop to standardize the presentation attributes

PharmaSUG China Paper 059

ODS/RTF Pagination Revisit

TLFs: Replaying Rather than Appending William Coar, Axio Research, Seattle, WA

A Macro that can Search and Replace String in your SAS Programs

INTRODUCTION THE FILENAME STATEMENT CAPTURING THE PROGRAM CODE

Compute; Your Future with Proc Report

A Macro to Create Program Inventory for Analysis Data Reviewer s Guide Xianhua (Allen) Zeng, PAREXEL International, Shanghai, China

Developing Data-Driven SAS Programs Using Proc Contents

Post-Processing.LST files to get what you want

Data Edit-checks Integration using ODS Tagset Niraj J. Pandya, Element Technologies Inc., NJ Vinodh Paida, Impressive Systems Inc.

Making an RTF file Out of a Text File, With SAS Paper CC13

Macro Method to use Google Maps and SAS to Geocode a Location by Name or Address

Getting Up to Speed with PROC REPORT Kimberly LeBouton, K.J.L. Computing, Rossmoor, CA

Enhancements to Basic Patient Profiles Scott Burroughs, GlaxoSmithKline, Research Triangle Park, NC

A Macro To Generate a Study Report Hany Aboutaleb, Biogen Idec, Cambridge, MA

An Efficient Method to Create Titles for Multiple Clinical Reports Using Proc Format within A Do Loop Youying Yu, PharmaNet/i3, West Chester, Ohio

A Tool to Compare Different Data Transfers Jun Wang, FMD K&L, Inc., Nanjing, China

ODS DOCUMENT, a practical example. Ruurd Bennink, OCS Consulting B.V., s-hertogenbosch, the Netherlands

One SAS To Rule Them All

Writing Programs in SAS Data I/O in SAS

Cover the Basics, Tool for structuring data checking with SAS Ole Zester, Novo Nordisk, Denmark

Validation Summary using SYSINFO

ADaM Reviewer s Guide Interpretation and Implementation

Implementing external file processing with no record delimiter via a metadata-driven approach

ABSTRACT MORE THAN SYNTAX ORGANIZE YOUR WORK THE SAS ENTERPRISE GUIDE PROJECT. Paper 50-30

Multiple Graphical and Tabular Reports on One Page, Multiple Ways to Do It Niraj J Pandya, CT, USA

Lex Jansen Octagon Research Solutions, Inc.

PharmaSUG Paper PO12

Use That SAP to Write Your Code Sandra Minjoe, Genentech, Inc., South San Francisco, CA

Reading in Data Directly from Microsoft Word Questionnaire Forms

Prove QC Quality Create SAS Datasets from RTF Files Honghua Chen, OCKHAM, Cary, NC

Paper An Automated Reporting Macro to Create Cell Index An Enhanced Revisit. Shi-Tao Yeh, GlaxoSmithKline, King of Prussia, PA

This Text file. Importing Non-Standard Text Files using and / Operators.

Indenting with Style

Quicker Than Merge? Kirby Cossey, Texas State Auditor s Office, Austin, Texas

CC13 An Automatic Process to Compare Files. Simon Lin, Merck & Co., Inc., Rahway, NJ Huei-Ling Chen, Merck & Co., Inc., Rahway, NJ

Sorting big datasets. Do we really need it? Daniil Shliakhov, Experis Clinical, Kharkiv, Ukraine

INTRODUCTION TO SAS HOW SAS WORKS READING RAW DATA INTO SAS

Demystifying Inherited Programs

Essential ODS Techniques for Creating Reports in PDF Patrick Thornton, SRI International, Menlo Park, CA

TLF Management Tools: SAS programs to help in managing large number of TLFs. Eduard Joseph Siquioco, PPD, Manila, Philippines

The Ugliest Data I ve Ever Met

Combining TLFs into a Single File Deliverable William Coar, Axio Research, Seattle, WA

PROBLEM FORMULATION, PROPOSED METHOD AND DETAILED DESCRIPTION

Generating a Detailed Table of Contents for Web-Served Output

The Dataset Diet How to transform short and fat into long and thin

9 Ways to Join Two Datasets David Franklin, Independent Consultant, New Hampshire, USA

Get SAS sy with PROC SQL Amie Bissonett, Pharmanet/i3, Minneapolis, MN

Paper CC16. William E Benjamin Jr, Owl Computer Consultancy LLC, Phoenix, AZ

Using UNIX Shell Scripting to Enhance Your SAS Programming Experience

Omitting Records with Invalid Default Values

Write SAS Code to Generate Another SAS Program A Dynamic Way to Get Your Data into SAS

Utilizing SAS for Cross- Report Verification in a Clinical Trials Setting

Introduction to UNIX. Logging in. Basic System Architecture 10/7/10. most systems have graphical login on Linux machines

LST in Comparison Sanket Kale, Parexel International Inc., Durham, NC Sajin Johnny, Parexel International Inc., Durham, NC

Streamline Table Lookup by Embedding HASH in FCMP Qing Liu, Eli Lilly & Company, Shanghai, China

BI-09 Using Enterprise Guide Effectively Tom Miron, Systems Seminar Consultants, Madison, WI

%check_codelist: A SAS macro to check SDTM domains against controlled terminology

Customized Flowcharts Using SAS Annotation Abhinav Srivastva, PaxVax Inc., Redwood City, CA

Create a Format from a SAS Data Set Ruth Marisol Rivera, i3 Statprobe, Mexico City, Mexico

How a Code-Checking Algorithm Can Prevent Errors

Your Own SAS Macros Are as Powerful as You Are Ingenious

Posters. Paper

Advanced PROC REPORT: Getting Your Tables Connected Using Links

Untangling and Reformatting NT PerfMon Data to Load a UNIX SAS Database With a Software-Intelligent Data-Adaptive Application

Data Quality Review for Missing Values and Outliers

%Addval: A SAS Macro Which Completes the Cartesian Product of Dataset Observations for All Values of a Selected Set of Variables

Creating a Patient Profile using CDISC SDTM Marc Desgrousilliers, Clinovo, Sunnyvale, CA Romain Miralles, Clinovo, Sunnyvale, CA

Check Please: An Automated Approach to Log Checking

SAS System Powers Web Measurement Solution at U S WEST

My Reporting Requires a Full Staff Help!

Regaining Some Control Over ODS RTF Pagination When Using Proc Report Gary E. Moore, Moore Computing Services, Inc., Little Rock, Arkansas

Fly over, drill down, and explore

SAS Data Integration Studio Take Control with Conditional & Looping Transformations

PharmaSUG 2013 CC26 Automating the Labeling of X- Axis Sanjiv Ramalingam, Vertex Pharmaceuticals, Inc., Cambridge, MA

Merging Data Eight Different Ways

Comparison of different ways using table lookups on huge tables

Document and Enhance Your SAS Code, Data Sets, and Catalogs with SAS Functions, Macros, and SAS Metadata. Louise S. Hadden. Abt Associates Inc.

So Much Data, So Little Time: Splitting Datasets For More Efficient Run Times and Meeting FDA Submission Guidelines

A Useful Macro for Converting SAS Data sets into SAS Transport Files in Electronic Submissions

Overview 14 Table Definitions and Style Definitions 16 Output Objects and Output Destinations 18 ODS References and Resources 20

Use SAS/AF, SCL and MACRO to Build User-friendly Applications on UNIX

Using V9 ODS LAYOUT to Simplify Generation of Individual Case Summaries Ling Y. Chen, Rho, Inc., Newton, MA

A Few Quick and Efficient Ways to Compare Data

SAS Drug Development Program Portability

A Cross-national Comparison Using Stacked Data

SAS PROGRAM EFFICIENCY FOR BEGINNERS. Bruce Gilsen, Federal Reserve Board

SAS PROGRAM EFFICIENCY FOR BEGINNERS. Bruce Gilsen, Federal Reserve Board

Bruce Gilsen, Federal Reserve Board

A SAS Macro to Create Validation Summary of Dataset Report

Creating PDF documents including hyper-links, bookmarks and a table of contents with the SAS software. Lex Jansen NV Organon Oss, The Netherlands

Give me EVERYTHING! A macro to combine the CONTENTS procedure output and formats. Lynn Mullins, PPD, Cincinnati, Ohio

Cutting the SAS LOG down to size Malachy J. Foley, University of North Carolina at Chapel Hill, NC

Automatic Indicators for Dummies: A macro for generating dummy indicators from category type variables

For example, let's say that we have the following functional specification:

If You Need These OBS and These VARS, Then Drop IF, and Keep WHERE Jay Iyengar, Data Systems Consultants LLC

Paper CC01 Sort Your SAS Graphs and Create a Bookmarked PDF Document Using ODS PDF ABSTRACT INTRODUCTION

Programming Gems that are worth learning SQL for! Pamela L. Reading, Rho, Inc., Chapel Hill, NC

Transcription:

Paper TS04 PDF Multi-Level Bookmarks via SAS Steve Griffiths, GlaxoSmithKline, Stockley Park, UK ABSTRACT Within the GlaxoSmithKline Oncology team we recently experienced an issue within our patient profile outputs relating to the generation of two-level PDF bookmarks to assist with clinical review; so that each data group listing had a unique bookmark subsetted within each subject. This presentation discusses the evolution of a method to create the desired two-level bookmarks within our patient profiles. INTRODUCTION Like most pharmaceutical companies, we at GSK are trying to make the most of the new techniques and methods for output generation. One of the advantages with SAS ODS is its ability to sort out a lot of the actual page formatting and output directly to PDF. Whilst this automated approach generates a reasonably good styled body for the document, it s quite a different matter when it comes to the PDF bookmarks when using multiple PROC REPORT statements under SAS9.1.3. We were unhappy with the inability to generate multi-level bookmarks especially within our patient profile outputs; preferring to have expandable subject bookmarks with each data group listed as a subset. Until PROC DOCUMENT is extended to deal with PROC REPORT or SAS9.2 is available on our reporting environment with its possible solutions, an alternative method was needed to achieve these bookmarks. This paper will focus on the implementation of a solution for our patient profile output. THE PROBLEM Using SAS ODS, it is very simple to add title information into a top level bookmark to obtain something akin to that shown in figure 1 below, using an ODS PROCLABEL statement code similar to: ods proclabel Subjid = 202: Demography"; Fig. 1: Basic bookmark list as seen in Unix prior to the call to (in this case) PROC REPORT. Of course the subject and titles can be placed within macro variables to obtain all the different bookmarks. 1

A mechanism exists within SAS to add a title to a second level bookmark using the CONTENTS= statement within the PROC REPORT command, but this is not perfect as demonstrated in the following code. Note that a ODS PROCLABEL needs to be executed twice. ods listing close; ods noptitle; ods pdf file='dummy.pdf' pdftoc=2; ods proclabel='subject 1'; data temp1; set dddata.sp_l_ns_l1; if subjid=1; proc report data = temp1 split='~' headline nowd spacing=2 headskip center missing ls=108 ps=43 contents='table1'; column SUBJID country invid invname centreid ptstudy2 ptsubjid prtrtgrp prvdur; define _all_ / display ; define statements removed for clarity data temp1; set dddata.demog_l3_nonstd; if subjid=1; ods proclabel='subject 1'; proc report data = temp1 split='~' headline nowd spacing=2 headskip center missing ls=108 ps=43 contents='table3'; column invid subjid basecp tpernum pertstdt pertendt days; define _all_ / display ; define statements removed for clarity ods pdf close; Figure 2 shows the bookmarks generated by this sample code. Fig. 2: Bookmarks generated by applying second level bookmarks directly. Since we really require the collapsing bookmarks with each subject listing displayed under the main title bookmark as shown in figure 3 it is necessary to determine an alternative approach. 2

Fig. 3: Desired two-level bookmark. Direct manipulation of the PDF file is not possible but the relevant information can be appended to a Postscript file in the form of language statements, which when processed using PS2PDF are converted to bookmarks. In order to achieve this a couple pieces of information are needed for each bookmark; namely the current page and for the parent bookmark the number of children. The statements needed for parent and children take the following forms respectively: [/Title (Parent Title) /Count number of children /Page First Page /View [/XYZ value value value] /OUT pdfmark [/Title (Child Title) /Page First Page /View [/XYZ value value value] /OUT pdfmark The final pdfmark statement is used to represent PDF features. FIRST APPROACH Initial development was restricted to a specific number of reports so the number of children could be simply hardcoded. Therefore the issue was dealing with the page numbering. Since each component listing could be any length in page size it was necessary to determine how many pages were actually generated for each report. The first versions of the procedure used differing Page X of Y style macros within PROC REPORT to determine the number of pages (and adding this information to a main page count variable) and were met with varying degrees of success. Ultimately it wasn t long before the 3

bookmark and associated page failed to correspond to each other due to differences between the ASCII version of the listing and the ODS formatted version. SECOND APPROACH During the development of the first approach it was discovered that the page information was present within the Postscript file, and so there must be a way to extract and utilize it within an ongoing page count. Using a Unix pipe to pass a search string through an output file it s possible to obtain the number of pages produced by an individual report. filename pages pipe "grep -i %%pages: outfile.ps"; infile pages lrecl=40 pad end=eof; input text $20.; if eof then call symput('page',trim(left(scan(text,2,' ')))); %let _page_ = %eval(&_page_ + &page); Using a global macro variable (&_page_ in this case) it s then possible to keep a record of the total page count which can be passed to the Postscript file. It is necessary to append the complied bookmark information onto the finalized Postscript file, so another file is needed to keep track of the commands to be added. In order to reduce the amount of external files needed a SAS catalog source was used to accomplish this. This catalog was then included and the commands executed to write directly to the Postscript file. filename outfile CATALOG "work._outfile" mod; %let _page_ = 1; Contained within the loop for each subject / Create the SAS command which will create the parent bookmark. / Note for the first subject, &_PAGE_ will be 1. / Again working on a pre-defined list of reports 14 in this case /---------------------------------------------------------------------------------*/ file outfile(bookmark.source) linesize=10000; mark="put ""[/Title (&g_subjid = &&subjid&j) /Count 14 /Page &_page_ /View [/XYZ 103 775 null] /OUT pdfmark"";"; put mark; Contained within the loop for each report / Create the SAS command which will create the child bookmark. / Note for the first subject/report, &_PAGE_ will be 1 since we have not performed / the search for %%page. /---------------------------------------------------------------------------------*/ file outfile(bookmark.source) linesize=10000; mark="put ""[/Title (&xdatagroup) /Page &_page_ /View [/XYZ 144.6 116.68 null] /OUT pdfmark"";"; put mark; This particular sub-loop would then generate the report, perform the string search to determine the number of pages generated; update the rolling page count and end. Processing on the next report would start with the updated &_PAGE_ value passing into the next bookmark command. 4

Initialise Select report Select Subject Write Child Write Parent Create actual listing Determine # of pages and update _PAGE_ Last Report NO YES NO Last Subject Fig. 4: Flow chart for process The last remaining problem to solve was how to filter off a copy of the current report to compute the pages whilst simultaneously keeping it in the main file. Luckily it is possible to open multiple file destinations in ODS PRINTER with the use of the ID= identifier demonstrated below: As part of the initialisation: ods listing close; ods escapechar= \ ; ods ps (id=main) file= outfile.ps style=styles.rtf uniform; Within the actual reporting loop: ods ps (id=temp) file= tmp_rep.ps style=styles.rtf startpage=yes uniform; The report is then created in both output destinations. Immediately after close the id=temp output file: ods ps (id=temp) close; Rest of process The next time the inner reporting loop executes, a new temp ODS PRINTER destination is established and it is then possible to determine the pages for the newer report. Whilst this method is successful only one summary output is produced per page, which did not meet the requirement of multiple outputs per page. YES 5

THIRD APPROACH Due to the fact that each listing could flow over multiple pages or indeed only take up a few lines, it seems reasonable to allow SAS to actually deal with the formatting problem of how many outputs per page whilst applying the constraint of starting a new page for each subject. The method of filtering off a copy of the current report would not work since it would never be known when page breaks were going to be introduced, so it s back to producing the Postscript file in its entirety first. In the previous method, searching the individual Postscript files gave information on the total number of pages produced for each report, but searching the entire file gives you the page(s) on which any report can be found. It is therefore necessary to create a data set with the page number for the first occurrence of any given report for each subject, yet allowing multiple instances of that page number if several reports are present on the page in question so additional variables are needed. Through the use of header information supplied through the TITLE statement it is also possible to obtain subject information. The actual report title is harder to obtain since it is effectively free text and impossible to search for, unless prefixed by an unique character sequence in this case a double hash (##). Modifying the earlier Unix pipe to search for these multiple strings %let txt_string = egrep i page: subject: ## outfile.ps; filename pages pipe &txt ; and post processing the resultant dataset it is possible to obtain something akin to a table of contents for the entire report which in turn can be parsed into the necessary commands needed for the Postscript file. / Using this create a data set with the page numbers associated with each datagroup / for each subject. /----------------------------------------------------------------------------------*/ data info(drop=dummy text start stop ); infile pages linesize=5000 lrecl=5000 truncover dlm=','; length text $2500; informat text $varying5000.; input text ; n=_n_; length subjid 8 page 8 title dummy $200 centid $8; retain subjid page &g_centid; Obtain PAGE information */ if index(upcase(text),'%%page:') then page = input(trim(left(scan(text,2,' %'))),best.); Obtain SUBJECT and CENTRE information */ Use the fact that text with Postscript file enclosed within ( ) to search for */ desired values */ if indexw(upcase(text),'subject:') then do; dummy=scan(text,2,'()'); subjid=input(trim(left(scan(dummy,-1,' ():'))),best.); centid=trim(left(scan(dummy,-3,' '))); end; Obtain TITLE information */ if index(upcase(text),'##') then do; start=index(upcase(text),'##'); stop=index(upcase(text),')'); title=substr(text,start+2, stop-(start+2)); end; if title ne ' ' and index(title,'####') eq 0; 6

/ keep first occurrence (first page) of datagroup / title /---------------------------------------------------------------------------------*/ proc sort data=info; by centid subjid title page ; data info; set info; by centid subjid title page; if first.title; One of the consequences of allow SAS to sort out the page formatting is that text wrapping can occur within the title (in order to fit the table width), and as such the complete title may not be easy to locate using the search. Therefore occasionally there can be a few reports which end up appearing to have the same title when truncated. This is particularly true producing the null report when there is no data to present. Luckily this can be prevented with the inclusion of a STYLE to the DEFINE statement within PROC REPORT. define none / display center style=[cellwidth=13cm]; However there may still be instances where the title, although unique is still too long for the table width, and the entire title is needed for the bookmark. To deal with this, a master bookmark data set containing subject and title information is complied during the reporting loop which is merged with the info data set. It s now possible to determine the number of child bookmarks, which should be the same for all subjects, and when used in conjunction with the merged bookmarked data set the set of commands for the Postscript file can be complied, and appended to the actual Postscript file. / Create file of SAS commands to place PS bookmark code into PS file / Using a -ve number in the count will automatically collapse all the bookmarks. /---------------------------------------------------------------------------------*/ filename outfile bookmark.txt" mod; filename outfile2 "outfile.ps" mod; set _temp_; by centid &g_subjid page n; nlistings=listings * -1; file outfile; if first.&g_subjid then do; put "put""[/title (Centre = "g_centid " Subject = " subjid ")/Count " nlistings "/Page " page "/View [/XYZ 103 775 null] /OUT pdfmark"";"; end; put "put""[/title (" title ") /Page " page "/View [/XYZ 144.6 116.68 null] /OUT pdfmark"";"; / Run SAS generated code appending bookmarks to PS file /--------------------------------------------------------------------------------*/ file outfile2; %inc outfile; The file is then converted and housekeeping performed to remove the external files. x ps2pdf "outfile.ps" "outfile.pdf"; 7

x rm "outfile.ps"; x rm bookmark.txt"; LIMITATIONS When using this approach on a large subject population having a considerable number of listings, there have been a few occasions when the program has aborted due to memory issues. The easiest solution to this has been to create subject subgroups and create several patient profile listings. CONCLUSION This paper has shown that it is possible to create parent and multiple child bookmarks in PDF using SAS, and whilst there are solutions available which use Java to manipulate files, it was necessary to create a method which would worked within the GSK regulated reporting system. Although this paper deals with the implementation of a solution for a specific problem within GSK Oncology, the fundamentals should be easily transferable to create multiple level bookmarks for other scenarios. Hopefully when support for PROC REPORT is provided within PROC DOCUMENT this sort of file manipulation will not be needed. REFERENCES Chung, Chang Y. and Dunn, Toby (2005), Page X of Y with Proc Report. Paper CC31. Proceedings of the Pharmaceutical Industry SAS Users Group Conference 2005 http://www.lexjansen.com/pharmasug/2005/coderscorner/cc31.pdf Jansen, Lex. (2001), Creating PDF Documents including Links, Bookmarks and a Table of Contents with the SAS Software. Paper FDA05. Proceedings of the Pharmaceutical Industry SAS Users Group Conference 2001. http://www.lexjansen.com/pharmasug/2001/proceed/fdacomp/fda05_jansen.pdf Karunasundera, Tikiri. (2006), Creating and Modifying PDF Bookmarks SAS Conference Proceedings: Western Users of SAS Software 2006 http://www.lexjansen.com/wuss/2006/data_presentation_and_business_intelligence/dpr-karunasundera.pdf CONTACT INFORMATION Your comments and questions are valued and encouraged. Contact the author at: Steve Griffiths GlaxoSmithKline 1-3 Iron Bridge Road Stockley Park West Uxbridge Middlesex UB11 1BU stephen.j.griffiths@gsk.com SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. indicates USA registration. Other brand and product names are registered trademarks or trademarks of their respective companies. 8