"Failed to add table" error using putpdf (Stata) - syntax-error

Note: I have a workaround, so this is only an attempt to understand the error.
Summary: I get a syntax error (failed to write table) when using putpdf. The error seems to be connected to using the bold option, but disappears when this option is replaced or erased.
I am trying to write a pdf with a series of tables using putpdf. This is a new built-in command (Stata 15 only), so I can't tell if what I am experiencing is a bug or a problem with the way I have formatted things. Unfortunately, this issue is impossible to replicate without the data I am using: The same general idea using sysuse auto couldn't replicate, so this is a weird one.
Start by adding in the data (this is from an ongoing survey I am checking daily, so I have trimmed it down to the parts that are replicating an error and replaced names with dummy names):
clear
cd [DIRECTORY] // Change this to yours
input str70 label str6 district str11 ta str23 village str27 cluster float(sdate coupons) byte shopid str33 reason
"Amosi - 1000001 - First Last (MARK-01-001)" "Mwan" "Kand" "Amosi" "First Last 22" 21266 . 13 "Not Yet Replaced"
"Amosi - 1000002 - First Last (MARK-01-001)" "Mwan" "Kand" "Amosi" "First Last 22" 21266 . 13 "Not Yet Replaced"
"Bello - 1000003 - First Last (MARK-01-001)" "Neno" "Damb" "Bello" "First Last 2" 21266 1 11 "Not Yet Replaced"
"Bello - 1000004 - First Last (MARK-01-001)" "Neno" "Chek" "Bello" "First Last 5" 21266 . 11 "Not Yet Replaced"
"Bello - 1000005 - First Last (MARK-01-001)" "Neno" "Damb" "Bello" "First Last 24" 21266 . 11 "Not Yet Replaced"
"Benalita - 1000006 - First Last (MARK-01-001)" "Neno" "Damb" "Benalita" "First Last 7" 21260 1 12 "Not Yet Replaced"
"Chakhumbira - 1000007 - First Last (MARK-01-001)" "Neno" "Damb" "Chakhumbira" "First Last 28" 21269 . 8 "Not Yet Replaced"
"Chapita - 1000008 - First Last (MARK-01-001)" "Neno" "Mlau" "Chapita" "First Last 32" . 1 1 ""
"Chapita - 1000009 - First Last (MARK-01-001)" "Neno" "Mlau" "Chapita" "First Last 3" . . 1 ""
"Chapita - 1000010 - First Last (MARK-01-001)" "Neno" "Mlau" "Chapita" "First Last 20" 21266 . 1 "Not Yet Replaced"
"Chapita - 1000011 - First Last (MARK-01-001)" "Neno" "Mlau" "Chapita" "First Last 9" . 1 1 ""
"Chapita - 1000012 - First Last (MARK-01-001)" "Neno" "Mlau" "Chapita" "First Last 31" 21262 . 1 "Not Yet Replaced"
"Chasesa - 1000013 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 16" . . 20 ""
"Chasesa - 1000014 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 30" 21263 1 3 "Not Yet Replaced"
"Chasesa - 1000015 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 16" . . 20 ""
"Chasesa - 1000016 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 25" . 1 20 ""
"Chasesa - 1000017 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 21" . . 20 ""
"Chasesa - 1000018 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 14" . . 20 ""
"Chasesa - 1000019 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 29" 21266 . 20 "Not Yet Replaced"
"Chasesa - 1000020 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 30" . . 3 ""
"Chasesa - 1000021 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 14" . . 20 ""
"Chasesa - 1000022 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 12" . 1 20 ""
"Chasesa - 1000023 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 26" 21251 . 1 "Not Yet Replaced"
"Chasesa - 1000024 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 21" 21249 . 20 "Not Yet Replaced"
"Chasesa - 1000025 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 27" 21250 . 20 "Not Yet Replaced"
"Chasesa - 1000026 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 11" 21249 . 20 "Not Yet Replaced"
"Chasesa - 1000027 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 26" . . 1 ""
"Chasesa - 1000028 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 4" . 1 20 ""
"Chasesa - 1000029 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 19" 21262 . 20 "Not Yet Replaced"
"Chasesa - 1000030 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 18" 21250 1 20 "Not Yet Replaced"
"Chasesa - 1000031 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 26" 21251 1 1 "Not Yet Replaced"
"Chasesa - 1000032 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 25" 21249 1 20 "Not Yet Replaced"
"Chasesa - 1000033 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 13" . 1 20 ""
"Chasesa - 1000034 - First Last (MARK-01-001)" "Neno" "Mlau" "Chasesa" "First Last 17" . 1 20 ""
"Chidakwani - 1000035 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 23" . . 2 ""
"Chidakwani - 1000036 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 23" . . 2 ""
"Chidakwani - 1000037 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 10" . . 2 ""
"Chidakwani - 1000038 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 23" . . 2 ""
"Chidakwani - 1000039 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 15" . . 2 ""
"Chidakwani - 1000040 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 15" . 1 2 ""
"Chidakwani - 1000041 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 15" . 1 2 ""
"Chidakwani - 1000042 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 1" . 1 2 ""
"Chidakwani - 1000043 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 8" 21252 . 2 "Not Yet Replaced"
"Chidakwani - 1000044 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 10" . 1 2 ""
"Chidakwani - 1000045 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 23" . . 2 ""
"Chidakwani - 1000046 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 1" 21251 . 2 "Not Yet Replaced"
"Chidakwani - 1000047 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 6" . 1 2 ""
"Chidakwani - 1000048 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 15" . . 2 ""
"Chidakwani - 1000049 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 10" . 1 2 ""
"Chidakwani - 1000050 - First Last (MARK-01-001)" "Neno" "Chek" "Chidakwani" "First Last 10" . . 2 ""
end
format %td sdate
Now open a document, and create tables within it. Here is the workflow:
Use levelsof to create a local macro containing unique village names to loop through (I want a separate table within the same PDF for each village)
Begin the PDF document
Start the loop on villages
Add a title before the table
Create a table based on the village name: Rather than just using the data(varlist) way of making a table, I am dropping all observations that do not fall under this village, then looping through each observation and writing a row on the table for each. This gives me control over the column span (the 'label' variable is a long string, while some are byte numbers, so I want different column widths and this seems like the only way putpdf can do what I want it to).
Add a new row to the top of the table as a header, then write the individual column cells one at a time
Save the pdf
Here is the code:
* Get unique values of village in local vstr
levelsof village, local(vstr)
* Open the document and start the loop
set trace on // Helps to see where the code screws up
putpdf begin, page(A4) landscape margin(top, 0.25in) margin(right, 0.25in) margin(bottom,0.25in) margin(left,0.25in)
foreach village of local vstr {
preserve
local name = subinstr("`village'"," ","_",.) // These aren't impacted here, but in teh rst of my data I have spaces and apostrophes which need to go
local name = subinstr("`name'","'","_",.)
* Add a title before each table
putpdf paragraph, font("Calibri Light",14) halign(center)
putpdf text ("`village'")
* Keep only obs in this village (note 'preserve' above)
qui keep if village == "`village'"
* Get the number of observations so we can loop through row-by-row
qui count
* Start a table with N rows based on the above count
putpdf table hhs_`name' = (`r(N)',17)
* Loop through all N rows and write cell contents
forvalues i=1/`r(N)' {
putpdf table hhs_`name'(`i',1) = (substr(label[`i'],1,50)), colspan(6) // Longer string, hence colspan(6)
putpdf table hhs_`name'(`i',7) = (district[`i'])
putpdf table hhs_`name'(`i',8) = (ta[`i'])
putpdf table hhs_`name'(`i',9) = (substr(village[`i'],1,4))
putpdf table hhs_`name'(`i',10) = (cluster[`i']), colspan(3)
putpdf table hhs_`name'(`i',13) = (shopid[`i'])
putpdf table hhs_`name'(`i',14) = (coupons[`i'])
putpdf table hhs_`name'(`i',15) = (reason[`i']), colspan(2)
putpdf table hhs_`name'(`i',17) = (string(sdate[`i'],"%td_dm"))
}
* Write header row at the top of teh table
putpdf table hhs_`name'(1,.), addrows(1, before)
* The line below is where the error seems to be triggered:
putpdf table hhs_`name'(1,1) = ("Label"), colspan(6) bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,7) = ("District"), bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,8) = ("TA"), bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,9) = ("Village"), bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,10) = ("Cluster"), colspan(3) bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,13) = ("Shop ID"), bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,14) = ("Coupon"), bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,15) = ("Reason"), colspan(2) bold bgcolor(lightgray)
*putpdf table hhs_`name'(1,17) = ("Date"), bold bgcolor(lightgray)
restore
}
* Save the document
putpdf save "Village Remaining Lists.pdf", replace
set trace off
I commented out all rows within the loop and then un-commented them one-by-one starting at the top (putpdf table hhs_name(i,1) = (substr(label...). Originally, the file had no trouble writing a PDF. The error ("failed to add table; r(198)") is a syntax error.
Now, if you take that line and change the option bold to italic (or nothing at all), the error disappears.
putpdf table hhs_`name'(1,1) = ("Label"), colspan(6) italic bgcolor(lightgray)
Using set trace on doesn't immediately reveal where the problem actually occurs, but when you scroll up in the resulting output (when the option is set to bold), you can see that the loop is starting again with the village "Chidakwani" before the error pops up. And the error seems to occur before the table for Chidakwani begins. So I think that the problem is happening somewhere around "Chasesa" village, and either that table is not stopping, or the Chidakwani table cannot start. And all because of the "bold" option.
The italic workaround is sufficient for now, but I thought I would throw this out there and see if anyone wants to take a stab at figuring out what is going on.

I had a similar issue and found that this error is also linked to the lenght of the labelname of the previous lines and also somehow to the putpdf paragraph statement.
This code results in the "Failed to add table" - error:
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",10) bold
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",12) bold
putpdf table tabq25 = (4,7)
while this code works fine
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",10) bold
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",12) bold
putpdf table tabq25 = (4,7)
By deleting or replacing the bold-statement, the Code runs as usual.
Deleting the second putpdf paragraph statement also works:
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",10) bold
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",12) bold
putpdf table tabq25 = (4,7)
The error might not be related to the character lenght only, as this works fine:
putpdf paragraph
putpdf text ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), linebreak font("Arial",10) bold
putpdf table tabq25 = (4,7)

Related

VBA: Why are the defined DAY values returning "30"?

I've created a worksheet on which a month can be generated by inserting the first day of the needed month. User inserts "01/01/2017" in B4, clicks a button, the month (January) is filled out and the values within the table (C4:I34) are removed. The VBA code linked to that button goes as follows:
Sub CalReset()
'
' CalReset Macro
'
'
Dim DayOne As Integer
Dim DayTwo As Integer
Dim DayThree As Integer
DayOne = Day(B32)
DayTwo = Day(B33)
DayThree = Day(B34)
MsgBox (DayOne & " " & DayTwo & " " & DayThree) 'I used this to check what values I was getting for the days, the message box returns "30 30 30".
Range("B4").AutoFill Destination:=Range("B4:B34"), Type:=xlFillSeries
Range("C4:I34").ClearContents
If DayOne <= "3" Then Range("B32").ClearContents
If DayTwo <= "3" Then Range("B33").ClearContents
If DayThree <= "3" Then Range("B34").ClearContents
End Sub
The idea is to avoid dates of the next month. For example, if 01/02/2017 is used as a starting point, it'll fill in February (28 days) so the macro should remove March 1, 2 & 3 in corresponding cells B32, B33 & B34 (is smaller than or equals 3).
Why is the definition "Day(B32)" returning "30" while I'm seeing "01/03/2017" in that cell? It should return "1".
Note that dates are in European format, DD/MM/YYYY. Thank you for your attention.
Excel uses a data system where day #1 is 0 January 1900 (or 31 December 1899 if you like), so day #0 is 30 December 1899.
So Day(B32), where the variable B32 has never been assigned a value and therefore still contains a default value of 0, will return 30 (i.e. the day portion of 30 December 1899).
You were probably wanting to use Day(Range("B32").Value), or Day([B32]).
Sub CalReset()
With ActiveSheet
.Range("B4").AutoFill Destination:=.Range("B4:B34"), Type:=xlFillSeries
.Range("C4:I34").ClearContents
If Day(.Range("B32").Value) <= 3 Then .Range("B32").ClearContents
If Day(.Range("B33").Value) <= 3 Then .Range("B33").ClearContents
If Day(.Range("B34").Value) <= 3 Then .Range("B34").ClearContents
End With
End Sub

Finding last items of a multi-column listview

I have a multi-column listview that a colleague created in a useform. It gets populated once the useform opens. I want to find the items in the last row of the populated listview. For example:
header1 header2 header3
11 12 13
21 22 23
31 32 33
I want a line (or a few lines) to store 31, 32, and 33 in separate variables.
My ListView is named lsvLots. I am capable of storing 31 in the String strProduct with the following:
strProduct = lsvLots.ListItems.Item(lsvLots.ListItems.Count)
HoweverI can only returns the last item of the first column that way. How can I access the other columns?
The last line in the list is determined by the ListCount and can be referenced as follows:
UserForm1.lsvLots.List(UserForm1.lsvLots.ListCount - 1, 0)
Note, that the ListCount will be 1 if there is one row in the ListBox. Yet, the index to reference this (first & last row) starts with 0 and not with 1. Therefore, you have to subtract 1 from the ListCount to convert it to an index (starting with 0).
The 0 at the end means, that you want to get the first column from that row. Once again, the columns are 0 for the first column, 1 for the second column, 2 for the third column, etc.
In short, to get all the items from the last row you should use:
strProduct1 = UserForm1.lsvLots.List(UserForm1.lsvLots.ListCount - 1, 0)
strProduct2 = UserForm1.lsvLots.List(UserForm1.lsvLots.ListCount - 1, 1)
strProduct3 = UserForm1.lsvLots.List(UserForm1.lsvLots.ListCount - 1, 2)
OR (to be neater):
With UserForm1.lsvLots
strProduct1 = .List(.ListCount - 1, 0)
strProduct2 = .List(.ListCount - 1, 1)
strProduct3 = .List(.ListCount - 1, 2)
End With
Try this
strProduct = lsvLots.ListItems(lsvLots.ListItems.Count).SubItems(1)

Hide column in excel by date YYYY MM for the rest of this year plus three more

I'm trying to hide columns in excel with vba.
So far This is what I have it will hide after 36 months but I was just told that it needed to do the rest of the forecasted year plus three years then hide all months in year 4 and 5 except for December.
I added a picture of the spreadsheet all cells except for the input, cells C10 thru BJ12 and row 6, come from the second page of the workbook. In row 6 I was trying to take the year from row 9 and use it that way, It can be hard coded in but it must be dynamic because the startyear changes from the second page depending on the scenario and job selected.
'Hide all months except for December after 36 months
With Range("AM:BJ")
.EntireColumn.Hidden = True
For Each cell In Range("AM7:BJ7" & ActiveCell.SpecialCells(xlCellTypeLastCell).Column)
Select Case cell.Value
Case Is = "CurrentYear"
Case Is = "12"
cell.EntireColumn.Hidden = False
End Select
Next cell
End With
Try this! The code should keep all columns unhidden until 3 years after the starting year. Then it should hide all columns except the December months.
'Grab Starting Info
StartingAddress = "C9" 'Set this to first date location
StartingYear = Left(Range(StartingAddress).Value, 4) 'Grab first four characters as starting year
'Loop through all dates and determine which to hide/unhide
For i = 0 To Application.Columns.Count - 2
If Range(StartingAddress).Offset(0, i).Value = vbNullString Then
Exit For 'Saves time because it doesn't go through entire loop but can if needed
Else
'Hides column if year is 3 years greater than the starting year and the month is not Dec.
If CInt(Left(Range(StartingAddress).Offset(0, i).Value, 4)) > (3 + StartingYear) And Right(Range(StartingAddress).Offset(0, i).Value, 2) <> "12" Then
Range(StartingAddress).Offset(0, i).EntireColumn.Hidden = True
Else
Range(StartingAddress).Offset(0, i).EntireColumn.Hidden = False 'Just in case columns from previous run are hidden
End If
End If
Next i
Example: (Starting Date = 2015 10) The code will keep all columns unhidden until 2019 where it will then keep only the December months. If you wanted it to start hiding at 2018 then change
If CInt(Left(Range(StartingAddress).Offset(0, i).Value, 4)) > (3 + StartingYear) And Right(Range(StartingAddress).Offset(0, i).Value, 2) <> "12" Then
To
If CInt(Left(Range(StartingAddress).Offset(0, i).Value, 4)) >= (3 + StartingYear) And Right(Range(StartingAddress).Offset(0, i).Value, 2) <> "12" Then

Insert Value, new line macro

I drafted two tables to calculate some values. One value (attractiveness) appears in H16, the other (competivity) in H26.
Each time I calculate new values, I must be able to add these into columns K and L, when pressing a ADD-button. Both of the columns have the following range; K10:K26 and L10:L26.
So the first time I press "add", I want H16 to be transferred to K10 and H26 to L10. The second time I press "add", I want the new values in H16 and H26 to be inserted in K11 and L11.
I've already several ways, but my basic knowledge is just apparently less than basic.
You can get the last used row in a column like this:
Columns(col_number).Rows(Rows.Count).End(xlUp).Row
Assuming that K10 is the last used row you could do something like this:
Cells(Columns(11).Rows(Rows.Count).End(xlUp).Row + 1, 11).Value = Range("H16").Value
This would find the last used row + 1 and in column 11 (K) and set its value to the value of H16
Another way of doing this is to start at row 10 and just keep going down until you find a blank cell and set its value to whatever you want.
For eample:
'Create a variable to store the current row
Dim i As Long
'Start at row 10 and go down until you find a blank cell
For i = 10 To Rows.Count
'If the current cell is blank
If Cells(i, 11).Value = "" Then
'Set its value to H16
Cells(i, 11).Value = Range("H16").Value
'Stop looking for blank cells
Exit For
End If
Next

vbscript to delete rows in an excel sheet using a specific condition

I have an excel sheet with a column containing data as follows and there is data in other columns.
Here there are blank cells between the rows. Between 1014,1027 there is 1 blank Cell. There are 3 blank Cells between 1027 and other 1027 and so on..
Script should delete the rows with more than one blank cell in "column A" between the rows that contains data in "column A".
Eg: Script should delete the rows 1027(starting row always has a value) and next three empty rows, so it should not delete the rows if the gap between two non-empty values is 1. The same process should be done for the entire sheet.
There are more than one empty cell between the following values.
Code
1014
1027
1027
1033
1033
1033
1020
1033
1008
Please suggest me on this.
Give this a try:
Sub rowKiller()
Dim N As Long, i As Long
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To N - 1
If Cells(i - 1, 1) <> "" And Cells(i, 1) = "" And Cells(i + 1, 1) <> "" Then
Cells(i, 1).Value = "XXX"
End If
Next i
Range("A:A").Cells.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Range("A:A").Replace "XXX", ""
End Sub