Hello Statalist,

I am trying to use putpdf to convert a surveyCTO-formatted survey into pdf, but I am receiving a pair of formatting errors whose cause I cannot understand. The surveyCTO spreadsheets has the following (relevant) variables (I cannot share the real data for all of the fields, but I have included it for what I think are the most relevant fields):

Code:
* Example generated by -dataex-. To install: ssc install dataex
clear
input int index str29 type str24 name str2045 label str297 constraint str176 relevance str3 required str1440 choices str225 values
201 "select_one iga_paid"       "q642"              "label201" ""                   "q640 = 1"                                              "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
202 "text"                      "q642_confirmation" "label202" ""                   "q642 = 2"                                              ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
203 "begin group"               "F6_1"              "label203" ""                   "q642 != 2"                                             ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
204 "note"                      "f61note1"          "label204" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
205 "select_one month"          "q643_mm"           "label205" ""                   ""                                                      "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
206 "integer"                   "q643_yyyy"         "label206" ". >1990 and .<2016" ""                                                      "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
207 "end group"                 "F6_1"              "label207" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
208 "select_one yesno"          "q646"              "label208" ""                   "q642 = 1 or q640 != 1"                                 "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
209 "select_one yesno"          "q646b"             "label209" ""                   "q646 = 0 and q640 = 3"                                 "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
210 "select_one yesno"          "q647"              "label210" ""                   "q646 = 1"                                              "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
211 "begin group"               "F6_4"              "label211" ""                   "(q646 = 0  and q640 != 3) or (q646b = 0 and q640 = 3)" ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
212 "begin group"               "F6_4a"             "label212" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
213 "note"                      "f64note1"          "label213" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
214 "select_one month"          "q648_mm"           "label214" ""                   ""                                                      "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
215 "integer"                   "q648_yyyy"         "label215" ". >1990 and .<2016" ""                                                      "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
216 "end group"                 "F6_4a"             "label216" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
217 "select_one stopcodes"      "q649a"             "label217" ""                   ""                                                      "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
218 "text"                      "q649b"             "label218" ""                   "q649a = 16"                                            "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
219 "end group"                 "F6_4"              "label219" ""                   ""                                                      ""    `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
220 "integer"                   "q650"              "label220" ".>0 and .<140"      "q647 = 1"                                              "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
221 "select_one yesno"          "q650a"             "label221" ""                   "q647 = 1"                                              "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
222 "integer"                   "q651"              "label222" ""                   "q641 = 1 and (q646b = 1 or q646 = 1)"                  "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
223 "integer"                   "q652"              "label223" ""                   "q641 = 2 and (q646b = 1 or q646 = 1)"                  "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
224 "select_one iga_incomefreq" "q653"              "label224" ""                   "(q642 = 1 or q640 = 2) and q646 = 1"                   "yes" `" "choice1" "choice2" "choice3" "' `" "value1" "value2" "value3" "'
end

To create the pdf, I loop over the values of index applying a program I wrote called formatquestion:

Code:
// import
use "C:\Users\jduggan\Desktop\test_survey.xlsx", clear

global question_count = 0

putpdf clear
    putpdf begin
    qui sum index
    forvalues i = 1/`r(max)' {            
        preserve
            qui keep if index == `i'
            formatquestion
        restore
    }
    putpdf save "C:\my\directory\path.pdf", replace
The global question_count is there for me to keep track of how many questions have been formatted throughout the entire loop.

The function formatquestion drops all observations except the one with index == i and then creates a set of locals containing the values of the variables for the observation with index == i. Having created and cleaned these locals, it then creates a set of putpdf tables/paragraphs that arrange the locals in a fashion that depends upon the value of the type variable:

Code:
    cap prog drop formatquestion
    prog def formatquestion
    
        // define fundamental locals
        local ctovars index type name label hint constraint relevance required choices values
        foreach var in `ctovars' {
             qui levelsof `var', loc(`var') clean
        }
                
        // clean up locals
      
            // relevance
            capture confirm e `relevance'
            if !_rc {
                loc relevance_details = `"`relevance'"'
            }
            else {
                loc relevance_details = "all"
            }
    
            // constraint
            capture confirm e `constraint'
            if !_rc {
                loc constraint_details = `"`constraint'"'
            }
            else {
                loc constraint_details = "none"
            }
            
            // required
            capture confirm e `required'
            if !_rc {
                loc required_details = upper(`"`required'"')
            }
            else {
                loc required_details = `"NO"'
            }
            
            // hint
            capture confirm e `hint'
            if !_rc {
                loc hint_details = (`"  (Hint: `hint')"')
            }
            else {
                loc hint_details
            }
            
            // labels
            capture confirm e `label'
            if !_rc {                    
                loc label_details = "`label'"
            }
            else {
                loc label_details = "THIS QUESTION HAS NO TEXT"
            }

            // choices
            capture confirm e `choices'
            if !_rc {
                loc choice_count : word count `choices'
            }
            else {
                loc choice_count
            }

        // define and perform formatting for each type of question
                        
            // TEXT and INTEGER
            if "`type'" == "text" | "`type'" == "integer" {
                
                di "`name'"
                di "`relevance_details'"
                di "`constraint_details'"
                di "`required_details'"
                
                // update question counter
                global question_count = $question_count + 1            
                
                // put in pdf
                                
                    // define cell containing question name, type
                    putpdf table nt`index' = (2,1), border(all, nil) memtable
                    putpdf table nt`index'(1,1) = ("$question_count. `name'"), bold
                    putpdf table nt`index'(2,1) = ("`type'")
               di "debug here # 1"          
                    
                    // define cell containing relevance, constraints, and required status
                    putpdf table auxt`index' = (3,1), border(all, nil) memtable
              di "debug here #2"
                    putpdf table auxt`index'(1, 1) = ("Relevance: `relevance_details'")
                    putpdf table auxt`index'(2, 1) = ("Constraints: `constraint_details'")    
                    putpdf table auxt`index'(3, 1) = ("Required: `required_details'")
                    
                    // created displayed table
                    putpdf table t`index' = (1, 7), border(all, nil)
                    putpdf table t`index'(1,1) = table(nt`index')
                    putpdf table t`index'(1,2) = ("`label_details'`hint_details'"), colspan(4)
                    putpdf table t`index'(1,6) = table(auxt`index'), colspan(2)
            }
            
            // SELECT ONE AND MULTI
            if strpos("`type'", "select_one") > 0 | strpos("`type'", "select_multi") > 0 {

                // update question counter
                global question_count = $question_count + 1

                // define locals
                if strpos("`type'", "select_one") > 0 {
                    loc qtype = "select_one"
                }
                else {
                    loc qtype = "select_multi"
                }
            
                // put in pdf
            
                    // define cell containing question name, type
                    putpdf table nt`index' = (2,1), border(all, nil) memtable
                    putpdf table nt`index'(1,1) = ("$question_count. `name'"), bold
                    putpdf table nt`index'(2,1) = ("`type'")                    
                    
                    // define cell containing question, hint, choices, and values
                        
                        // define subcell containing choices and values
                        putpdf table cv`index' = (`choice_count', 1), border(all, nil) memtable
                        forvalues j = 1 / `choice_count' {
                            loc ch: word `j' of `choices'
                            loc v: word `j' of `values'
                            putpdf table cv`index'(`j', 1) = ("`ch'  = `v'")
                        }
                        
                        // create cell combining label/hint with choices/values
                        putpdf table lhcv`index' = (2, 1), border(all, nil) memtable
                        putpdf table lhcv`index'(1, 1) = ("`label_details'`hint_details'")
                        putpdf table lhcv`index'(2, 1) = table(cv`index')
                        
                    // define cell containing relevance, constraints, and required status
                    putpdf table auxt`index' = (3, 1), border(all, nil) memtable
                    putpdf table auxt`index'(1, 1) = ("Relevance: `relevance_details'")
                    putpdf table auxt`index'(2, 1) = ("Constraints: `constraint_details'")        
                    putpdf table auxt`index'(3, 1) = ("Required: `required_details'")
                    
                    // created displayed table
                    putpdf table t`index' = (1, 7), border(all, nil)
                    putpdf table t`index'(1,1) = table(nt`index')
                    putpdf table t`index'(1,2) = table(lhcv`index'), colspan(4)
                    putpdf table t`index'(1,6) = table(auxt`index'), colspan(2)
                
            }
  end
The code runs smoothly for several seconds before I am met with the error "failed to set table border r(198); end of do-file r(198);." The error always occurs in the same place -- it will run until the input given is (here, I use real data):

Code:
* Example generated by -dataex-. To install: ssc install dataex
clear
input int index str29 type str24 name str2045 label str297 constraint str176 relevance str3 required str1440 choices str225 values
244 "text" "q661c1" "Other (specify)" "" "selected(q661c, '10')" "yes" "" ""
end
And more specifically, the line in the code that causes the problem appears to be "putpdf table auxt`index' = (3,1), border(all, nil) memtable" because the last line to run properly is "di "debug here # 1"". This is the first problem.

The second problem occurs when I exclude the entire " // SELECT ONE AND MULTI" section from the questionformat function above; my loop will now not perform any putpdf commands when type is equal to select_one or select_multi. I also move around the debugging messages to identify the location of this second problem. To be clear, the formatquestion function now reads:


Code:
    cap prog drop formatquestion
    prog def formatquestion
    
        // define fundamental locals
        local ctovars index type name label hint constraint relevance required choices values
        foreach var in `ctovars' {
             qui levelsof `var', loc(`var') clean
        }
                
        // clean up locals
      
            // relevance
            capture confirm e `relevance'
            if !_rc {
                loc relevance_details = `"`relevance'"'
            }
            else {
                loc relevance_details = "all"
            }
    
            // constraint
            capture confirm e `constraint'
            if !_rc {
                loc constraint_details = `"`constraint'"'
            }
            else {
                loc constraint_details = "none"
            }
            
            // required
            capture confirm e `required'
            if !_rc {
                loc required_details = upper(`"`required'"')
            }
            else {
                loc required_details = `"NO"'
            }
            
            // hint
            capture confirm e `hint'
            if !_rc {
                loc hint_details = (`"  (Hint: `hint')"')
            }
            else {
                loc hint_details
            }
            
            // labels
            capture confirm e `label'
            if !_rc {                    
                loc label_details = "`label'"
            }
            else {
                loc label_details = "THIS QUESTION HAS NO TEXT"
            }

            // choices
            capture confirm e `choices'
            if !_rc {
                loc choice_count : word count `choices'
            }
            else {
                loc choice_count
            }

        // define and perform formatting for each type of question
                        
            // TEXT and INTEGER
            if "`type'" == "text" | "`type'" == "integer" {
                
                di "`name'"
                di "`relevance_details'"
                di "`constraint_details'"
                di "`required_details'"
                
                // update question counter
                global question_count = $question_count + 1            
                
                // put in pdf
                                
                    // define cell containing question name, type
                    putpdf table nt`index' = (2,1), border(all, nil) memtable
                    putpdf table nt`index'(1,1) = ("$question_count. `name'"), bold
                    putpdf table nt`index'(2,1) = ("`type'")
                    
                    // define cell containing relevance, constraints, and required status
                    putpdf table auxt`index' = (3,1), border(all, nil) memtable
                    putpdf table auxt`index'(1, 1) = ("Relevance: `relevance_details'")
                    putpdf table auxt`index'(2, 1) = ("Constraints: `constraint_details'")    
                    putpdf table auxt`index'(3, 1) = ("Required: `required_details'")

              di "debug here # 3"          
                    // created displayed table
                    putpdf table t`index' = (1, 7), border(all, nil)
              di "debug here #4"
                    putpdf table t`index'(1,1) = table(nt`index')
                    putpdf table t`index'(1,2) = ("`label_details'`hint_details'"), colspan(4)
                    putpdf table t`index'(1,6) = table(auxt`index'), colspan(2)
            }
          
    end
As above, the code runs smoothly through many observations before hitting an error. The error is slightly different from the one above. It reads, "failed to add table r(198); end of do-file r(198);"
The last line to run properly is "di "debug here # 3" ", and so the cause of the problem appears to be "putpdf table t`index' = (1, 7), border(all, nil)". The observation that causes this problem is:

Code:
* Example generated by -dataex-. To install: ssc install dataex
clear
input int index str29 type str24 name str2045 label str297 constraint str176 relevance str3 required str1440 choices str225 values
180 "text" "q633_other" "Other(Specify)" "" "q633=5" "yes" "" ""
end
I found a similar problem mentioned by a user on stackexchange: https://stackoverflow.com/questions/...g-putpdf-stata
The solution offered -- to remove the "bold" option from your tables, prevents this error from occurring. Specifically, I remove the bold option from the line.

Code:
putpdf table nt`index'(1,1) = ("$question_count. `name'"), bold
I would, however, like to be able to bold these words if possible. In any case, I would very much appreciate any guidance here.

Cheers,

Julian