Vba: Print an Excel sheet to multiple pdf pages - vba

I'll try to keep this short. I'm working on a Excel project and I have to print at the end a single sheet, the problem is the printed PDF is too small, so I've looked up for it in different forums and I found out that I had to turn .FitToPagesTall = 0 In order that the excel worksheet don't get fit to one pdf page. The problem I'm struggling with now is that even if the pdf pages are bigger than before, It still small and It's making it hard to read. My idea is to print each 30 rows (for example) in a pdf page (Page1 --> Range("A1:E30"), Page2 --> Range("A31:E60").. etc, you got the idea)
Any ideas how I can do that please ?
Thanks in advance !
Update #1: Here's a screen shot of only a slice of my data range
Even if I set the .PrintArea to A:D, it still give the same result. To rephrase my request: I'm looking for a way to print different ranges in multiple pages.
thanks everyone for giving me a part out of ur time.

try to adapt the Orientation and fit the data horizontally, something like this :
With ActiveSheet.PageSetup
.PrintTitleRows = "$1:$1" 'to repeat your header on each page
.CenterHorizontally = True
.CenterVertically = True
.Orientation = xlLandscape ' or xlPortrait
.Zoom = False
.FitToPagesWide = 1 'fit to wide only
.FitToPagesTall = 0
End With

EDIT: auto-selection code added
My understanding of the question: how to print multiple selections from the same worksheet into PDF containing multiple pages (each selection = new page)
Multiple selection (ctlr) & Print Selection
Range("A1:E30, A31:E60") _
.PrintOut Copies:=1, Collate:=True 'your ranges in the quotes
'prompts user to choose destination where to save to
It is equal to manually selecting multiple ranges (holding ctrl) and selecting print selection when printing.
Edit - Code added
If you like the above idea, you might like the following code?
It constructs a string, such as: "A1:D30,A31:D60,A61:D90", which specifies ranges to be selected. Assumptions is that data is in columns A-D and last row can be found in column A. You can specify how many rows to print in my_step.
Sub select_and_print()
Dim s_rn, s_range As String
Dim i, my_step As Integer
Dim selections_count As Single
my_step = 30 'how many rows to include on 1 page
'to prevent extra page in some cases:
selections_count = WorksheetFunction.RoundUp((Cells(Rows.Count, 1).End(xlUp).Row / my_step), 0)
'String, which specifies selections in ws, between columns A - D
For i = my_step To selections_count * my_step Step my_step:
s_rn = "A" & (i - my_step + 1) & ":D" & i
If i = my_step Then
s_range = s_rn
Else
s_range = s_range + "," + s_rn
End If
Next i
'selection & print
Range(s_range).PrintOut Copies:=1, Collate:=True
End Sub

Building off #JC Guidicelli's answer, the important things I was missing was .FitToPagesTall = False. I don't know why Excel requires False and not 0, but that seems to be the way it is.
Sheets(Array("Sheet 1", "Sheet 2", "Sheet 3")).Select
With ActiveSheet.PageSetup
.Zoom = False
.FitToPagesWide = 1
.FitToPagesTall = False
End With
and in my export command, I needed IgnorePrintAreas:=True.
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=<file path>, Quality:=xlQualityStandard, IgnorePrintAreas:=True
This gives me a PDF with all the data from Sheets 1, 2, and 3, and if data from those sheets is more than a page, it splits it over multiple pages without zooming it in.

Related

Using VBA in excel to set specific formatting when creating new sheets using modules

My project is to create an easy to use list based off of a master list of items that are separated into separate sheets according to shop, and then format them ready for printing.
Currently, I have a macro button that divides up a master list into separate sheets depending on what value is in the shop column.
Next, I need to copy the formatting or set up the formatting of the new sheets in a very specific way: Print breaks to the right of column J, landscape, a specific header, and a box of text at the very bottom with space for a signature.
So far I've only managed to set the new sheets to landscape. Page break doesn't work.
Sub SetAttributes()
For Each xWorksheet In ActiveWorkbook.Worksheets
xWorksheet.PageSetup.Orientation = xlLandscape
xWorksheet.Cells.PageBreak = xlPageBreakNone
xWorksheet.Columns("K").PageBreak = xlPageBreakManual
Next xWorksheet
End Sub
Has anyone got any suggestions as to how I can move forward?
Ended up writing the specific formatting rather than code that simply copies the formatting.
Sub SetAttributes()
'Code telling excel that I want the following to apply to all my
'sheets in this workbook
For Each xWorksheet In ActiveWorkbook.Worksheets
'Set the page as landscape
xWorksheet.PageSetup.Orientation = xlLandscape
xWorksheet.Columns("K").PageBreak = xlPageBreakManual
'Set space for the header by increasing TOP margin
xWorksheet.PageSetup.TopMargin = _
Application.InchesToPoints(1.5)
'Inclusion Logo
xWorksheet.PageSetup.LeftHeader = "&G"
xWorksheet.PageSetup.LeftHeaderPicture.Filename = ThisWorkbook.Path +
"\Logo2.png"
'SSSFT logo
xWorksheet.PageSetup.RightHeader = "&G"
xWorksheet.PageSetup.RightHeaderPicture.Filename = ThisWorkbook.Path +
"\Logo1.png"
' Address
xWorksheet.PageSetup.CenterHeader = "&G"
xWorksheet.PageSetup.CenterHeaderPicture.Filename = ThisWorkbook.Path +
"\Address.png"
'Footer
xWorksheet.PageSetup.CenterFooter = "&A"
'set column width to help printing format
xWorksheet.Columns("A").ColumnWidth = 10
xWorksheet.Columns("C").ColumnWidth = 21
xWorksheet.Columns("E").ColumnWidth = 26
xWorksheet.Columns("F").ColumnWidth = 9
'Insert space for signatures 3 rows below the last row of
'data with a space for the pharmacy to date and sign
'Dim lastRow As Long
lastRow = xWorksheet.Cells(Rows.Count, 1).End(xlUp).Row
' wrap text
xWorksheet.Range("A:J").WrapText = True
'And move to the next sheet
Next xWorksheet
End Sub

excel checkspelling single cell

I'm struggling a bit with CheckSpelling in Excel. I have a merged cell that I want to check, but only this cell. Here's what I'm doing.
ActiveSheet.Unprotect strSheetPassword
Application.DisplayAlerts = False
Set ma = Range("B21").MergeArea
ma.MergeCells = False
Union(Range("B1"), ma(1, 1)).CheckSpelling
ma.MergeCells = True
Application.DisplayAlerts = True
ActiveSheet.Protect strSheetPassword
It's checking the cell I want, but it's also checking the rest of the document. In reading other posts, I got the impression that checking a single cell causes CheckSpelling to check the entire document. This is why I put in the Union with the Range("B1") - B1 contains header text that doesn't have any misspellings and is normally locked, so that users can't change it. But, it is still checking the rest of the sheet! I've tried quite a few variations on this, but it still keeps checking the rest of the sheet.
CONCLUSION
I had been under the impression that it was possible to invoke the CheckSpelling form and have it only check certain cells. Apparently, this isn't true. Instead of building my own form, I should be able to get away with checking the whole sheet each time, although I really don't like that. Thanks for all the feedback!
For a single merged cell:
Sub spell_me()
Dim b As Boolean
b = Application.CheckSpelling(Word:=ActiveCell.Text)
MsgBox b & vbCrLf & ActiveCell.Address & vbCrLf & ActiveCell.Text
End Sub
EDIT#1:
To find the miscreant word, you could Split() the text into individual words and check each word.
If it is enough if the wrong part gets highlighted you can use this:
Sub SpellCheck()
Dim response As Boolean
Dim words As Variant
Dim wordCount As Long
Dim startAt As Long
words = Split(ActiveCell.Text, " ")
'set all of the text to automatic color
ActiveCell.Font.ColorIndex = xlAutomatic
For wordCount = LBound(words) To UBound(words)
response = Application.CheckSpelling(word:=words(wordCount))
If Not response Then
'find out where it is in the text and color the font red
startAt = InStr(ActiveCell.Text & " ", words(wordCount) & " ")
ActiveCell.Characters(Start:=startAt, Length:=Len(words(wordCount))).Font.Color = vbRed
End If
Next
End Sub

Select Multiple Excel Sheets to Export to PDF

I have some code written to build a list of worksheets in a string based on if the user selects a checkbox to include the sheet in the PDF report. See below:
If CheckBox1.Value = True Then
PDFsheets = "Sheet11"
End If
If CheckBox2.Value = True Then
If PDFsheets = "" Then
PDFsheets = "Sheet13"
Else
PDFsheets = PDFsheets & ",Sheet13"
End If
End If
If CheckBox3.Value = True Then
If PDFsheets = "" Then
PDFsheets = "Sheet2"
Else
PDFsheets = PDFsheets & ",Sheet2"
End If
End If
For example, when all 3 checkboxes are selected, MsgBox PDFsheets shows the result Sheet11, Sheet13, Sheet2
NOW, when I try to do a multiple sheet select I get Run-Time Error 9 - Subscript Out of Range. The different things I've tried include:
ThisWorkBook.Sheets(PDFsheets).Select
ThisWorkBook.Sheets(Array(PDFsheets)).Select
xPDF() = Split(PDFsheets, ",")
ThisWorkBook.Sheets(xPDF).Select
xPDF() = Split(PDFsheets, ",")
ThisWorkBook.Sheets(Array(xPDF)).Select
xPDF() = Split(PDFsheets, ",")
For i = 0 to Application.CountA(xPDF) - 1
Sheets(xPDF(i)).Select
next i
ALSO, for the sake of brevity I have tried all of the above examples with ActiveWorkbook instead of ThisWorkBook. PLUS, I've tried rewriting my string building part to reference the sheets names instead of numbers with all of the examples above. So instead of Sheet11, Sheet13, Sheet2 the result was "Sheet11", "Sheet13, "Sheet2" with the double quotes around the sheets.
I've look at other questions and code examples on here and elsewhere that show this same goal and I'm using the selection line exactly as they have had it and I'm getting the Subscript out of range error. (I've also verified everything is spelled correctly)
Additional Note if I try typing the string value directly in it works - ThisWorkBook.Sheets("Sheet11, Sheet13, Sheet2").Select - However, this doesn't allow me to keep it variable.
Your are building a comma separated string that you must convert into an array. You were very close:
Sub BuildAString()
Dim PDFsheets As String
Dim s As Worksheet
PDFsheets = "Sheet1,Sheet2,Sheet3"
ary = Split(PDFsheets, ",")
Sheets(ary).Select
Selection.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
"C:\TestFolder\Book1.pdf", Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:= _
True
End Sub
Note this makes 1 pdf file for all three sheets. You would use a loop if you wanted 3 separate files.Note that there are no spaces inPDFsheets because my worksheet names have no spaces.

Formatting Copy/paste charts in VBA

Currently, I have some code that copy pastes some graphs over to a newly created sheet and then turns that sheet into a pdf. However, if the graphs extend beyond one page, the graph is cutoff in the middle between the two pages. How do I ensure that this does not happen? Should I create a template specifically to copy/paste these graphs onto?
Here is the part that copy/pastes the graphs over.
x = 1
y = 1
For i = 1 To source.ChartObjects.Count
title_name = source.ChartObjects(i).Chart.ChartTitle.Text
If InStr(Replace(title_name, ",", ""), search) > 0 Then
source.ChartObjects(i).Activate
ActiveChart.ChartArea.Copy
wsTemp.Cells(x, y).PasteSpecial
x = x + 20
End If
Next
Next
wsTemp.ExportAsFixedFormat Type:=xlTypePDF, Filename:=NewFileName, Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=True
wsTemp.Delete
Application.DisplayAlerts = True
LetsContinue:
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
This depends on the procedure used to create the PDF. If it is taking the PrintArea of the worksheet and converting it then you could do this
1. When pasting the graph you should also set the print area to cover the width of the graph.
Something along the lines of this:
rngPrintArea = Range(wsTemp.cells(1,1), wsTemp.cells(x, y + 1))
wsTemp.PageSetup.PrintArea = rngPrintArea.Address
2. If this issue isn't with the PrintArea on the spreadsheet and is
occurring because the width of the graph is just bigger than the
fixed size allotted for 1 page when using the PDF procedure then you
need to decrease the size of the pasted graph.
ActiveChart.Width = 10 ' Change 10 to whatever width is acceptable for the PDF procedure
ActiveChart.Heigth = 10 ' Change 10 to whatever height is acceptable for the PDF procedure

excel vba not exporting pagesetup to pdf correctly

I have code which formats a worksheet to the desired setup and layout (one page wide and tall in landscape). When I run the code (part of a long macro) it formats the pagesetup correctly.
If I manually export and save it as a pdf, then it uses the correct page setup, producing a one page PDF that is in landscape. However, the same export done by VBA produces a PDF that is severalpages long and in portrait.
i can't figure out why it's doing this. i've tried various solutions such as selecting the worksheet before exporting it, but all to no avail.
Any help is appreciated.
Code looks like this:
Sub SaveAsPDF()
Sheets(ReportWsName).ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
[SaveFolderPath] & "\" & ReportWsName, Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:= _
False
End Sub
UPDATE:
Code used to format the pagesetup (since it's rather long I am only adding the relevant section of that sub)
Private Sub CreateNewReport(ProvisionCode As String, TimeFrom As Date, TimeTo As Date)
... other code here...
'Format report to create the desired layout
With Worksheets(ReportWsName)
'Delete unnecessary data and format the rest
.Range("A:B,D:D,F:G,J:M,O:O,Q:S").Delete Shift:=xlToLeft
.Range("A:F").EntireColumn.AutoFit
.Range("C:C, E:F").ColumnWidth = 30
With .Range("G:G")
.ColumnWidth = 100
.WrapText = True
End With
'Insert standard formating header form Reporting template
.Rows("1:2").Insert
wsReportTemplate.Range("1:3").Copy .Range("A1")
.Range("A2") = "Notes Report for " & ProvisionCode & " (" & TimeFrom & " - " & TimeTo & ")"
'Insert standard formating footer form Reporting template
wsReportTemplate.Range("A6:G7").Copy .Range("A" & .UsedRange.Rows.Count + 2)
'Ensure all data is hard coded
.UsedRange.Value = .UsedRange.Value
'Format Print Area to one Page
With ActiveSheet.PageSetup
.PrintArea = Worksheets(ReportWsName).UsedRange
.Orientation = xlLandscape
.FitToPagesWide = 1
End With
End With
End Sub
I have found what seems to be the solution:
Application.PrintCommunication = False
With ActiveSheet.PageSetup
.Orientation = xlLandscape
.Zoom = False
'.PrintArea = Worksheets(ReportWsName).UsedRange
.FitToPagesWide = 1
'.FitToPagesTall = 1
End With
Application.PrintCommunication = True
I needed to add the Application.PrintCommunication part to the equation. For whatever reason Excel would overwrite the settings I was putting if I ran the code without it.
I think the problem is that you need to add the .Zoom = False to your page setup code:
'Format Print Area to one Page
With ActiveSheet.PageSetup
.PrintArea = Worksheets(ReportWsName).UsedRange
.Orientation = xlLandscape
.FitToPagesWide = 1
.Zoom = False 'I have added this line
End With
From what I have tried this should solve it for you.
Let me know how it goes!
EDIT: Maybe you need:
'Format Print Area to one Page
With ActiveSheet.PageSetup
.PrintArea = Worksheets(ReportWsName).UsedRange
.Orientation = xlLandscape
.FitToPagesWide = 1
.FitToPagesTall = 1
.Zoom = False 'I have added this line
End With
EDIT2: What if you changed:
.PrintArea = Worksheets(ReportWsName).UsedRange
To
.PrintArea = Worksheets(ReportWsName).UsedRange.Address
Yes!!!, I have had the same problem: I was not able to export a sheet with the page Setup settings already applied on it.
Before trying the Application.PrintCommunication I tested Wait and Sleep commands without success. Finally I skipped this issue by using CopyPicture method, adding a chart page and then exporting it to pdf, but resolution in my pdf it was not fine and I was not able to play with margins.
So just add Application.PrintCommunication=false before your code , on pagesetup settings like CaptainABC says and most important: close with Application.PrintCommunication=true after the code.
Thank you for this useful post.