Страница 1 из 2

Создать PDF-файлы из картинок по схожим признакам в именах

Добавлено: Вс янв 20, 2019 15:18
kabatov
Добрый день.

Есть потребность автоматизировать процесс создания многостраничных pdf. Суть такова: в активной панели открыта папка с большим количеством tiff. Имена файлов не совсем произвольные и имеют следующий вид: scan_001-01.tif, scan_001-02.tif <...> scan_001-55.tif, image_001.tif, image_002.tif <...> image_850.tif, pic 00.tif, pic 01.tif <...> pic 99.tif и т. д. Необходимо сделать так, чтобы по нажатии кнопки на панели TC в открытой в другой панели папке были созданы многостраничные pdf из групп файлов, определяемых умным скриптом автоматически (т. е. scan_001.pdf, image.pdf, pic.pdf). На деле в именах файлов обычно гораздо больше порядка, но хотелось бы получить средство, не зависящее от наличия разделителя и четко заданной маски имени. Однако, если последнее условие в принципе не выполнимо, подойдет и что-то раз и навсегда настроенное на работу с файлами, именованными строго определенным образом.
Для создания pdf используется утилита NConvert разработчика программы XnView.
Все, что удалось сделать самостоятельно по прочтении большого количества здешних тем, это кнопка, создающая pdf из выделенных в активной панели файлов:

Код: Выделить всё

TOTALCMD#BAR#DATA
%COMMANDER_PATH%\Utils\NConvert\nconvert.exe
-multi -o "%T%%" -out pdf -q 100 -c 3 -l "%L"
shell32.dll,250
выделенные файлы — в многостраничный PDF
Из кода кнопки видно, какие параметры конвертации требуются. Эта кнопка, конечно, облегчит работу, но не решает описанной задачи. Заранее спасибо всем, кто готов поломать голову над решением.

Добавлено: Вс янв 20, 2019 20:40
Flasher

Код: Выделить всё

'================================ VBS ================================
' Создать PDFs из картинок активного каталога согласно именным группам

' Условие:   Путь запуска — пустой

' Параметры: "<путь назначения>" <маска расширений> <формат сжатия>
' Форматы сжатия: 1 (Fax), 2 (Rle), 3 (LZW), 4 (ZIP), 5 (JPEG)
' /min — ключ cмены скрытой консоли на свёрнутую в панель задач

' Примеры:   "%T" *.tif 3 /min
'            "%T" *.jpg;*jpeg;*.png;*.tif;*.tiff 5

Option Explicit
'========================== Путь к NConvert ==========================
Const NConv = "%COMMANDER_PATH%\Utils\NConvert\nconvert.exe"
'================================================ Автор: Flasher © ===

Dim A, WSS, ShA, Dic, Kit, FSO, Coll, Sort, Rgx, CDir, _
Itms, Items, ODir, M, List, F, Name, BN0, BN, Item, i, S
Const Title = " Объединение картинок в PDF       "
Set A = WSH.Arguments: If A.UnNamed.Count <> 3 Then _
MsgBox "Укажите 3 параметра!", 4144, Title : WSH.Quit

Set WSS = CreateObject("WScript.Shell")
Set ShA = CreateObject("Shell.Application")
Set Dic = CreateObject("Scripting.Dictionary")
Set Kit = CreateObject("Scripting.Dictionary")
Set FSO = CreateObject("Scripting.FileSystemObject")
Set Coll = CreateObject("System.Collections.ArrayList")
Set Sort = CreateObject("System.Collections.SortedList")
Set Rgx = New RegExp : CDir = WSS.CurrentDirectory
Set Itms  = ShA.NameSpace(CDir).Items
Set Items = ShA.NameSpace(CDir).Items

Items.Filter 8416, A(1)
ODir = FSO.BuildPath(A(0), "\")
If A.Named.Exists("min") Then M = 2 Else M = 0
Rgx.Pattern = "((.+)[ \-—_])(\d+)\w?\.[A-z]{3,4}$"
List = FSO.GetSpecialFolder(2) & "\" & FSO.GetTempName

For Each F In Items
  Name = FSO.GetFileName(F.Path) : BN = FSO.GetBaseName(Name)
  If Rgx.Test(Name) Then
    Set Name = Rgx.Execute(Name)(0) : BN0 = Name.Submatches(0)
    If Not Dic.Exists(BN0) Then
      Dic.Add BN0, Empty : Item = " -l """ & List
      Itms.Filter 8416, Replace(A(1), "*", BN0 & "*")
      If Itms.Count > 1 Then
        For Each i in Itms
          Sort.Add CLng(Rgx.Execute(i.Path)(0).Submatches(2)), i.Path
        Next : Coll.Addrange Sort.Values
        BN = Name.Submatches(1) : S = ""
        Itms.Filter 8416, Replace(A(1), "*", BN)
        If Itms.Count = 1 Then i = Itms.Item(0).Path : KitAdd i : S = i & vbCrLf
        FSO.CreateTextFile(List, 1).Write S & Join(Coll.ToArray, vbCrLf)
        Sort.Clear : Coll.Clear
      Else Item = " """ & F.Path End If
      Call Run
    End If
  Else KitAdd F.Path
  End if
Next

For Each F in Kit.Keys
  Item = " """ & F : Call Run
Next

If Len(BN) Then
  If FSO.FileExists(List) Then FSO.DeleteFile List, 1
  WSS.Popup "     Выполнено!",        1.5, Title, 4160
Else
  WSS.Popup "Нет подходящих файлов!", 1.5, Title, 4144
End If
For Each i in Array(Name, Itms, Items, Coll, Sort, Dic, Kit, FSO, Rgx, WSS)
  Set i = Nothing
Next

Sub Run
  WSS.Run """" & NConv & """ -quiet -multi -o """ & ODir & BN &_
  ".pdf" & """ -out pdf -q 100 -c " & A(2) & Item & """", M, 1
End Sub

Sub KitAdd(FPath)
  If Kit.Exists(FPath) Then Kit.Remove(FPath) Else Kit.Add FPath, Empty
End Sub

Добавлено: Пн янв 21, 2019 01:23
kabatov
Спасибо большое. Скрипт работает, но pdf генерируются, по всей видимости, с ошибкой в каких-то параметрах, т. к. файлы получаются значительно меньшего веса, чем те, что сделаны Nconvert с помощью упомянутой выше кнопки или непосредственно из XnView. Важно, чтобы соблюдались следующие два параметра: качество 100 (-q 100) и тип сжатия LZW (-c 3).

Добавлено спустя 1 час 10 минут:

Тестирую работу скрипта. Обнаружилось следующее:
1. Опытным путем выяснил, что при записи pdf почти наверняка применяется компрессия JPEG (совпадают размеры выходных pdf, написанных скриптом и XnView, при соответствующих настройках).
2. Если в параметрах задано:

Код: Выделить всё

"%T" *.jpg;*jpeg;*.png;*.tiff
файлы с расширением tif скрипт ожидаемо пропускает.
Если и в параметрах указано *.tiff, и расширения файлов tiff, появляется сообщение об ошибке:
Сценарий: C\totalcmd\Utils\TC_pdf.vbs
Строка: 29
Символ: 3
Ошибка: Недопустимый вызов или аргумент процедуры
Код: 800A0005
Источник: Ошибка выполнения Microsoft VBScrit
Та же ошибка с параметром *.tif для файлов с расширением tif.

Добавлено: Пн янв 21, 2019 05:55
Flasher
kabatov писал(а):т. к. файлы получаются значительно меньшего веса
Для jpg/jpeg делал -с 5, для остальных — -с 3.
kabatov писал(а):файлы с расширением tif скрипт ожидаемо пропускает.
Логично. И?
kabatov писал(а):появляется сообщение об ошибке:
Значит, вы где-то промахнулись с исходными данными:
kabatov писал(а):имеют следующий вид: scan_001-01.tif, scan_001-02.tif <...> scan_001-55.tif, image_001.tif, image_002.tif <...> image_850.tif, pic 00.tif, pic 01.tif <...> pic 99.tif

Добавлено: Пн янв 21, 2019 11:24
kabatov
Правильно я понимаю, что, если строку

Код: Выделить всё

If InStr("|jpg|jpeg|", "|" & LCase(Ext) & "|") Then C = 5 Else C = 3
заменить на во всех случаях будет применяться компрессия LZW?

Добавлено: Пн янв 21, 2019 11:32
Flasher
Лучше иначе сделать. Я исправлю на LZW, если так надо. Сперва с ошибкой разберёмся.

Добавлено: Пн янв 21, 2019 11:59
kabatov
Спасибо еще раз.
Вот еще два наблюдения:
1. Файлы с именами вида img___1.jpg, img___2.jpg скрипт обрабатывает, а вот на img___1_.jpg, img___2_.jpg выдает упомянутую ошибку.
2. Если встречаются файлы в разных форматах, но идентичные по части имени, например:
image_1.jpg
image_2.jpg
image_3.tif
image_4.tif
image_5.tif

или

image_1.tif
image_2.tif
image_3.tif
image_4.jpg
image_5.jpg
то скрипт делает pdf только из первой группы. То есть в первом примере получится двухстраничный pdf из jpeg, во втором — трехстраничный из tiff. Обычно работать приходится с файлами одного формата, но, если встретится такая, как в приведенных примерах, смесь, необходимо иметь на выходе пятистраничный pdf.

Добавлено спустя 7 минут:

Еще два маленьких вопроса.
1. Можно ли как-то дать понять пользователю, что скрипт в процессе работы? Просто часто обрабатывать приходится тысячи файлов, это занимает много времени, и хотелось бы понимать, что происходит в данный момент.
2. Как при необходимости остановить работу скрипта?

Добавлено: Пн янв 21, 2019 12:01
Flasher
1. Ну, как я и сказал, вы промахнулись (не дополнили) с условием. Считаете, что такое именование окончаний №_.<ext> целесообразно?
Я ещё не видел, чтобы в конце базовых имён после счётчика ставился знак подчёркивания.
К тому же в исходных примерах указано наличии передних нулей в счётчике, что упрощало ситуацию. Теперь же вы приводите пример с их отсутствием. Нужно такие моменты заранее уточнять.
2. Да, я это специально сделал, чтобы разделять группы расширений. По-вашему, image_1.jpg и image_1.tif должны оказаться в одной группе? На мой взгляд, это неестественно.
kabatov писал(а):Еще два маленьких вопроса.
1. Ну, в конце же есть уведомление об окончании. Его разве недостаточно?
2. Завершить процессы wscript.exe и nconvert.exe:

Код: Выделить всё

TOTALCMD#BAR#DATA
*%WINDIR%\SYSTEM32\taskkill.exe /f /im wscript.exe /im nconvert.exe

gpedit.dll,1
Завершить WScript и NConvert

1

Добавлено: Пн янв 21, 2019 13:10
kabatov
1. По этому пункту согласен. Такое именование надо рассматривать как ошибку, поэтому правильно было бы приводить перед конвертацией имеющиеся исходные файлы в порядок. Собственно, так и делаю. В общем, буду следить за этим. Этот вопрос, будем считать, разрешен.

2. Несколько форматов в исходных файлах могут встретиться, если последние делались с одного оригинала, но в разное время разными людьми. Здесь принципиально ориентироваться на имена.

3. Уведомления достаточно, чтобы понять, что работа завершена. Просто речь о том, что, когда в течение 20—30 минут компьютер занят конвертацией, имеет смысл как-то дать знать об этом оператору. Разумеется, можно пользоваться скриптом и без этого. Это совершенно не принципиальное пожелание, не более.

Добавлено: Пн янв 21, 2019 13:27
Flasher
1. А по поводу цифр в счётчике?
2. Хотелось бы на поставленный вопрос ответ получить. Два файла с одним номером и разным расширением.
Возьмём даже 4: image_001.jpg, image_002.jpg, image_001.tif, image_002.tif. Что на выходе?
3. Отрисовка прогресса — это лишний забор времени. Могу добавить ключ на свёрнутую консоль, если устроит.

Добавлено: Пн янв 21, 2019 15:46
kabatov
1. Счетчика в именах может и не быть. В общем, пока вижу, что все работает, как надо, за исключением подобных случаев:

а) иногда встречаются буквенные обозначения: image_004.jpg, image_005a.jpg, image_005b.jpg (на выходе надо бы получить image.pdf),
б) scan.tif, scan-01.tif, scan-02.tif (на выходе — scan.pdf),
в) одиночные файлы с уникальными именами должны конвертироваться в одностраничный pdf с сохранением полного имени:
<...>
image-01.tif
image-02.tif
pic-01.tif
scan-01.tif
scan-02.tif
<...>
Результат: <...> image.pdf, pic-01.pdf, scan.pdf <...>

2. Случаев полного совпадения имен с разным расширением (image_001.jpg, image_001.tif) вообще-то быть не должно. Но, если такое встретилось, хорошо бы выдавать предупреждение либо игнорировать jpeg.

3. Не принципиально. Можно оставить, как есть.

Добавлено: Пн янв 21, 2019 17:33
Flasher
1. Начинается... Вообще я писал о нулях в счётчике. Прочтите внимательно.
a,в) Это ладно.
б) А если scan.tif, scan-01.tif, scan-001.tif scan_01.tif, scan 01.tif?

2. Это лишний гемор. Задаются любые расширения, а не только эти два. Т. е. фактически придётся каждое имя сопоставлять с потенциальным множеством файлов с заданными расширениями. Это и на скорости выполнения отразится.

3. Так нужна минимизация или нет? Это самое простое из обсуждаемого.

Добавлено: Пн янв 21, 2019 21:56
kabatov
1.
А если scan.tif, scan-01.tif, scan-001.tif scan_01.tif, scan 01.tif?
Вряд ли я смогу сформулировать задачу исчерпывающе точно, так, чтобы формулировка эта включила бы все множество возможных вариантов. Для этого нужен опыт и мозги программиста. Вполне вероятно, что Вы снова поймаете меня на неточности, смоделировав новую ситуацию. Сейчас Ваш скрип делает то, что и было мне нужно, за что я Вам очень благодарен. Все файлы я готовлю в нужном формате, с правильными характеристиками и именами. Поэтому давайте исходить из того, что имена файлов заканчиваются счетчиком, в котором может быть любое количество цифр, и первая из которых не обязательно ноль, а слева от счетчика — один из трех символов: пробел, дефис или знак подчеркивания.
2.
а) При разбиении списка файлов на группы скрипт не должен принимать во внимание расширение: image-01.jpg, image-02.jpeg, image-03.tif — одна группа, три страницы файла image.pdf
б) Файлы разных форматов, но с идентичными именами отправляются в один pdf: scan_55.jpg, scan_55.tif, scan_56.tif, scan_57.tif — четыре страницы файла scan.pdf
3. Минимизация нужна.

Спасибо.

Добавлено: Вт янв 22, 2019 15:09
kabatov
Уважаемый Flasher!
Первый раз использовал Ваш скрипт в работе. Искренняя Вам благодарность от коллег по сектору репрографии Российской национальной библиотеки. Некоторые считают Вашу услугу достойной памятника. С сегодняшнего дня в деле пополнения электронной библиотеки РНБ есть часть Вашего труда.

Добавлено: Вт янв 22, 2019 19:49
Flasher
kabatov
Ах, вон откуда ноги растут. Почётно. :) Привет коллегам. Скрипт обновил. Проверяйте.
P.S.: Я памятник себе воздвиг нерукотворный ... Изображение ( Изображение )