Страницы: 1
RSS
WEB API или другие способы подключения к PDF Creator 24
 
Привет!
Появилась задачка в конвертации картинок в PDF и объединение их в один файл. Все это необходимо запускать из Excel.

Из разрешённого ПО есть https://www.pdf24.org/ru/. Но читая не могу найти информацию по API. Может кто-то заморачивался подобным?

Сейчас не объединяя подряд печатаю файлы (PDF, JPG, PNG) с помощью горячих клавиш и VBA.
 
ImageMagick, набор программ (консольных утилит) для чтения и редактирования файлов множества графических форматов. Является свободным и кроссплатформенным продуктом. Или у вас можно только   https://www.pdf24.org/ru/ ?
 
bigorq,
к сожалению, только https://www.pdf24.org/ru/ + их же ПО можно установить на ПК народу. Но там тоже не нашел вариантов как выполнить задуманное кроме горячих клавиш
Изменено: evgeniygeo - 09.02.2026 16:53:15
 
нашел все API, теперь грызу его)))
 
PDF24 Creator Manual оно?
 
irabel,
оно, большое Вам спасибо!
Жаль, что получилось подключиться только через power shell, а наша безопасность против такого подхода...(
Пробовал через WEB API, но даже не успев закончить мне подсветили на конф данных...(

Скрытый текст

Ищу теперь другие варианты
Изменено: evgeniygeo - 15.02.2026 13:00:01
 
evgeniygeo, а нельзя закинуть все картинки на один лист Excel и оттуда уже сохранить в один pdf файл?
 
irabel,
пробовал, получается отвратительное качество и проблемы с качеством сформированного pdf(
 
evgeniygeo, здравствуйте.
Могу попробовать написать dll которая будет загружать картинки на лист Excel'а или сразу формировать pdf файл. Такое у Вас разрешено?
Изменено: Aлeкceй - 16.02.2026 11:26:07
 
del
Изменено: nilske - 30.03.2026 06:25:36
 
Aлeкceй,
большое спасибо за предложение, но боюсь будет аналогичное отношение как и к bat  :D
Изменено: evgeniygeo - 16.02.2026 12:02:06
 
можно без dll. у power point есть варианты преобразования внутренними средствами. Пошукаю. то есть в офисе есть библы которые могут сделать это без внешних апи.
 
Можно без апи.

Вот пример конвертера (будет работать , если на компе стоит power point)

5. Alt+F8 → выберите "ConvertImagesToPDF" → выполнить.

Пример excel приложен. (запустить макрос, выбрать картинки (можно разных форматов), слепить все в один pdf (по мере выполнения процедуры показать куда и под каким именем сохранить pdf)


Процедура:
Код
Sub ConvertImagesToPDF_Advanced()
    
    Dim fd As FileDialog
    Dim filesList As New Collection
    Dim i As Long
    Dim outputPath As String
    
    ' ---- Выбор картинок ----
    Set fd = Application.FileDialog(msoFileDialogFilePicker)
    With fd
        .Title = "Выберите изображения"
        .AllowMultiSelect = True
        .Filters.Clear
        .Filters.Add "Изображения", "*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tif;*.tiff"
        If .Show <> -1 Then Exit Sub
        For i = 1 To .SelectedItems.Count
            filesList.Add .SelectedItems(i)
        Next i
    End With
    
    ' ---- Настройки через UserForm-заменитель (InputBox) ----
    Dim pageSize As String
    pageSize = InputBox("Выберите размер страницы:" & vbCrLf & vbCrLf & _
                        "1 - A4 (210 x 297 мм)" & vbCrLf & _
                        "2 - A3 (297 x 420 мм)" & vbCrLf & _
                        "3 - Letter (216 x 279 мм)" & vbCrLf & _
                        "4 - Подогнать под размер картинки" & vbCrLf & _
                        "5 - Квадрат (210 x 210 мм)", _
                        "Настройки PDF", "1")
    
    If pageSize = "" Then Exit Sub
    
    Dim orientation As String
    orientation = InputBox("Ориентация страницы:" & vbCrLf & vbCrLf & _
                           "1 - Книжная (портрет)" & vbCrLf & _
                           "2 - Альбомная (ландшафт)" & vbCrLf & _
                           "3 - Авто (по ориентации картинки)", _
                           "Ориентация", "3")
    
    If orientation = "" Then Exit Sub
    
    ' ---- Путь сохранения ----
    outputPath = Application.GetSaveAsFilename( _
        InitialFileName:="Output.pdf", _
        FileFilter:="PDF (*.pdf), *.pdf", _
        Title:="Сохранить PDF")
    
    If outputPath = "False" Then Exit Sub
    If LCase(Right(outputPath, 4)) <> ".pdf" Then outputPath = outputPath & ".pdf"
    
    ' ---- Создание PDF ----
    Application.StatusBar = "Создание PDF..."
    
    Dim pptApp As Object, pptPres As Object
    Dim pptSlide As Object, pptShape As Object
    Dim wasOpen As Boolean
    
    On Error Resume Next
    Set pptApp = GetObject(, "PowerPoint.Application")
    wasOpen = Not (pptApp Is Nothing)
    On Error GoTo 0
    
    If pptApp Is Nothing Then
        Set pptApp = CreateObject("PowerPoint.Application")
        pptApp.Visible = True
    End If
    
    Set pptPres = pptApp.Presentations.Add(WithWindow:=False)
    
    ' Размеры страниц (в пунктах, 1 мм ≈ 2.835 pt)
    Dim pgW As Single, pgH As Single
    Select Case pageSize
        Case "1": pgW = 595.28: pgH = 841.89   ' A4
        Case "2": pgW = 841.89: pgH = 1190.55  ' A3
        Case "3": pgW = 612: pgH = 792          ' Letter
        Case "4": pgW = 0: pgH = 0              ' Авто
        Case "5": pgW = 595.28: pgH = 595.28    ' Квадрат
        Case Else: pgW = 595.28: pgH = 841.89
    End Select
    
    For i = 1 To filesList.Count
        
        Application.StatusBar = "Обработка " & i & "/" & filesList.Count & "..."
        DoEvents
        
        Dim fPath As String
        fPath = filesList(i)
        If Dir(fPath) = "" Then GoTo SkipFile
        
        ' Сначала нужно узнать размер картинки для режима "Авто"
        ' Создаём временный слайд для получения размеров
        Dim tempSlide As Object
        Set tempSlide = pptPres.Slides.Add(pptPres.Slides.Count + 1, 12)
        
        Set pptShape = tempSlide.Shapes.AddPicture( _
            FileName:=fPath, LinkToFile:=False, _
            SaveWithDocument:=True, Left:=0, Top:=0)
        
        Dim imgW As Single, imgH As Single
        imgW = pptShape.Width
        imgH = pptShape.Height
        
        ' Определяем размер слайда
        Dim curW As Single, curH As Single
        
        If pageSize = "4" Then
            ' Подгоняем слайд под картинку (макс 5000 pt ограничение PPT)
            curW = imgW
            curH = imgH
            ' Ограничиваем максимальный размер
            If curW > 5000 Or curH > 5000 Then
                Dim maxDim As Single
                maxDim = IIf(curW > curH, curW, curH)
                Dim ratio As Single
                ratio = 5000 / maxDim
                curW = curW * ratio
                curH = curH * ratio
            End If
        Else
            curW = pgW
            curH = pgH
            
            ' Авто-ориентация
            If orientation = "3" Then
                ' Если картинка альбомная, а страница книжная — переворачиваем
                If (imgW > imgH) And (curW < curH) Then
                    Dim tmp As Single
                    tmp = curW: curW = curH: curH = tmp
                ElseIf (imgH > imgW) And (curH < curW) Then
                    tmp = curW: curW = curH: curH = tmp
                End If
            ElseIf orientation = "2" Then
                ' Принудительно альбомная
                If curW < curH Then
                    tmp = curW: curW = curH: curH = tmp
                End If
            End If
        End If
        
        ' Удаляем временный слайд
        pptShape.Delete
        tempSlide.Delete
        
        ' Устанавливаем размер слайда (PowerPoint меняет для всех слайдов,
        ' но при экспорте в PDF это работает корректно для последнего размера)
        ' Для разных размеров страниц — используем единый размер
        pptPres.PageSetup.SlideWidth = curW
        pptPres.PageSetup.SlideHeight = curH
        
        ' Добавляем слайд
        Set pptSlide = pptPres.Slides.Add(pptPres.Slides.Count + 1, 12)
        
        ' Вставляем картинку
        Set pptShape = pptSlide.Shapes.AddPicture( _
            FileName:=fPath, LinkToFile:=False, _
            SaveWithDocument:=True, Left:=0, Top:=0)
        
        ' Масштабируем
        Dim scX As Single, scY As Single, sc As Single
        
        If pageSize = "4" Then
            ' Растягиваем на весь слайд
            pptShape.LockAspectRatio = False
            pptShape.Width = curW
            pptShape.Height = curH
            pptShape.Left = 0
            pptShape.Top = 0
        Else
            scX = curW / pptShape.Width
            scY = curH / pptShape.Height
            sc = IIf(scX < scY, scX, scY)
            
            pptShape.LockAspectRatio = True
            pptShape.Width = pptShape.Width * sc
            ' Центрируем
            pptShape.Left = (curW - pptShape.Width) / 2
            pptShape.Top = (curH - pptShape.Height) / 2
        End If
        
SkipFile:
    Next i
    
    ' Экспорт
    If pptPres.Slides.Count > 0 Then
        Application.StatusBar = "Экспорт в PDF..."
        pptPres.SaveAs outputPath, 32  ' ppSaveAsPDF
        
        pptPres.Close
        If Not wasOpen Then pptApp.Quit
        
        Application.StatusBar = False
        MsgBox "PDF создан: " & outputPath & vbCrLf & _
               "Страниц: " & filesList.Count, vbInformation
    Else
        pptPres.Close
        If Not wasOpen Then pptApp.Quit
        Application.StatusBar = False
        MsgBox "Нет изображений для экспорта!", vbExclamation
    End If
    
End sub
 
Цитата
написал:
Может кто-то заморачивался подобным?
заморачивался) решение выше.
 
Лалыч,
насколько я понимаю, в этом решении для моего случая есть несколько проблем:
1. Формат размера картинок разный, а Power Point делает их одинаковыми по последней картинке
2. Иногда, в конвертации и объединении участвует PDF,


Пока решил с помощью api вышеописанного ПО, но зарубили ИБ. Сейчас смотрю в сторону Word.
 
Цитата
написал:
насколько я понимаю, в этом решении для моего случая есть несколько проблем:1. Формат размера картинок разный, а Power Point делает их одинаковыми по последней картинке2. Иногда, в конвертации и объединении участвует PDF,
ну вот можно и так, немного с юмором, но вариант рабочий, осуществим анализ на уровне пикселя, мать его:)

Что нужно для склейки (хотя бы одно из):
  1. Adobe Acrobat Pro — дорого, но работает из коробки
  2. PDFtk (бесплатно) — скачать с pdflabs.com, установить, и всё
  3. Ghostscript (бесплатно) — ghostscript.com
  4. Python + PyPDF2 — pip install PyPDF2
Код
Sub ConvertImagesToPDF_UltimateEdition()
    '==========================================================
    ' ULTIMATE PDF CONVERTER v2.0
    ' Теперь с блэкджеком и поддержкой PDF!
    ' Каждая картинка получает СВОЙ размер страницы.
    ' PDF-файлы тоже приглашены на вечеринку.
    '==========================================================
    
    Dim fd As FileDialog
    Dim filesList As New Collection
    Dim i As Long
    Dim outputPath As String
    
    ' ---- Выбор файлов (теперь и PDF!) ----
    Set fd = Application.FileDialog(msoFileDialogFilePicker)
    With fd
        .Title = "Выберите изображения и/или PDF"
        .AllowMultiSelect = True
        .Filters.Clear
        .Filters.Add "Изображения и PDF", "*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tif;*.tiff;*.pdf"
        .Filters.Add "Только изображения", "*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tif;*.tiff"
        .Filters.Add "Только PDF", "*.pdf"
        If .Show <> -1 Then Exit Sub
        For i = 1 To .SelectedItems.Count
            filesList.Add .SelectedItems(i)
        Next i
    End With
    
    If filesList.Count = 0 Then
        MsgBox "Вы не выбрали ни одного файла. Грустно.", vbExclamation
        Exit Sub
    End If
    
    ' ---- Настройки ----
    Dim pageSize As String
    pageSize = InputBox("Размер страницы:" & vbCrLf & vbCrLf & _
                        "1 - A4 (210 x 297 мм)" & vbCrLf & _
                        "2 - A3 (297 x 420 мм)" & vbCrLf & _
                        "3 - Letter (216 x 279 мм)" & vbCrLf & _
                        "4 - Подогнать под размер картинки (РЕКОМЕНДУЕТСЯ)" & vbCrLf & _
                        "5 - Квадрат (210 x 210 мм)", _
                        "Настройки PDF", "4")
    If pageSize = "" Then Exit Sub
    
    Dim orientation As String
    If pageSize <> "4" Then
        orientation = InputBox("Ориентация страницы:" & vbCrLf & vbCrLf & _
                               "1 - Книжная (портрет)" & vbCrLf & _
                               "2 - Альбомная (ландшафт)" & vbCrLf & _
                               "3 - Авто (по ориентации картинки)", _
                               "Ориентация", "3")
        If orientation = "" Then Exit Sub
    Else
        orientation = "3"
    End If
    
    ' ---- Путь сохранения ----
    outputPath = Application.GetSaveAsFilename( _
        InitialFileName:="Output.pdf", _
        FileFilter:="PDF (*.pdf), *.pdf", _
        Title:="Сохранить итоговый PDF")
    
    If CStr(outputPath) = "False" Then Exit Sub
    If LCase(Right(outputPath, 4)) <> ".pdf" Then outputPath = outputPath & ".pdf"
    
    ' ---- Подготовка временной папки ----
    Dim tempFolder As String
    tempFolder = Environ("TEMP") & "\VBA_PDF_Merge_" & Format(Now, "yyyymmddhhnnss") & "\"
    MkDir tempFolder
    
    ' ---- Разделяем файлы на картинки и PDF ----
    Dim imageFiles As New Collection
    Dim pdfFiles As New Collection  ' Коллекция: ключ = порядковый номер, значение = путь
    Dim fileOrder() As String       ' Массив для хранения порядка: "IMG" или "PDF"
    Dim fileIndex() As Long         ' Индекс в соответствующей коллекции
    
    ReDim fileOrder(1 To filesList.Count)
    ReDim fileIndex(1 To filesList.Count)
    
    Dim imgCount As Long, pdfCount As Long
    imgCount = 0: pdfCount = 0
    
    For i = 1 To filesList.Count
        Dim ext As String
        ext = LCase(Right(filesList(i), 4))
        If ext = ".pdf" Then
            pdfCount = pdfCount + 1
            pdfFiles.Add filesList(i)
            fileOrder(i) = "PDF"
            fileIndex(i) = pdfCount
        Else
            imgCount = imgCount + 1
            imageFiles.Add filesList(i)
            fileOrder(i) = "IMG"
            fileIndex(i) = imgCount
        End If
    Next i
    
    ' ---- Конвертируем каждую картинку в отдельный PDF через PowerPoint ----
    Dim tempPDFs As New Collection  ' Все временные PDF в правильном порядке
    
    If imgCount > 0 Then
        Dim pptApp As Object, wasOpen As Boolean
        
        On Error Resume Next
        Set pptApp = GetObject(, "PowerPoint.Application")
        wasOpen = Not (pptApp Is Nothing)
        On Error GoTo 0
        
        If pptApp Is Nothing Then
            Set pptApp = CreateObject("PowerPoint.Application")
            pptApp.Visible = True
        End If
        
        ' Размеры страниц (в пунктах)
        Dim pgW As Single, pgH As Single
        Select Case pageSize
            Case "1": pgW = 595.28: pgH = 841.89   ' A4
            Case "2": pgW = 841.89: pgH = 1190.55  ' A3
            Case "3": pgW = 612: pgH = 792          ' Letter
            Case "4": pgW = 0: pgH = 0              ' Авто
            Case "5": pgW = 595.28: pgH = 595.28    ' Квадрат
            Case Else: pgW = 595.28: pgH = 841.89
        End Select
        
        ' === КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: каждая картинка = отдельная презентация = отдельный PDF ===
        Dim imgIdx As Long
        For imgIdx = 1 To imageFiles.Count
            
            Application.StatusBar = "Конвертация изображения " & imgIdx & "/" & imgCount & "... ☕"
            DoEvents
            
            Dim fPath As String
            fPath = imageFiles(imgIdx)
            If Dir(fPath) = "" Then GoTo SkipImage
            
            ' Создаём ОТДЕЛЬНУЮ презентацию для каждой картинки!
            ' Потому что PowerPoint — диктатор и не даёт слайдам быть разного размера
            Dim pptPres As Object
            Set pptPres = pptApp.Presentations.Add(WithWindow:=False)
            
            ' Сначала добавляем картинку чтобы узнать размер
            Dim pptSlide As Object, pptShape As Object
            Set pptSlide = pptPres.Slides.Add(1, 12) ' ppLayoutBlank
            
            Set pptShape = pptSlide.Shapes.AddPicture( _
                Filename:=fPath, LinkToFile:=False, _
                SaveWithDocument:=True, Left:=0, Top:=0)
            
            Dim imgW As Single, imgH As Single
            imgW = pptShape.Width
            imgH = pptShape.Height
            
            ' Определяем размер страницы
            Dim curW As Single, curH As Single
            
            If pageSize = "4" Then
                curW = imgW
                curH = imgH
                ' Ограничение PowerPoint ~5000 pt
                If curW > 5000 Or curH > 5000 Then
                    Dim maxDim As Single
                    maxDim = IIf(curW > curH, curW, curH)
                    Dim scaleRatio As Single
                    scaleRatio = 5000 / maxDim
                    curW = curW * scaleRatio
                    curH = curH * scaleRatio
                End If
                ' Минимальный размер
                If curW < 72 Then curW = 72
                If curH < 72 Then curH = 72
            Else
                curW = pgW
                curH = pgH
                
                ' Авто-ориентация
                Dim tmp As Single
                If orientation = "3" Then
                    If (imgW > imgH) And (curW < curH) Then
                        tmp = curW: curW = curH: curH = tmp
                    ElseIf (imgH > imgW) And (curH < curW) Then
                        tmp = curW: curW = curH: curH = tmp
                    End If
                ElseIf orientation = "2" Then
                    If curW < curH Then
                        tmp = curW: curW = curH: curH = tmp
                    End If
                End If
            End If
            
            ' Устанавливаем размер слайда (теперь это не мешает другим!)
            pptPres.PageSetup.SlideWidth = curW
            pptPres.PageSetup.SlideHeight = curH
            
            ' Масштабируем картинку
            If pageSize = "4" Then
                pptShape.LockAspectRatio = False
                pptShape.Width = curW
                pptShape.Height = curH
                pptShape.Left = 0
                pptShape.Top = 0
            Else
                Dim scX As Single, scY As Single, sc As Single
                scX = curW / pptShape.Width
                scY = curH / pptShape.Height
                sc = IIf(scX < scY, scX, scY)
                
                pptShape.LockAspectRatio = True
                pptShape.Width = pptShape.Width * sc
                pptShape.Left = (curW - pptShape.Width) / 2
                pptShape.Top = (curH - pptShape.Height) / 2
            End If
            
            ' Сохраняем как отдельный PDF
            Dim tempPdfPath As String
            tempPdfPath = tempFolder & "img_" & Format(imgIdx, "0000") & ".pdf"
            pptPres.SaveAs tempPdfPath, 32  ' ppSaveAsPDF
            pptPres.Close
            
SkipImage:
            DoEvents
        Next imgIdx
        
        ' Закрываем PowerPoint если мы его открыли
        If Not wasOpen Then
            On Error Resume Next
            pptApp.Quit
            On Error GoTo 0
        End If
    End If
    
    ' ---- Собираем все PDF в правильном порядке ----
    ' Счётчики для отслеживания текущего индекса в каждой коллекции
    Dim curImgPdf As Long: curImgPdf = 0
    Dim curPdfFile As Long: curPdfFile = 0
    
    For i = 1 To filesList.Count
        If fileOrder(i) = "IMG" Then
            curImgPdf = curImgPdf + 1
            Dim imgPdfPath As String
            imgPdfPath = tempFolder & "img_" & Format(curImgPdf, "0000") & ".pdf"
            If Dir(imgPdfPath) <> "" Then
                tempPDFs.Add imgPdfPath
            End If
        ElseIf fileOrder(i) = "PDF" Then
            curPdfFile = curPdfFile + 1
            tempPDFs.Add pdfFiles(curPdfFile)
        End If
    Next i
    
    ' ---- Объединяем все PDF ----
    If tempPDFs.Count = 0 Then
        MsgBox "Нечего объединять. Все файлы оказались пустышками! :f09f98b1:", vbExclamation
        GoTo Cleanup
    End If
    
    Application.StatusBar = "Объединение PDF... Почти готово! :f09f8f81:"
    
    If tempPDFs.Count = 1 Then
        ' Только один файл — просто копируем
        FileCopy tempPDFs(1), outputPath
    Else
        ' Объединяем через Adobe Acrobat (если есть) или через Shell
        Dim merged As Boolean
        merged = False
        
        ' Попытка 1: Adobe Acrobat Pro
        merged = MergeWithAcrobat(tempPDFs, outputPath)
        
        ' Попытка 2: PDFtk (бесплатная утилита)
        If Not merged Then
            merged = MergeWithPDFtk(tempPDFs, outputPath)
        End If
        
        ' Попытка 3: Ghostscript
        If Not merged Then
            merged = MergeWithGhostscript(tempPDFs, outputPath)
        End If
        
        ' Попытка 4: Python (если установлен)
        If Not merged Then
            merged = MergeWithPython(tempPDFs, outputPath, tempFolder)
        End If
        
        If Not merged Then
            ' Последний вариант — сохраняем отдельные файлы
            Dim fallbackFolder As String
            fallbackFolder = Left(outputPath, InStrRev(outputPath, "\"))
            
            Dim msg As String
            msg = "Не удалось объединить PDF! :f09f98a4:" & vbCrLf & vbCrLf & _
                  "Не найден ни один инструмент для склейки:" & vbCrLf & _
                  "• Adobe Acrobat Pro" & vbCrLf & _
                  "• PDFtk (pdftk.exe)" & vbCrLf & _
                  "• Ghostscript (gswin64c.exe)" & vbCrLf & _
                  "• Python с PyPDF2/pikepdf" & vbCrLf & vbCrLf & _
                  "Сохранить отдельные PDF в папку?"
            
            If MsgBox(msg, vbYesNo + vbQuestion) = vbYes Then
                Dim outFolder As String
                outFolder = Left(outputPath, InStrRev(outputPath, "\")) & "PDF_Pages\"
                On Error Resume Next
                MkDir outFolder
                On Error GoTo 0
                
                For i = 1 To tempPDFs.Count
                    Dim destFile As String
                    destFile = outFolder & "Page_" & Format(i, "000") & ".pdf"
                    FileCopy tempPDFs(i), destFile
                Next i
                
                MsgBox "Отдельные PDF сохранены в:" & vbCrLf & outFolder, vbInformation
            End If
            GoTo Cleanup
        End If
    End If
    
    Application.StatusBar = False
    MsgBox ":f09f8e89: PDF создан: " & outputPath & vbCrLf & _
           "Страниц/файлов: " & tempPDFs.Count & vbCrLf & _
           "(Картинок: " & imgCount & ", PDF: " & pdfCount & ")", vbInformation

Cleanup:
    Application.StatusBar = False
    ' Чистим временные файлы
    On Error Resume Next
    Dim f As String
    f = Dir(tempFolder & "*.*")
    Do While f <> ""
        Kill tempFolder & f
        f = Dir
    Loop
    RmDir tempFolder
    On Error GoTo 0
    
End Sub

' ============================================================
' ФУНКЦИИ ОБЪЕДИНЕНИЯ PDF
' (Четыре всадника PDF-апокалипсиса)
' ============================================================

Function MergeWithAcrobat(ByRef pdfFiles As Collection, ByVal outputPath As String) As Boolean
    ' Пытаемся использовать Adobe Acrobat Pro
    ' (Если у вас есть лицензия, вы — счастливчик!)
    
    On Error GoTo AcrobatFail
    
    Dim acroApp As Object
    Set acroApp = CreateObject("AcroExch.App")
    
    Dim primaryDoc As Object
    Set primaryDoc = CreateObject("AcroExch.PDDoc")
    
    ' Открываем первый PDF
    If Not primaryDoc.Open(pdfFiles(1)) Then GoTo AcrobatFail
    
    ' Добавляем остальные
    Dim i As Long
    For i = 2 To pdfFiles.Count
        Dim nextDoc As Object
        Set nextDoc = CreateObject("AcroExch.PDDoc")
        
        If nextDoc.Open(pdfFiles(i)) Then
            Dim numPages As Long
            numPages = nextDoc.GetNumPages
            primaryDoc.InsertPages primaryDoc.GetNumPages - 1, nextDoc, 0, numPages, False
            nextDoc.Close
        End If
        Set nextDoc = Nothing
    Next i
    
    ' Сохраняем
    primaryDoc.Save 1, outputPath  ' PDSaveFull
    primaryDoc.Close
    acroApp.Exit
    
    Set primaryDoc = Nothing
    Set acroApp = Nothing
    
    MergeWithAcrobat = True
    Exit Function
    
AcrobatFail:
    On Error Resume Next
    If Not primaryDoc Is Nothing Then primaryDoc.Close
    If Not acroApp Is Nothing Then acroApp.Exit
    Set primaryDoc = Nothing
    Set acroApp = Nothing
    On Error GoTo 0
    MergeWithAcrobat = False
End Function

Function MergeWithPDFtk(ByRef pdfFiles As Collection, ByVal outputPath As String) As Boolean
    ' PDFtk — бесплатный швейцарский нож для PDF
    ' Скачать: https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
    
    On Error GoTo PDFtkFail
    
    ' Ищем pdftk
    Dim pdftkPath As String
    pdftkPath = FindExecutable("pdftk.exe")
    
    If pdftkPath = "" Then
        MergeWithPDFtk = False
        Exit Function
    End If
    
    ' Собираем команду
    Dim cmd As String
    cmd = """" & pdftkPath & """"
    
    Dim i As Long
    For i = 1 To pdfFiles.Count
        cmd = cmd & " """ & pdfFiles(i) & """"
    Next i
    
    cmd = cmd & " cat output """ & outputPath & """"
    
    ' Запускаем и ждём
    Dim exitCode As Long
    exitCode = RunAndWait(cmd)
    
    MergeWithPDFtk = (Dir(outputPath) <> "")
    Exit Function
    
PDFtkFail:
    MergeWithPDFtk = False
End Function

Function MergeWithGhostscript(ByRef pdfFiles As Collection, ByVal outputPath As String) As Boolean
    ' Ghostscript — олдскульный, но надёжный как танк
    
    On Error GoTo GSFail
    
    Dim gsPath As String
    gsPath = FindExecutable("gswin64c.exe")
    If gsPath = "" Then gsPath = FindExecutable("gswin32c.exe")
    
    If gsPath = "" Then
        MergeWithGhostscript = False
        Exit Function
    End If
    
    Dim cmd As String
    cmd = """" & gsPath & """ -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite" & _
          " -sOutputFile=""" & outputPath & """"
    
    Dim i As Long
    For i = 1 To pdfFiles.Count
        cmd = cmd & " """ & pdfFiles(i) & """"
    Next i
    
    Dim exitCode As Long
    exitCode = RunAndWait(cmd)
    
    MergeWithGhostscript = (Dir(outputPath) <> "")
    Exit Function
    
GSFail:
    MergeWithGhostscript = False
End Function

Function MergeWithPython(ByRef pdfFiles As Collection, ByVal outputPath As String, _
                         ByVal tempFolder As String) As Boolean
    ' Python — потому что почему бы и нет?
    ' Нужен PyPDF2 или pikepdf: pip install PyPDF2
    
    On Error GoTo PythonFail
    
    Dim pythonPath As String
    pythonPath = FindExecutable("python.exe")
    If pythonPath = "" Then pythonPath = FindExecutable("python3.exe")
    
    If pythonPath = "" Then
        MergeWithPython = False
        Exit Function
    End If
    
    ' Создаём Python-скрипт
    Dim scriptPath As String
    scriptPath = tempFolder & "merge_pdf.py"
    
    Dim f As Integer
    f = FreeFile
    Open scriptPath For Output As #f
    
    Print #f, "import sys"
    Print #f, "try:"
    Print #f, "    from PyPDF2 import PdfMerger"
    Print #f, "    merger = PdfMerger()"
    
    Dim i As Long
    For i = 1 To pdfFiles.Count
        Print #f, "    merger.append(r'" & Replace(pdfFiles(i), "'", "\'") & "')"
    Next i
    
    Print #f, "    merger.write(r'" & Replace(outputPath, "'", "\'") & "')"
    Print #f, "    merger.close()"
    Print #f, "    print('OK')"
    Print #f, "except ImportError:"
    Print #f, "    try:"
    Print #f, "        import pikepdf"
    Print #f, "        pdf = pikepdf.Pdf.new()"
    
    For i = 1 To pdfFiles.Count
        Print #f, "        src = pikepdf.Pdf.open(r'" & Replace(pdfFiles(i), "'", "\'") & "')"
        Print #f, "        pdf.pages.extend(src.pages)"
    Next i
    
    Print #f, "        pdf.save(r'" & Replace(outputPath, "'", "\'") & "')"
    Print #f, "        print('OK')"
    Print #f, "    except ImportError:"
    Print #f, "        print('FAIL')"
    Print #f, "        sys.exit(1)"
    Print #f, "except Exception as e:"
    Print #f, "    print(f'ERROR: {e}')"
    Print #f, "    sys.exit(1)"
    
    Close #f
    
    ' Запускаем
    Dim cmd As String
    cmd = """" & pythonPath & """ """ & scriptPath & """"
    
    Dim exitCode As Long
    exitCode = RunAndWait(cmd)
    
    MergeWithPython = (Dir(outputPath) <> "")
    Exit Function
    
PythonFail:
    MergeWithPython = False
End Function

' ============================================================
' ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
' ============================================================

Function FindExecutable(ByVal exeName As String) As String
    ' Ищем исполняемый файл в PATH и типичных местах
    
    Dim testPaths As Variant
    testPaths = Array( _
        exeName, _
        "C:\Program Files\PDFtk\bin\" & exeName, _
        "C:\Program Files (x86)\PDFtk\bin\" & exeName, _
        "C:\Program Files\gs\gs10.02.1\bin\" & exeName, _
        "C:\Program Files\gs\gs10.01.2\bin\" & exeName, _
        "C:\Program Files\gs\gs10.00.0\bin\" & exeName, _
        "C:\Program Files\gs\gs9.56.1\bin\" & exeName, _
        "C:\Program Files (x86)\gs\gs9.56.1\bin\" & exeName, _
        Environ("LOCALAPPDATA") & "\Programs\Python\Python312\" & exeName, _
        Environ("LOCALAPPDATA") & "\Programs\Python\Python311\" & exeName, _
        Environ("LOCALAPPDATA") & "\Programs\Python\Python310\" & exeName, _
        "C:\Python312\" & exeName, _
        "C:\Python311\" & exeName, _
        "C:\Python310\" & exeName _
    )
    
    ' Сначала проверяем через WHERE (для файлов в PATH)
    Dim tempFile As String
    tempFile = Environ("TEMP") & "\vba_which_" & Replace(exeName, ".", "_") & ".txt"
    
    On Error Resume Next
    Shell "cmd /c where " & exeName & " > """ & tempFile & """ 2>nul", vbHide
    
    ' Даём время на выполнение
    Dim waitStart As Single
    waitStart = Timer
    Do While Timer - waitStart < 2
        DoEvents
        If Dir(tempFile) <> "" Then
            If FileLen(tempFile) > 0 Then Exit Do
        End If
    Loop
    
    If Dir(tempFile) <> "" Then
        If FileLen(tempFile) > 0 Then
            Dim f As Integer
            f = FreeFile
            Dim line As String
            Open tempFile For Input As #f
            If Not EOF(f) Then
                Line Input #f, line
                line = Trim(line)
                If line <> "" And Dir(line) <> "" Then
                    Close #f
                    Kill tempFile
                    FindExecutable = line
                    Exit Function
                End If
            End If
            Close #f
        End If
        Kill tempFile
    End If
    On Error GoTo 0
    
    ' Проверяем известные пути
    Dim p As Variant
    For Each p In testPaths
        If p <> exeName Then  ' Пропускаем голое имя
            On Error Resume Next
            If Dir(CStr(p)) <> "" Then
                FindExecutable = CStr(p)
                Exit Function
            End If
            On Error GoTo 0
        End If
    Next p
    
    FindExecutable = ""
End Function

Function RunAndWait(ByVal cmd As String) As Long
    ' Запускает команду и ждёт завершения
    
    Dim wsh As Object
    Set wsh = CreateObject("WScript.Shell")
    
    RunAndWait = wsh.Run("cmd /c " & cmd, 0, True)  ' 0 = скрытое окно, True = ждать
    
    Set wsh = Nothing
End Function
 
Лалыч,
жаль, что на корпоративные ПК ничего из этого поставить не могу
Страницы: 1
Читают тему
Наверх