A tool for preparing PDF files for publishing on the Internet.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

808 lines
24 KiB

#!/usr/bin/env tclsh
package require Tk
package require Img
package require fileutil
package require platform
#----------------------------------------------------------------------
#
# Copyright (C) 2019, Maxim Lihachev, <envrm@yandex.ru>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#----------------------------------------------------------------------
#
# REQUIREMENTS:
# ghostscript
# imagemagick
# cwebp
# exiftool
# resmushit-cli
#
# Сочетания клавиш
# Функции для работы с файлами в формате PDF
namespace eval pdf {
# Список меток, доступных в метаданных PDF
set all_tags {author title creator keywords producer subject}
# Массив для хранения меток
array set tags {}
array set tag_labels {
Author {Автор(ы)}
Title {Заголовок}
Creator {Создатель}
Keywords {Ключевые слова}
Producer {Продюсер}
Subject {Тема}
}
# Значение метки по умолчанию
set default_tag "Гностическая библиотека gnosis.study"
# Текущая дата
set lastmod [clock format [clock seconds] -format %Y-%m-%d]
# Просмотрщик файлов в формате PDF
if {[string match "macosx*" [::platform::generic]]} {
set viewer open
} else {
set viewer xdg-open
}
# Markdown-шаблон
set template {
---
layout: book
lastmod: $::pdf::lastmod
title: $::pdf::title
authors: $::pdf::authors
pathway: Библиотека
category: Исследования
pages:
year:
language: RUS
filesize: $::pdf::tags(File Size)
pdf: $::pdf::file
tags:
-
---
}
# Открытие файла в просмотрщике документов в формате PDF
# По умолчанию используется zathura
proc view_file {} {
exec nohup $::pdf::viewer $::pdf::file >/dev/null 2>/dev/null &
}
# Загрузка markdown из файла или создание текста на основе шаблона
proc get_markdown {filename template} {
set markdown_file "${::pdf::file_rootname}.md"
if {[file exists $markdown_file]} {
set md [::fileutil::cat $markdown_file]
} else {
set md [make_markdown $markdown_file $template]
}
return $md
}
# Заполнение markdown-шаблона
proc make_markdown {filename template} {
return [join [lrange [lmap line [split [subst $template] "\n"] {
regsub -all {\s{2,}} $line { } line
string trim $line
}] 1 end] "\n"]
}
# Инициализация пустых переменных с информацией о PDF-файле
proc erase_info {} {
catch {
unset ::pdf::file
unset ::pdf::file_path
unset ::pdf::file_basename
unset ::pdf::file_rootname
unset ::pdf::title
unset ::pdf::authors
unset ::pdf::canonical_authors
array unset ::pdf::tags
foreach tag $::pdf::all_tags {
set ::pdf::tags([string totitle "${tag}"]) {}
}
}
}
# Получение информации о PDF-файле
proc load {filename} {
set file_basename [file rootname [file tail $filename]]
::pdf::erase_info
# Попытка получения автора/авторов и названия по шаблону
#
# Фамилия_1 И.О., Фамилия_2 И.О. - Название работы.pdf
#
if {![regexp {(.*) - (.*)} $file_basename {} authors title]} {
set authors {}
set title $file_basename
}
set ::pdf::file [file tail $filename]
set ::pdf::file_path [file dirname $filename]
set ::pdf::file_basename $file_basename
set ::pdf::file_rootname [file rootname $filename]
set ::pdf::title $title
set ::pdf::authors [authors_list $authors]
set ::pdf::canonical_authors [canonical_authors $authors]
read_tags
}
# Чтение метаданных PDF-файла
proc read_tags {} {
foreach line [split [exec exiftool -all -t "$::pdf::file"] "\n"] {
set data [split $line "\t"]
set k [lindex $data 0]
set v [lindex $data 1]
set ::pdf::tags($k) $v
}
}
# Сохранение метаданных в файл
# и запись markdown-описания на диск
proc write_metadata {} {
write_tags
write_markdown
}
# Запись метаданных
proc write_tags {} {
exec exiftool -Author=$::pdf::tags(Author)\
-Title=$::pdf::tags(Title)\
-Creator=$::pdf::tags(Creator)\
-Keywords=$::pdf::tags(Keywords)\
-Producer=$::pdf::tags(Producer)\
-Subject=$::pdf::tags(Subject)\
"[file join $::pdf::file_path $::pdf::file]"
}
# Сохранение markdown-файла
proc write_markdown {} {
set file_output "[file join $::pdf::file_path $::pdf::file_basename].md"
::fileutil::writeFile $file_output [$::gui::txt get 1.0 end]
}
# Перечисление авторов в виде markdown-списка:
#
# Author 1, Author 2 - Title.pdf
#
# =>
#
# - Author 1
# - Author 2
proc authors_list {authors} {
return [join [
lmap a [split $authors ","] {
format "\n- %s" [string trim $a]
}
] {}]
}
# Список авторов в формате
#
# И.О. Фамилия_1, И.О. Фамилия_2
#
proc canonical_authors {authors} {
return [join [
lmap a [split $authors ","] {
lreverse [split [string trim $a]]
}
] ", "]
}
}
# Функции для работы с изображениями
namespace eval cover {
# Размер текущего изображения
set size {}
# Размеры изображения титульного листа
set sizes {x600 x700 x800}
# Размер миниатюры изображения титульного листа
set th_size x250
# Изменение размера
proc resize {} {
set new_size [.img.resize.sizes get]
exec convert -resize $new_size "${::pdf::file_rootname}.jpg" "${::pdf::file_rootname}.jpg"
set ::cover::size [get_size]
}
# Получение размера изображения в формате WxH
proc get_size {} {
return [exec magick identify -format {%[fx:w]x%[fx:h]} "${::pdf::file_rootname}.jpg"]
}
# Создание миниатюры
proc make_thumbnail {} {
exec convert -resize $::cover::th_size "${::pdf::file_rootname}.jpg" "${::pdf::file_rootname}.th.jpg"
}
# Извлечение первой страницы файла
proc extract {} {
exec convert "${::pdf::file}\[0\]" "${::pdf::file_rootname}.jpg"
}
# Создание webp-вариантов изображений
proc make_webp {} {
exec cwebp "${::pdf::file_rootname}.jpg" -o "${::pdf::file_rootname}.webp" >&-
exec cwebp "${::pdf::file_rootname}.th.jpg" -o "${::pdf::file_rootname}.th.webp" >&-
}
# Оптимизация изображений
proc optimize {} {
exec resmushit-cli --preserve-filename "${::pdf::file_rootname}.jpg"
}
# TODO: Обрезка изображения
proc crop {} {
return "convert -resize size image image"
}
}
##########################
# GUI
##########################
# Графический интерфейс пользователя
namespace eval gui {
namespace path {::tcl::mathop ::tcl::mathfunc}
# Название приложения
set main_title "Press'n'Post"
#
# Использованы значки: https://dribbble.com/shots/2888226-1800-Free-Minimal-Icon-Pack-20x20
#
namespace eval icon {
set view [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAPxJREFUOBHdVEsO
AUEQbSKuYOEQNlZWVk7ABSQu4BKcw91Y2FhZSAT16Nd506qnRaxU0lOf95kagxBCuH951qZz41vDm7kt
PUcaepg3Ix/5ameek0jI56WefOaLEWdKJqCztpp8zWcTTCgiwL6Wyc/zyYSjjl0AIFB/EuR73OOvDUPP
u01lVnqS5+bdivhPYX39B3vGrZ1+fFbF9jbbFDDluX8OMEUokTVMEezznIChkVaRiE0RJKNexN7DgKdQ
0dim6Esi5WqdzPR7iM9oEJFdYtQLGDeCd0LGZt5LgWBqhxz0qku1blj6BVCMjMi3f9PR/UVvXokht22f
VA+yBpIvVGF67wAAAABJRU5ErkJggg==
}]
set save [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAYAAABiS3YzAAAAAXNSR0IArs4c6QAAAMhJREFUOBFjYMAO
nIDCX4D4P5kYw1QfoMh3Mg2DOQLF0DAg7xfUwOVAmhlFFpUDshxkyBmosCSU/58JKgCiEoB4GRCzAjEI
LAXio0AM0hgIxEQDmKE5QB3zgBjksr9E68ajsBwoBwuLBiAb5B0QH+Q9fAAkj9X7IE0wA0GGgwDMUJg4
IRqkHgSwhukiiBzlJAsWI0ywiJEkBIsokjQRUjy0DSU39uGhQnfv+wKtZsSDQfJYAd1ditUVxAjSxKWg
MAPlbaoCmrgUAAgMVcgk92TZAAAAAElFTkSuQmCC
}]
set add [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAYAAABiS3YzAAAAAXNSR0IArs4c6QAAAHFJREFUOBFjYGBg
+E8ApwHlkQGIj1cPE7JqarJhtkpSaChIP9gsmrh0+BnaAw0vEE0Q0MT7LEBrGQlaTZyC58SYtRmoCJbc
sNEgeayAJt7HahMWQZIjCua1wZ2jQD4dGi6lSezTxFDkMIWFLTo9OEp+APPUO5cVXVflAAAAAElFTkSuQmCC
}]
set extract [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAATpJREFUOBGtlLtK
A2EQhTcSsVJUvJFSC/HyOtZWVj6BT6SNlZ19tAgJ4h2tRax8A/U7ycw46LqG7A6cnfPP5fzzM7Ctoig+
QWM21ZiSCbWTYCvxSejwpY1PWEdwm2cclD1Fo06ymGPr2zDR0AlSdltFbJ3cXsqHTpCUHIfmhao+dIKM
o2I1a/g7sJ96QidISlbRVZIPQH0f4BDIQifIKF4s4c/Bjp2zW+FwD9Tzbl78KPFvZYK6/daSb/gt4LYM
8VwPvghOgA/kPgIdko9WoKeo4BVsAk19AxTrgwUg02JOgYvJx+HZ+BN+F1za+QXvYldwTZZtmsMZcNEg
Cmhz2qBsDlwAL7yGa9IymyHodUEGBH82zBLrAl2khVTZL8H5P6ol+p+YWoeC+mWJyBr5fWXBkWzNb53f
V+nVX3BUZefmrTd2AAAAAElFTkSuQmCC
}]
set thumbnail [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAATFJREFUOBGtlLFK
Q1EQRI0o2FhY2NvYCvkBP8E6tensA3apxMJS8gNWItjnQwI2lv6BXZAkMy/3LOuNwoW8C5Pd2Zmd3Bd4
GRzsnvXuqH1y2G5tcw7abE2u7sl6v+GRvnuv30z7v56y9xv6x/ENW245lO9a4Eb1XvBoZP7v3EvA91JM
cHaCRyPlCrWqn+L4VupPEseKHsaRlB/hAUeq7+pZ+ChzOLbgNA6jr0PPpD0Kz8KF4IN3yxJHcH0TCM6h
p2ylyh6j4DTTooxVmTn0UvgS7oR88DALHg2K6kRg/p36HIrOWvBoUEp9SkF4XAllxlrwaFBUj4W5YG0p
3AqLwj1zaL0XPBqZfPw6vgqeO+xG8DkXcmi915n8UQuzMsthmP8KReuq30sH7nN4t7uM3v9tNgRFf0H5
p3RQAAAAAElFTkSuQmCC
}]
set resize [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAF9JREFUOBFjZCAM
/hNWwsDARIwiYtQwAhURZSNQHUgtNgDWTzUXsSBZgddGJHVYmVRz0eAzCDmMsPodSRBv7FLNa8S4iFBs
guWp5qJRg5ASAQ4mcqzhTSc49MOFqRbYAFdBByiUR9g+AAAAAElFTkSuQmCC
}]
set optimize [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAHVJREFUOBHtk1EK
wCAMQ1W8/5U3FQqNRqlYYR/zz5itL0FjGNczSnYl2a02Z17Y4uKMHbVk7oSV4qiz8r0kuUOoO4RJajLr
q2o0mXuH/w9n/dv173eo72Gfi96z3lT24HOPzAjlxRAYkIQM/FcJZSJg7G7cCV+b8wotFKfp7AAAAABJRU5ErkJggg==
}]
set content [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAYAAABiS3YzAAAAAXNSR0IArs4c6QAAAG9JREFUOBFjYGBg
YAHiWiC+D8RvgXg5EEsAMUWgCaj7Pxo+DeQzU2LqE6iB6UBaA4i/QPmGQBoEZgIxuqV4+UxgbTQgqO59
ULgdBuI/QKwExP+AeCMQxwPxZyAeXGA0SVE/PkaT1GgpReVUNTSSFAA/Q2sLKirhAgAAAABJRU5ErkJggg==
}]
set webp [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAJlJREFUOBFjYMAE
e4BC/8nAz4F6gjGNY2BYS4ZhMAc8xWYgOWISMEcwkqMbhx6QKxmYgNgTiB8DMczZxNIgPSC9GIAcw2CW
gvTCAEwM7jJQOBAL4GGGpAFsICgMQQwQIDU80fWB+aAwpCoYNZDy4KR6GLJQ7ia4CZ9BLGqmQyd0A+FW
kchAyRBUD0OQgU9IdBGycqx6qVp8AQC1Cl8RUqquKQAAAABJRU5ErkJggg==
}]
}
# Окно с информацией о программе
proc about_window {} {
tk_messageBox -type ok -icon info \
-message "$::gui::main_title" \
-detail [join { \
"Данная программа предназначена для подготовки PDF-файлов к публикации в гностической библиотеке gnosis.study."
"Copyright (c) 2019 Maxim Likhachev (envrm)."
"Лицензия: GPLv3."
} "\n\n"]
}
# Функции для работы с текстом
namespace eval text {
# Подсветка синтаксиса YAML
proc hl_yaml {widget} {
set offset 1.0
while 1 {
set field_position [split [$widget search ":" $offset end] "."]
if {[llength $field_position] == 0} {
break
}
set row [lindex $field_position 0]
set col [lindex $field_position 1]
set offset "$row.[expr {$col + 1}]"
$widget tag add field $row.0 $offset
}
}
# Создание текстового поля
proc create {parent} {
set t [text $parent.t -setgrid true -width 50 -height 20 -wrap none -font {Sans 16}]
$t tag configure field -foreground "blue"
$t tag configure regular
::gui::text::load $t [::pdf::get_markdown $::pdf::file $::pdf::template]
hl_yaml $t
return $t
}
# Загрузка текста в виджет
proc load {txt content} {
$txt delete 1.0 end
$txt insert 1.0 $content
$txt tag add regular 1.0 end
hl_yaml $txt
}
}
# Функции
namespace eval image {
# Масштабирование изображения под размер окна (tcl-версия)
proc img_scale_x_tcl {im mx_ht} {
set h [image height $im]
set w [image width $im]
set ::cover::size "${w}x${h}"
puts "IMG: ${h}x${w}"
puts "IMG: ${mx_ht}x___"
set mx_wt [expr {$mx_ht * $w / $h}]
if {$mx_wt >= $w} {
set mode -subsample
set xfactor [expr {$mx_ht / $h}]
set yfactor [expr {$mx_wt / $w}]
} else {
set mode -subsample
set xfactor [expr {$h / $mx_ht}]
set yfactor [expr {$w / $mx_wt}]
}
puts "IMG: ${mx_ht}x${mx_wt}"
puts "IMG: ${xfactor}x${yfactor}"
set new [image create photo -width $mx_wt -height $mx_ht]
$new copy $im -shrink -subsample $xfactor $yfactor
image delete $im
return $new
}
# Масштабирование изображения под размер окна (imagemagick-версия)
proc img_scale_x {im mx_ht} {
set h [image height $im]
set w [image width $im]
set ::cover::size "${h}x${w}"
exec convert -resize x${mx_ht} "${::pdf::file_rootname}.jpg" "/tmp/${::pdf::file_basename}.publish_tmp.jpg"
set new [image create photo -file "/tmp/${::pdf::file_basename}.publish_tmp.jpg"]
image delete $im
return $new
}
# Отображение изображения
proc load {w} {
set cover_file "${::pdf::file_rootname}.jpg"
if {[file exists $cover_file]} {
set cover [img_scale_x [image create photo image_file -file "${::pdf::file_rootname}.jpg"] 600]
} else {
set cover [image create photo -height 400 -width 400]
}
$w configure -image $cover
}
}
# Главное меню
#
# | Файл | Правка | Изображение | Помощь |
#
namespace eval menu {
proc make {} {
menu .menu
. configure -menu .menu
::gui::menu::item .menu.file "Файл" {
"Открыть новый файл..." ::main::open_file
"Просмотр текущего файла" ::pdf::view_file
"Выйти из программы" ::main::quit
}
::gui::menu::item .menu.edit "Правка" {
"Сгенерировать markdown" {
::gui::text::load $::gui::txt [::pdf::make_markdown $::pdf::file $::pdf::template]
}
"Сохранить метаданные" ::pdf::write_metadata
}
::gui::menu::item .menu.image "Изображение" {
"Извлечь обложку" {
::cover::extract
::gui::image::load $::gui::cover_image
}
"Изменить размер" ::cover::resize
"Оптимизировать" ::cover::optimize
"Создать миниатюру" ::cover::make_thumbnail
"Создать webp" ::cover::make_webp
}
::gui::menu::item .menu.about "Помощь" {
"О программе" ::gui::about_window
}
}
# Создание выпадающего меню
proc item {path label commands} {
set parent ".[lrange [split $path .] 1 end-1]"
menu $path -tearoff 0
$parent add cascade -menu $path -label $label -underline 0
foreach {submenu command} $commands {
$path add command -label $submenu -command $command
}
}
}
# Установка заголовка главного окна
proc set_title {title} {
wm title . $title
}
# Кнопка с изображением
proc image_button {path label command {width 15}} {
set id [lindex [split $path .] end]
ttk::button $path -text $label \
-image [ set ::gui::icon::$id] -compound left \
-width $width -style My.TButton \
-command $command
}
# ----------------------------------------------------------------------
#
# GUI
#
# +--------------------------------------------------------+
# | .menu [x]|
# | +------------------------+ +------------------------+ |
# | | .img | | .metadata | |
# | | +--------------------+ | | +--------------------+ | |
# | | | .img.cover | | | | .metadata.txt | | |
# | | | | | | | | | |
# | | | | | | | | | |
# | | | | | | | | | |
# | | | | | | | | | |
# | | | | | | | | | |
# | | | | | | +--------------------+ | |
# | | | | | | +--------------------+ | |
# | | | | | | | .metadata.tags | | |
# | | +--------------------+ | | | [...] | | |
# | | +--------------------+ | | | [...] | | |
# | | | .img.resize [...] | | | | [...] | | |
# | | +--------------------+ | | +--------------------+ | |
# | | +--------------------+ | | +--------------------+ | |
# | | | .img.buttons [...] | | | | .metadata.buttons | | |
# | | +--------------------+ | | +--------------------+ | |
# | +------------------------+ +------------------------+ |
# +--------------------------------------------------------+
#
proc show {} {
if {![winfo exists .img]} {
::gui::make
} else {
::gui::load_data
}
set_title "$::gui::main_title - $::pdf::file"
}
proc make {} {
# [IMAGE] ------------------------------------------------------
frame .img
set ::gui::cover_image [label .img.cover -relief groove]
::gui::image::load $::gui::cover_image
frame .img.resize
label .img.resize.size_label -text "Размер изображения:"
entry .img.resize.size_entry -textvariable ::cover::size -width 10
label .img.resize.resize_label -text "Новый размер:"
ttk::combobox .img.resize.sizes -values $::cover::sizes -width 10
.img.resize.sizes set [lindex $::cover::sizes 0]
grid .img.resize.size_label -row 0 -column 0 -sticky w
grid .img.resize.size_entry -row 0 -column 1 -sticky w
grid .img.resize.resize_label -row 0 -column 2 -sticky w
grid .img.resize.sizes -row 0 -column 3 -sticky w
frame .img.buttons
image_button .img.buttons.view "Посмотреть файл" ::pdf::view_file
image_button .img.buttons.extract "Извлечь обложку" {
::cover::extract
::gui::image::load $::gui::cover_image
}
image_button .img.buttons.resize "Изменить размер" ::cover::resize
image_button .img.buttons.optimize "Оптимизировать" ::cover::optimize
image_button .img.buttons.thumbnail "Создать миниатюру" ::cover::make_thumbnail
image_button .img.buttons.webp "Создать webp" ::cover::make_webp
grid .img.buttons.view -row 0 -column 0 -padx 1 -pady 1 -sticky w
grid .img.buttons.extract -row 0 -column 1 -padx 1 -pady 1 -sticky w
grid .img.buttons.resize -row 0 -column 2 -padx 1 -pady 1 -sticky w
grid .img.buttons.optimize -row 1 -column 0 -padx 1 -pady 1 -sticky w
grid .img.buttons.thumbnail -row 1 -column 1 -padx 1 -pady 1 -sticky w
grid .img.buttons.webp -row 1 -column 2 -padx 1 -pady 1 -sticky w
pack .img.cover -fill both -expand true
pack .img.resize -pady 5
pack .img.buttons
# [METADATA] ---------------------------------------------------
set metadata [frame .metadata]
frame .metadata.tags
set ::gui::txt [::gui::text::create $metadata]
set row 0
foreach tag $::pdf::all_tags {
set tag_name [string totitle "${tag}"]
label .metadata.tags.${tag}_label -text "$::pdf::tag_labels(${tag_name}):"
entry .metadata.tags.${tag}_entry -width 40 -textvariable "::pdf::tags($tag_name)"
button .metadata.tags.${tag}_button -image $::gui::icon::add -command "::gui::fill_tag $tag_name"
grid .metadata.tags.${tag}_label -row $row -column 0 -sticky w
grid .metadata.tags.${tag}_entry -row $row -column 1 -sticky w
grid .metadata.tags.${tag}_button -row $row -column 2 -sticky w -ipadx 1 -ipady 1
incr row
}
frame .metadata.buttons
image_button .metadata.buttons.save "Сохранить метаданные" ::pdf::write_metadata 20
image_button .metadata.buttons.content "Сгенерировать markdown" {
::gui::text::load $::gui::txt [::pdf::make_markdown $::pdf::file $::pdf::template]
} 20
grid .metadata.buttons.content -row 0 -column 1 -padx 1 -pady 1 -sticky ns
grid .metadata.buttons.save -row 0 -column 2 -padx 1 -pady 1 -sticky ns
pack $::gui::txt -fill both -expand true
pack .metadata.tags -fill x -anchor w
pack .metadata.buttons -pady 5 -anchor w
# [TOP LEVEL] --------------------------------------------------
ttk::style theme use clam
ttk::style configure My.TButton -anchor w
set_title $::gui::main_title
menu::make
bind_keys
pack .img -fill both -expand true -padx 10 -pady 10 -side left
pack .metadata -fill both -expand true -padx 10 -pady 10 -side right
focus $::gui::txt
}
proc load_data {} {
::gui::image::load $::gui::cover_image
::gui::text::load $::gui::txt [::pdf::get_markdown $::pdf::file $::pdf::template]
update
}
# Заполнение метаданных вариантами по умолчанию
proc fill_tag {tag} {
set ::pdf::tags($tag) [switch $tag {
"Author" {
set ::pdf::canonical_authors
}
"Title" {
set ::pdf::title
}
default {
set ::pdf::default_tag
}
}]
}
proc bind_keys {} {
# Подсветка YAML при изменении текста
bind $::gui::txt <Any-KeyRelease> "::gui::text::hl_yaml $::gui::txt"
bind . <KeyPress-Control_L><KeyPress-o> ::main::open_file
bind . <KeyPress-Control_R><KeyPress-o> ::main::open_file
bind . <KeyPress-Control_L><KeyPress-y> ::pdf::view_file
bind . <KeyPress-Control_R><KeyPress-y> ::pdf::view_file
bind . <KeyPress-Control_L><KeyPress-q> ::main::quit
bind . <KeyPress-Control_R><KeyPress-q> ::main::quit
wm protocol . WM_DELETE_WINDOW ::main::quit
}
}
# ------------------------------------------------------------------------------
# Общие процедуры
namespace eval main {
# Удаление временных данных
proc remove_tmp_data {} {
foreach tmp_file [glob "/tmp/*.publish_tmp.jpg"] {
file delete $tmp_file
}
}
# Выход из программы
proc quit {} {
set answer [tk_messageBox -message "Выйти из программы?" \
-icon question -type yesno]
if {$answer == yes} {
remove_tmp_data
exit 0
}
}
# Завершение работы с сообщением об ошибке
proc die {msg {ui cli} {exit_code 1}} {
switch $ui {
cli {
puts stderr $msg
}
gui {
tk_messageBox -type ok -icon error -message $msg
}
}
exit $exit_code
}
# Загрузка файла и запуск GUI
proc open_file {{filename ""}} {
if {$filename ne ""} {
set pdf_file $filename
} else {
# set pdf_file [::tk::dialog::file:: open -filetypes {{{PDF Files} {.pdf}}} ]
set pdf_file [tk_getOpenFile -parent . -filetypes {{PDF .pdf}}]
}
if {$pdf_file eq ""} {
::main::die "ERROR: Не указан файл."
}
if {![file exists $pdf_file]} {
::main::die "ERROR: Файл $filename не существует."
}
pdf::load $pdf_file
puts [string repeat "=" 80]
puts "#### FILE: $pdf_file"
puts [string repeat "=" 80]
parray ::pdf::tags
puts [string repeat "-" 80]
gui::show
}
}
# ------------------------------------------------------------------------------
# Запуск приложения
# ------------------------------------------------------------------------------
update
main::open_file [lindex $argv 0]