Save a number of excel worksheets as a PDF - vba

Option Explicit
Dim mySheets As Dictionary
Private Sub SaveAndOpen_Click()
'set up variables
Dim i As Long
Dim j As Long
Dim myArr() As Long
Dim filename As String
ReDim myArr(1 To Sheets.Count)
j = 1
'make bounds
Dim from As Long
Dim tonum As Long
'numbers inputted from a userform
from = FromBox.Value
tonum = ToBox.Value
filename = Cells(3, 4) & "." & mySheets.Item(from) & "-" & mySheets.Item(tonum)
For i = 1 To mySheets.Count
If i >= FromBox.Value And i <= ToBox.Value Then
myArr(j) = i
j = j + 1
End If
Next i
Dim filepath As String
For i = 1 To UBound(myArr)
filepath = filepath & myArr(i)
Next i
filepath = "c:\file\path\here\"
ThisWorkbook.Sheets(myArr).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, filename:= _
filepath & filename, Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=True
ThisWorkbook.Sheets(1).Select
End Sub
Private Sub UserForm_Initialize()
Copies.Value = 1
FromBox.Value = 1
Dim i As Long
Set mySheets = New Dictionary
For i = 1 To ActiveWorkbook.Sheets.Count
mySheets.Add i, ActiveWorkbook.Sheets(i).Name
SheetBox.Value = SheetBox.Value & i & " - " & ActiveWorkbook.Sheets(i).Name & vbCrLf
Next i
ToBox.Value = i - 1
End Sub
This subroutine takes information from a userform, which has user inputted variables in FromBox and ToBox; these are both longs. The goal is to be able to save, for instance, sheets 2 - 10. The parameters are specified by the user.
the following code, with the bottom section uncommented, works when the user specifies all of the worksheets (IE there are 10 worksheets, and the user specifies range 1-10). But when the user specifies 2-10, it fails.
The problem, I think, is that I'm trying to select 10 elements with a 9 element long array.

As Scott Holtzman pointed out in a comment, you are dimensioning myArr larger than it should be. It therefore has unassigned values in it, which are left as the default zero value, and that causes problems because you don't have a sheet 0 to be selected.
I think the following code should work:
Option Explicit
Dim mySheets As Dictionary
Private Sub SaveAndOpen_Click()
'set up variables
Dim i As Long
Dim j As Long
Dim myArr() As Long
Dim filename As String
'make bounds
Dim from As Long
Dim tonum As Long
'numbers inputted from a userform
from = FromBox.Value
tonum = ToBox.Value
'Check ToBox.Value is valid
If tonum > Sheets.Count Then
MsgBox "Invalid To value"
Exit Sub
End If
'Check FromBox.Value is valid
If from > tonum Then
MsgBox "Invalid From value"
Exit Sub
End If
'Setup myArr
ReDim myArr(from To tonum)
For j = from To tonum
myArr(j) = j
Next
filename = Cells(3, 4) & "." & mySheets.Item(from) & "-" & mySheets.Item(tonum)
'
Dim filepath As String
'For i = LBound(myArr) To UBound(myArr)
' filepath = filepath & myArr(i)
'Next i
filepath = "c:\file\path\here\"
ThisWorkbook.Sheets(myArr).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, filename:= _
filepath & filename, Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=True
ThisWorkbook.Sheets(1).Select
End Sub

Related

How to export two PowerPoint slides to PDF file?

In this site, I found code that it prints the last slide as PDF.
Sub PDFtesti()
timestamp = Now()
Dim PR As PrintRange
Dim lngLast As Long
Dim lngFirst As Long
Dim savePath As String
Dim PrintPDF As Integer
Dim name As String
name = ActivePresentation.Slides(2).Shapes("TextBox1").OLEFormat.object.Text
savePath = "C:\Powerpoint\" & Format(timestamp, "yyyymmdd-hhnn") & " - " & name & ".pdf"
lngLast = ActivePresentation.Slides.Count
With ActivePresentation.PrintOptions
.Ranges.ClearAll
Set PR = .Ranges.Add(lngLong, lngLong)
End With
ActivePresentation.ExportAsFixedFormat _
Path:=savePath, _
FixedFormatType:=ppFixedFormatTypePDF, _
PrintRange:=PR, _
Intent:=ppFixedFormatIntentScreen, _
FrameSlides:=msoTrue, _
RangeType:=ppPrintSlideRange
End Sub
I would like print two slides: slide number 2 and last page.
I tried
Set PR = .Ranges.Add(lngLong, lngLong)
Set PR = .Ranges.Add(2, 2)
and
Set PR = .Ranges.Add(Array("lngLong, lngLong" & "2,2")
Updated solution. In order to make printing specific slides more comfortable, I decided to put them into one variable (slidesToPrint). All slides not put into this variable are hidden just before printing (so they are not printed). After printing the hide order is restored to the original. So, slides which must be printed, bust be listed in this line:
slidesToPrint = Array(2, lngLast)
Full code:
Sub PDFtesti()
Dim timestamp As Date
Dim PR As PrintRanges
Dim lngLast As Long
Dim lngFirst As Long
Dim savePath As String
Dim PrintPDF As Integer
Dim name As String
Dim originalHides() As Long
Dim slidesToPrint() As Variant
Dim i As Variant
timestamp = Now()
With ActivePresentation
name = .Slides(2).Shapes("TextBox1").OLEFormat.Object.Text
savePath = "C:\Powerpoint\" & Format(timestamp, "yyyymmdd-hhnn") & " - " & name & ".pdf"
lngLast = .Slides.Count
.PrintOptions.Ranges.ClearAll
' Slides to print are put here (inside parentheses)
slidesToPrint = Array(2, lngLast)
ReDim originalHides(1 To lngLast)
For i = 1 To lngLast
originalHides(i) = .Slides(i).SlideShowTransition.Hidden
.Slides(i).SlideShowTransition.Hidden = -1
Next
For Each i In slidesToPrint()
.Slides(i).SlideShowTransition.Hidden = 0
Next
.ExportAsFixedFormat _
Path:=savePath, _
FixedFormatType:=ppFixedFormatTypePDF, _
Intent:=ppFixedFormatIntentScreen, _
FrameSlides:=msoTrue
For i = 1 To lngLast
.Slides(i).SlideShowTransition.Hidden = originalHides(i)
Next
End With
End Sub
In your code you use PR As PrintRange. But since you want two pages, the second and the last, you will need two ranges, which is not of type PrintRange, but instead, PrintRanges. In this case you would do:
.Ranges.Add(2, 2)
.Ranges.Add(lngLast, lngLast)
But this would not work, because the function ExportAsFixedFormat only accepts PrintRange, but not PrintRanges. One option would be printing both slides to separate files first using PrintRanges(1) i.e. (2, 2) and next using PrintRanges(2) i.e. (lngLast, lngLast). But this is not really what you want.
The solution. It uses PR(1) (PR is of type PrintRanges, while PR(1) is PrintRange). PR(1) is the range corresponding to the last two slides. The trick is that just before printing you move the second slide to the position lngLast - 1 (one before last) and after printing you return it to the correct place.
This moves the second slide to one before last position:
.Slides(2).MoveTo lngLast - 1
This returns it to the original position:
.Slides(lngLast - 1).MoveTo 2
Full code:
Sub PDFtesti()
Dim timestamp As Date
Dim PR As PrintRanges
Dim lngLast As Long
Dim lngFirst As Long
Dim savePath As String
Dim PrintPDF As Integer
Dim name As String
timestamp = Now()
With ActivePresentation
name = .Slides(2).Shapes("TextBox1").OLEFormat.Object.Text
savePath = "C:\Powerpoint\" & Format(timestamp, "yyyymmdd-hhnn") & " - " & name & ".pdf"
lngLast = .Slides.Count
Set PR = .PrintOptions.Ranges
PR.ClearAll
PR.Add lngLast - 1, lngLast
.Slides(2).MoveTo lngLast - 1
.ExportAsFixedFormat _
Path:=savePath, _
FixedFormatType:=ppFixedFormatTypePDF, _
PrintRange:=PR(1), _
Intent:=ppFixedFormatIntentScreen, _
FrameSlides:=msoTrue, _
RangeType:=ppPrintSlideRange
.Slides(lngLast - 1).MoveTo 2
End With
End Sub

VBA Search Single Column

New to asking questions on this site, and to VBA so please bear with me... I'm compiling this database that is linking drawing numbers that show the same items but each drawing shows a different aspect of that particular 'area' shown in the drawing (I Hope that makes sense). The function that i would like to have is to be able to search just the A column for a value, and return the all of the unique times that the value shows up in the A column and the corresponding B column value. I thought that even with my paltry VBA skills i could manage this but I dont have much so far. This is what i have:
Dim ISO As String
Dim Rng As Range
ISO = InputBox("ISO Number: ", "Enter ISO Number")
If Trim(ISO) <> "" Then
With Sheets("Sheet1").Range("A:A")
Set Rng = .Find(What:=ISO)
If Not Rng Is Nothing Then
Application.Goto Rng, True
Else
MsgBox ("Nothing Found")
End If
End With
End If
Thanks in Advance.
I'd use a for loop to iterate over the cells.
Sub FindMatches()
Dim ISO As String
Dim Rng As Range
Dim lastRow As Long, x As Long
Dim ws As Worksheet
Dim foundCount As Long
Dim endString As String
ISO = InputBox("ISO Number: ", "Enter ISO Number")
If Trim(ISO) <> "" Then
Set ws = Sheets("Sheet1") ' always best to use a variable for an object if possible
lastRow = ws.Cells(100000, 1).End(xlUp).Row ' work out how many rows to loop through
For x = 1 To lastRow ' use a for loop to iterate over each row
If ws.Cells(x, 1) = ISO Then
foundCount = foundCount + 1
endString = endString & ws.Cells(x, 2) & vbNewLine ' add column B to the string
End If
Next x
End If
MsgBox "Found " & foundCount & " matches: " & vbNewLine & endString
End Sub
For faster processing you could use an array rather than read from the cells one at at time:
Sub FindMatchesArray()
Dim ISO As String
Dim Rng As Range
Dim lastRow As Long, x As Long
Dim ws As Worksheet
Dim foundCount As Long
Dim endString As String
Dim arr() As Variant
ISO = InputBox("ISO Number: ", "Enter ISO Number")
If Trim(ISO) <> "" Then
Set ws = Sheets("Sheet1") ' always best to use a variable for an object if possible
lastRow = ws.Cells(100000, 1).End(xlUp).Row ' work out how many rows to loop through
arr = ws.Range("A1:B" & lastRow).Value
For x = 1 To lastRow ' use a for loop to iterate over each row
If arr(x, 1) = ISO Then
foundCount = foundCount + 1
endString = endString & arr(x, 2) & vbNewLine ' add column B to the string
End If
Next x
End If
MsgBox "Found " & foundCount & " matches: " & vbNewLine & endString
End Sub
You could use Find and FindNext.
The first Test will return the values in a message box, the second will place the returned values in cell A1 on Sheet2.
I could've sworn this should work as a Worksheetfunction, but no luck (.FindNext won't work in a UDF).
Sub Test()
Dim MyMessage As String
MyMessage = ReturnCountAndValue("5", ThisWorkbook.Worksheets("Sheet1").Columns(1))
MsgBox MyMessage, vbOKOnly + vbInformation
End Sub
Sub Test2()
With ThisWorkbook
.Worksheets("Sheet2").Range("A1") = ReturnCountAndValue(.Worksheets("Sheet1").Range("K2"), _
.Worksheets("Sheet1").Range("F2:F9"))
End With
End Sub
Public Function ReturnCountAndValue(SearchValue As String, _
SearchColumn As Range) As String
Dim rFound As Range
Dim sFirstAddress As String
Dim sTempReturn As String
Dim lCounter As Long
With SearchColumn
Set rFound = .Find(What:=SearchValue, LookIn:=xlValues, LookAt:=xlWhole)
If Not rFound Is Nothing Then
sFirstAddress = rFound.Address
Do
lCounter = lCounter + 1
sTempReturn = sTempReturn & rFound.Offset(, 1).Value & vbCr
Set rFound = .FindNext(rFound)
Loop While rFound.Address <> sFirstAddress
sTempReturn = lCounter & " items found. " & vbCr & _
sTempReturn
Else
sTempReturn = SearchValue & " not found in range " & SearchColumn.Address
End If
End With
ReturnCountAndValue = sTempReturn
End Function

Track changes in excel against previous version vba

I am trying to track the changes made in an excel file used by multiple people. So far I have built the model (with help from stackoverflow) to save a version of itself within activeworkbook.path & "\Version Control".
The copy is saved when file opens and is named: date & time & activeworkbook.name
Currently it look at a range to determine if intersect then return the new value in sheet x cell y. How do I write the code to allow it to open the latest saved backup and give me in sheet x cell z the older versions value?
Code I have:
Track Changes:
Private Sub Worksheet_Change(ByVal Target As Range)
If Sheets("UserLog").Cells(1, 24) = IsBlank Then Exit Sub
Dim strAddress As String
Dim val
Dim dtmTime As Date
Dim Rw As Long
If Intersect(Target, Range("CheckRange5")) Is Nothing Then Exit Sub
dtmTime = Format(Now(), "YYYY-MM-DD HH:MM:SS")
val = Target.Value
strAddress = Target.Address
Rw = Sheets("UserLog").Range("D" & Rows.Count).End(xlUp).Row + 1
With Sheets("UserLog")
.Cells(Rw, 4) = strAddress
.Cells(Rw, 5) = val
.Cells(Rw, 6) = dtmTime
.Cells(Rw, 7) = Environ("Username")
.Cells(Rw, 8) = "SCI - SDBD"
End With
ActiveWorkbook.Save
End Sub
Saving the File:
Private Sub Workbook_Open()
Dim MyDate
MyDate = Date
Dim MyTime
MyTime = Time
Dim TestStr As String
TestStr = Format(MyTime, "hhmmss")
Dim Test1Str As String
Test1Str = Format(MyDate, "YYYY-MM-DD")
xxPath = ActiveWorkbook.Path
Application.DisplayAlerts = False
ActiveWorkbook.SaveCopyAs Filename:=xxPath & "\Version Control" & "\" & Test1Str & " " & TestStr & " " & ActiveWorkbook.Name
ActiveWorkbook.Save
Application.DisplayAlerts = True

Create and Write to a text file using an excel macro and VBA

I am using a macro and VBA code to create a text file with a specific format. All the data needed to create the text file is gathered from the macro cells.
I have attached pictures of the macro data file and the output text file (please see below).
excel macro with data
Desired output txt format-example
Also, below is my VBA code I generated to get data from the macro and create/write into a text file. I still need to figure out how to write it in the specified format (Desired output txt format-example).
Sub ExcelToTxt()
'Declaring variables
Dim lCounter As Long
Dim lLastRow As Long
Dim destgroup As String
Dim parmlabel as Variant
Dim FName As Variant
'Activate Sheet1
Sheet1.Activate
'Find the last row that contains data
With Sheet1
lLastRow = .Cells(.Rows.Count, "A").End(xlDown).Row
End With
'Create txt file
FName = Application.GetSaveAsFilename("", "txt file (*.txt), *.txt")
'Open FName For Output As #1
For lCounter = 2 To lLastRow
'Read specific data from the worksheet
With Sheet1 destgroup = .Cells(lCounter, 19)
parmlabel = .Cells(lCounter, 8)
If destgroup="trex_15hz" Or destgroup="trex_10hz" Or destgroup="trex_5hz" Then
'Write selected data to text file
'Write #1, parmlabel
End If
End With
'Continue looping until the last row
Next lCounter
'Close the text file
Close #1
End Sub
Any help with what I need to add in my VBA to create the formatted output txt file will be greatly appreciate it.
Thank you in advance.
You can combine the data into an array and then convert it back into text.
Sub ExcelToTxt()
'Declaring variables
Dim i As Long, j As Integer
Dim n As Long, k As Long
Dim destgroup As String
Dim FName As String
Dim vDB, vR(1 To 6), vJoin(), vResult()
Dim sJoin As String, sResult As String
Dim s As Long
'Activate Sheet1
Sheet1.Activate
'Find the last row that contains data
With Sheet1
vDB = .Range("a1").CurrentRegion '<~~ get data to array from your data range
n = UBound(vDB, 1) 'size of array (row of 2 dimension array)
End With
'Create txt file
FName = Application.GetSaveAsFilename("", "txt file (*.txt), *.txt")
For i = 2 To n '<~~loop
destgroup = vDB(i, 2) '<~~ second column
If destgroup = "trex_15hz" Or destgroup = "trex_10hz" Or destgroup = "trex_5hz" Then
vR(1) = "; ### LABEL DEFINITION ###" '<~~ text 1st line
s = Val(Replace(vDB(i, 3), "label", ""))
vR(2) = "EQ_LABEL_DEF,02," & Format(s, "000")
vR(3) = "UDB_LABEL," & Chr(34) & vDB(i, 4) & Chr(34) '<~~ 2nd line
ReDim vJoin(4 To 7)
vJoin(4) = Chr(34) & vDB(i, 4) & Chr(34)
For j = 5 To 7
vJoin(j) = vDB(i, j)
Next j
sJoin = Join(vJoin, ",")
vR(4) = "STD_SUB_LABE," & sJoin '<~~ 3th line
ReDim vJoin(8 To 12)
vJoin(8) = Chr(34) & UCase(vDB(i, 8)) & Chr(34)
vJoin(9) = Chr(34) & vDB(i, 9) & Chr(34)
vJoin(10) = Format(vDB(i, 10), "#.000000000")
For j = 11 To 12
vJoin(j) = vDB(i, j)
Next j
sJoin = Join(vJoin, ",")
vR(5) = "STD_SUB_LABE," & sJoin '<~~ 4the line
vR(6) = "END_EQ_LABEL_DEF" '<~~ 5th line
k = k + 1
ReDim Preserve vResult(1 To k)
vResult(k) = Join(vR, vbCrLf) '<~~ 5 line in array vR and get to array vResult with join method
End If
Next i
sResult = "EQUIPMENT_ID_DEF,02,0x1," & Chr(34) & "trex" & Chr(34) '<~~ text file first line
sResult = sResult & vbCrLf & Join(vResult, vbCrLf) '<~~ combine 1th and other line
ConvertText FName, sResult '<~~ sub presedure
End Sub
Sub ConvertText(myfile As String, strTxt As String)
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
With objStream
'.Charset = "utf-8"
.Open
.WriteText strTxt
.SaveToFile myfile, 2
.Close
End With
Set objStream = Nothing
End Sub

How to assign value from a named range in one worksheet to a cell in the active worksheet?

I am trying to archive data from formatted worksheet called BOD_Labsheet to one called Data. I have done something similar using UserForms but am encountering problems here.
When I run the macro, I get the error "Method 'Range' of object _Worksheet failed" on line
dataWorksheet.Cells(emptyRow, 2) = bodWorksheet.Range("BOD_Lab_Date").Value
The Data worksheet is active when I do the copying.
Should I simply copy all the values from BOD_Labsheet to an array, activate the Data Worksheet and recopy values?
Here is the complete code:
Sub Submit_BOD()
'
' Submit_BOD Macro
'
Dim dataWorksheet As Worksheet, bodWorksheet As Worksheet, suspendedSolidsWorksheet As Worksheet
Dim dataSheetName As String
Dim bodSheetName As String
Dim suspendedSolidsName As String
dataSheetName = "Data"
bodSheetName = "BOD_Labsheet"
suspendedSolidsName = "Suspended_Solids_Labsheet"
Set dataWorksheet = ActiveWorkbook.Sheets(dataSheetName)
Set bodWorksheet = ActiveWorkbook.Sheets(bodSheetName)
Set suspendedSolidsWorksheet = ActiveWorkbook.Sheets(suspendedSolidsName)
Dim myRanges() As Variant
myRanges = Array("BOD_Collected_By", "BOD_Temp_Out", "BOD_Temp_IN", "BOD_Source", "BOD_Sample_Vol_4", _
"BOD_Dilution_1", "BOD_Blank_IDO_4", "BOD_Blank_FDO_4", "BOD_Sample_Vol_7", "BOD_Dilution_2", _
"BOD_Blank_IDO_7", "BOD_Blank_FDO_7", "BOD_Seed_IDO_13", "BOD_Seed_FDO_13", "BOD_Seed_IDO_14", _
"BOD_Seed_FDO_14", "BOD_Influent_IDO_15", "BOD_Influent_FDO_15", "BOD_Influent_IDO_16", _
"BOD_Influent_FDO_16", "BOD_Effluent_IDO_20", "BOD_Effluent_FDO_20", "BOD_Effluent_IDO_21", "BOD_Effluent_FDO_21", _
"In_BOD_Concentration", "Out_BOD_Concentration")
'Make Data Sheet active
dataWorksheet.Activate
Dim myDate As Date
myDate = DateValue(bodWorksheet.Range("BOD_Lab_Date").Value)
Dim yearAsString As String, monthAsString As String, dayAsString As String
yearAsString = Format(myDate, "yyyy")
monthAsString = Format(myDate, "mm")
dayAsString = Format(myDate, "dd")
Dim reportNumberText As String
reportNumberText = "NP" & yearAsString & monthAsString & dayAsString
Debug.Print "reportNumberText = "; reportNumberText
'Determine emptyRow
Dim emptyRow As Integer
emptyRow = WorksheetFunction.CountA(Range("A:A")) + 1
'Transfer information
'Sample Number
dataWorksheet.Cells(emptyRow, 1).Value = reportNumberText
'Date and Time Collected
dataWorksheet.Cells(emptyRow, 2) = bodWorksheet.Range("BOD_Lab_Date").Value
dataWorksheet.Cells(emptyRow, 3) = Format(bodWorksheet.Range("BOD_Collection_Date").Value, "dd-mmm-yyyy")
dataWorksheet.Cells(emptyRow, 4) = Format(bodWorksheet.Range("BOD_Read_On_Date").Value, "dd-mmm-yyyy")
Dim i As Integer, j As Integer
For i = LBound(myRanges) To UBound(myRanges)
j = i + 4
dataWorksheet.Cells(emptyRow, j) = bodWorksheet.Range(myRanges(i)).Value
Debug.Print "dataWorksheet.Cells(" & emptyRow & "," & j & ") " & dataWorksheet.Cells(emptyRow, j).Value
Next i
ActiveWorkbook.Save
suspendedSolidsWorksheet.Activate
Range("SS_Date").Select
End Sub
Is "BOD_LAB_DATE" more than one cell? Maybe your method usually works also, but I usually would copy a range of cells by reversing your order and using copy, like so:
bodWorksheet.Range("BOD_Lab_Date").Copy dataWorksheet.Cells(emptyRow, 2)