Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Using MATA to create multiple PDF's by using for (correctly)?

    Hi, in Stata 14, it is now possible to create a PDF using mata, something I appreciate very much.
    But, I am struggling to get the 'basics' to work. For example, with this example code, it is possible to create multiple file names and display them in Stata's result window:
    Code:
    global filepath "C:\YourFolder"
    mata
    mata clear
    text = "a","b","c"
    pdft = ".pdf"
    for(i=1;i<=length(text);++i) {
    myname = "$filepath`'\Example_file_"+text[i]+pdft[1]
    myname
    }
    end
    or
    Code:
    global filepath "C:\YourFolder"
    mata
    mata clear
    pdft = ".pdf"
    for (i=1; i<=3; i++) {
    myname = "$filepath`'\Example_file_"+sprintf("%02.0f",i)+pdft[1]
    myname
    }
    end
    The above two examples are fine for me because the string of text is like "filepath_filename`i'.pdf".

    Now I want to implement this in mata code so that multiple PDF's are created. I tried:
    Code:
    global filepath "C:\YourFolder"
    mata
    mata clear
    pdft = ".pdf"
    for (i=1; i<=10; i++) {
    pdf = PdfDocument()
    pdf.setPageSize("A4")
    pdf.save("$filepath`'\Example_file_"+sprintf("%02.0f",i)+pdft[1])
    pdf.close()
    }
    end
    But, this results in errors:
    type mismatch: exp.exp: transmorphic found where struct expected
    expression invalid
    Probably, I am too ignorant working with mata to get this right. So, any help is much appreciated.
    Last edited by ericmelse; 26 May 2015, 09:53.
    http://publicationslist.org/eric.melse

  • #2
    You need restructure your for loop into a function in order to use Mata class and Mata structure inside the loop. The code would look like the following

    Code:
    global filepath "C:\YourFolder"
    clear mata
    mata:
    
    void writepdf(real scalar n)
    {    
        class PdfDocument pdf
        real scalar i
        string scalar fname
        for (i=1; i<=n; i++) {
            pdf = PdfDocument()
            pdf.setPageSize("A4")
            fname = "$filepath`'\Example_file_"+sprintf("%02.0f",i)+ ".pdf"
            fname
            pdf.save(fname)
            pdf.close()
        }
    }
    
    writepdf(10)
    
    end
    The reason Mata class does not work in a loop interactively is the same as Bill Gould's explanation in the following thread

    http://www.statalist.org/forums/foru...anding-structs

    Comment


    • #3
      Many thanks Hua, this indeed 'works'.
      But now I want to include some content (from the example in the help file):
      Code:
      clear mata
      mata:
      
      void writepdf(real scalar n)
      {    
          class PdfDocument pdf
          real scalar i
          string scalar fname
          for (i=1; i<=n; i++) {
              pdf = PdfDocument()
              pdf.setPageSize("A4")
      
              p = PdfParagraph()
              p.addString("This is our first example of a paragraph. ")
              p.addString("Let's add another sentence. ")
              p.addString("Now we will conclude our first paragraph.")
              pdf.addParagraph(p)
      
              fname = "$filepath`'\Example3_file_"+sprintf("%02.0f",i)+ ".pdf"
              fname
              pdf.save(fname)
              pdf.close()
          }
      }
      
      writepdf(10)
      
      end
      Clearly, it is not 'that simple' as this gives another error.
      type mismatch: exp.exp: transmorphic found where struct expected
      (13 lines skipped)

      Possibly, you explain how this works.
      http://publicationslist.org/eric.melse

      Comment


      • #4
        You need declare the variable p in the function. Add the following line should fix the problem:
        Code:
        class PdfParagraph scalar p
        The entire code would look like:

        Code:
        global filepath "C:\YourFolder"
        clear mata
        
        mata:
        
        void writepdf(real scalar n)
        {    
            class PdfDocument scalar pdf
            real scalar i
            string scalar fname
            class PdfParagraph scalar p
            
            for (i=1; i<=n; i++) {
                pdf = PdfDocument()
                pdf.setPageSize("A4")
        
                p = PdfParagraph()
                p.addString("This is our first example of a paragraph. ")
                p.addString("Let's add another sentence. ")
                p.addString("Now we will conclude our first paragraph.")
                pdf.addParagraph(p)
        
                fname = "$filepath`'\Example3_file_"+sprintf("%02.0f",i)+ ".pdf"
                fname
                pdf.save(fname)
                pdf.close()
            }
        }
        
        writepdf(10)
        
        end

        Comment


        • #5
          Dear Hue, many thanks for your explanation. I have exercised your example and it works fine.
          I continued with the code, so that a table is included in the PDF file (to be created) and insert (two) images into a cell of this table. First, I added a new class for the table with:
          Code:
          class PdfTable scalar t
          Next, I added the code for table, note that an image called "test.jpg" is expected to be in the working folder:
          Code:
          global filepath "C:\YourFolder"
          clear mata
          
          mata:
          
          void writepdf(real scalar n)
          {    
              class PdfDocument scalar pdf
              real scalar i
              string scalar fname
              class PdfParagraph scalar p
              class PdfTable scalar t
                  
              for (i=1; i<=n; i++) {
                  pdf = PdfDocument()
                  pdf.setPageSize("A4")
          
                  p = PdfParagraph()
                  p.addString("This is our first example of a paragraph. ")
                  p.addString("Let's add another sentence. ")
                  p.addString("Now we will conclude our first paragraph.")
          
                  pdf.addParagraph(p)
                  t = PdfTable()
                  t.init(2,1)
                  t.setWidthPercent(.55)    //
                  p.setFont("Consolas")    // Courier New
                  t.setHAlignment("center")
                  t.setCellContentImage(1,1,"$filepath/test.jpg")
                  t.setCellContentImage(2,1,"$filepath/test.jpg")
                  t.setCellVAlignment(1,1,"bottom")
                  t.setCellVAlignment(2,1,"bottom")
                  t.setBorderWidth(0)
                  pdf.addTable(t)
                  
                  fname = "$filepath`'\Example3_file_"+sprintf("%02.0f",i)+ ".pdf"
                  fname
                  pdf.save(fname)
                  pdf.close()
              }
          }
          
          writepdf(10)
          
          end
          This works just fine.

          Now my (new) question is, how to use a string (vector) of names (words) to be included in the text of the document that cycles through together with the number of PDF's to be created.
          For example, I want to create 3 PDF's (writepdf(3)) and use the names "Jones1 Jones2 Jones3" in consecutive order.
          In regular Stata syntax I would use [tokenize Jones1 Jones2 Jones3], and make reference to it using [1] and the [mac shift] command within syntax controlled by forvalue i = 1(1)3 { }.
          I have studied the mata manual and some other documents but I am not able to get this right.
          So, possibly you can show me how to accomplish this.
          http://publicationslist.org/eric.melse

          Comment


          • #6
            The following code shows how to generate a string vector with a given prefix

            Code:
            cscript
            
            mata:
            
            /* example 1: construct Jones1 Jones2 Jones3 .. in consecutive order */
            void string_vec1(real scalar n, string scalar prefix)
            {
                real scalar i
                string scalar name
                
                for(i=1; i<=n; i++) {
                    name = prefix+sprintf("%g", i)
                    name
                }
            }
            
            string_vec1(3, "Jones")
            string_vec1(4, "Joe")
            end
            The next example simply uses a passed in string vector to archive the same effect. Although it does a bit of checking to make sure the loop accesses the elements in the string vector within its bounds:

            Code:
            cscript
            
            mata:
               
            /* example 2: construct name based on a given string vector */
            void string_vec2(real scalar n, string rowvector sv)
            {
                real scalar i, dim
                string scalar name
            
                dim = cols(sv)
                for(i=1; i<=n && i<=dim; i++) {
                    name = sv[i]
                    name
                }
            }
            
            string_vec2(3, ("Jones1", "Jones2", "Jones3"))
            string_vec2(3, ("quick", "brown", "fox", "jump"))
            
            end

            Comment


            • #7
              Dear Hue, many thanks again. I have explored your suggestions and implemented what I need in a "mo" file with:
              Code:
              clear mata
              mata:
              // mosave the function in the current (PERSONAL) working directory
              void string_vec2(real scalar n, string rowvector sv)
              {
                  real scalar i, dim
                  string scalar name
              
                  dim = cols(sv)
                  for(i=1; i<=n && i<=dim; i++) {
                      name = sv[i]
                      name
                  }
              }
              
              mata mosave string_vec2(), replace
              
              end
              Next, (after exiting Stata and starting Stata again, change to the (PERSONAL) working directory), I can run:
              Code:
              mata
              string_vec2(4, ("quick", "brown", "fox", "jump"))
              end
              This leads to the display in Stata's result window of :
              quick
              brown
              fox
              jump

              which proves that the mata function works as expected, i.e. it prompts each string consecutively.

              Now I want to use the function string_vec2() in the code that is required to create the PDF, like:
              Code:
              global filepath "C:\YourFolder"
              
              clear mata
              mata:
              void writepdf(real scalar n) {
                  class PdfDocument pdf
                  real scalar i
                  string scalar fname
                  class PdfParagraph scalar p
                  string scalar string_vec2
                  
                  for (i=1; i<=n; i++) {
                      pdf = PdfDocument()
                      p = PdfParagraph() 
                      t = PdfText()
                      p.addString("This is some text on the count of "+sprintf("%01.0f",i))
                      p.addString("+string_vec2(4, ("quick", "brown", "fox", "jump"))")
                      p.addText(t)
                      pdf.addParagraph(p)
                      
                      fname = "$filepath`'\Example_file_"+sprintf("%02.0f",i)+".pdf"
                      fname
                      pdf.save(fname)
                      pdf.close()
                  }
              }
              writepdf(4)    // set the number instances
              end
              Note that I have defined:
              Code:
                  string scalar string_vec2
              before the for loop, which includes:
              Code:
              p.addString("+string_vec2(4, ("quick", "brown", "fox", "jump"))")
              However, this codes leads to the error:
              invalid expression
              Clearly, I am doing something wrong here, although when I delete the line [p.addString("+string_vec2(4, ("quick", "brown", "fox", "jump"))")] the code will execute as expected.
              So, maybe it is something simple, and can you explain what is required to call the function string_vec2 properly within the for loop that is used to create the PDF.
              http://publicationslist.org/eric.melse

              Comment


              • #8

                string_vec2() is an example to show how to use string vector in Mata. You would not want to plug it in your pdf generating code directly. The following code might do what you want but is still just an example:

                Code:
                cscript
                global filepath "C:\YourFolder"
                
                clear mata
                mata:
                void writepdf(real scalar n, string rowvector sv) {
                    class PdfDocument pdf
                    real scalar i, dime
                    string scalar fname
                    class PdfParagraph scalar p
                    
                    dim = cols(sv)
                    for (i=1; i<=n && i<=dim; i++) {
                        pdf = PdfDocument()
                        p = PdfParagraph()
                        t = PdfText()
                        p.addString("This is some text on the count of "+sprintf("%01.0f",i))
                        p.addString(" "+sv[i])
                        pdf.addParagraph(p)
                        
                        fname = "$filepath`'\Example_file_"+sprintf("%02.0f",i)+".pdf"
                        fname
                        pdf.save(fname)
                        pdf.close()
                    }
                }
                writepdf(4, ("quick", "brown", "fox", "jumps"))
                end
                Last edited by Hua Peng (StataCorp); 29 May 2015, 12:43.

                Comment


                • #9
                  In the code section of the above post
                  Code:
                  real scalar i, dime
                  should be
                  Code:
                  real scalar i, dim

                  Comment


                  • #10
                    Dear Hua, yes this works fine. I suppose, when this is just one solution, it will be interesting to learn about alternatives (e.g. getting strings by case from a dta variable). But, I will try to find examples of that elsewhere. Many thanks for answering my questions and I am excited to see that mata can be used in this manner as a PDF report generator.
                    http://publicationslist.org/eric.melse

                    Comment

                    Working...
                    X