Compare commits
6 Commits
32e9acfa77
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
f4b08fb951
|
|||
|
e8752e53ac
|
|||
|
147b880524
|
|||
|
a02966d1d1
|
|||
|
ba5fdf20df
|
|||
|
a8dc72349b
|
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 - 2024, Přemysl Eric Janouch <p@janouch.name>
|
||||
Copyright (c) 2017 - 2025, Přemysl Eric Janouch <p@janouch.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
30
README.adoc
30
README.adoc
@@ -33,15 +33,39 @@ Runtime dependencies: libcrypto (OpenSSL 1.1 API)
|
||||
$ cd builddir
|
||||
$ ninja
|
||||
|
||||
Go
|
||||
~~
|
||||
In addition to the C++ version, also included is a native Go port,
|
||||
which has enhanced PDF 1.5 support:
|
||||
|
||||
$ go get janouch.name/pdf-simple-sign/cmd/pdf-simple-sign
|
||||
----
|
||||
$ go install janouch.name/pdf-simple-sign/cmd/pdf-simple-sign@master
|
||||
----
|
||||
|
||||
And a crude external VFS for Midnight Commander, that may be used to extract
|
||||
and a crude external VFS for Midnight Commander, that may be used to extract
|
||||
all streams from a given PDF file:
|
||||
|
||||
$ go get janouch.name/pdf-simple-sign/cmd/extfs-pdf
|
||||
----
|
||||
$ GOBIN=$HOME/.local/share/mc/extfs.d \
|
||||
go install janouch.name/pdf-simple-sign/cmd/extfs-pdf@master
|
||||
----
|
||||
|
||||
To enable the VFS, edit your _~/.config/mc/mc.ext.ini_ to contain:
|
||||
|
||||
----
|
||||
[pdf]
|
||||
Type=^PDF
|
||||
Open=%cd %p/extfs-pdf://
|
||||
----
|
||||
|
||||
Lua PDF generator
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Build dependencies: Meson, a C++17 compiler, pkg-config +
|
||||
Runtime dependencies: C++ Lua >= 5.3 (custom Meson wrap fallback),
|
||||
cairo >= 1.15.4, pangocairo, libqrencode
|
||||
|
||||
This is a parasitic subproject located in the _lpg_ subdirectory.
|
||||
It will generate its own documentation.
|
||||
|
||||
Contributing and Support
|
||||
------------------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2021, Přemysl Eric Janouch <p@janouch.name>
|
||||
// Copyright (c) 2021 - 2024, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
@@ -46,6 +46,8 @@ func streamSuffix(o *pdf.Object) string {
|
||||
return "jp2"
|
||||
case "DCTDecode":
|
||||
return "jpg"
|
||||
case "FlateDecode":
|
||||
return "zz"
|
||||
default:
|
||||
return filter.String
|
||||
}
|
||||
|
||||
9
lpg/.clang-format
Normal file
9
lpg/.clang-format
Normal file
@@ -0,0 +1,9 @@
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: 80
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: ForContinuationAndIndentation
|
||||
SpaceAfterCStyleCast: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignOperands: DontAlign
|
||||
SpacesBeforeTrailingComments: 2
|
||||
1160
lpg/lpg.cpp
Normal file
1160
lpg/lpg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
240
lpg/lpg.lua
Normal file
240
lpg/lpg.lua
Normal file
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env lpg
|
||||
local project_url = "https://git.janouch.name/p/pdf-simple-sign"
|
||||
|
||||
function h1 (title)
|
||||
return lpg.VBox {fontsize=18., fontweight=600,
|
||||
title, lpg.HLine {2}, lpg.Filler {-1, 6}}
|
||||
end
|
||||
function h2 (title)
|
||||
return lpg.VBox {fontsize=16., fontweight=600,
|
||||
lpg.Filler {-1, 8}, title, lpg.HLine {1}, lpg.Filler {-1, 6}}
|
||||
end
|
||||
function h3 (title)
|
||||
return lpg.VBox {fontsize=14., fontweight=600,
|
||||
lpg.Filler {-1, 8}, title, lpg.HLine {.25}, lpg.Filler {-1, 6}}
|
||||
end
|
||||
function p (...)
|
||||
return lpg.VBox {..., lpg.Filler {-1, 6}}
|
||||
end
|
||||
function code (...)
|
||||
return lpg.VBox {
|
||||
lpg.Filler {-1, 4},
|
||||
lpg.HBox {
|
||||
lpg.Filler {12},
|
||||
lpg.VBox {"<tt>" .. table.concat {...} .. "</tt>"},
|
||||
lpg.Filler {},
|
||||
},
|
||||
lpg.Filler {-1, 6},
|
||||
}
|
||||
end
|
||||
function define (name, ...)
|
||||
return lpg.VBox {
|
||||
lpg.Filler {-1, 2},
|
||||
lpg.Text {fontweight=600, name}, lpg.Filler {-1, 2},
|
||||
lpg.HBox {lpg.Filler {12}, lpg.VBox {...}, lpg.Filler {}},
|
||||
lpg.Filler {-1, 2},
|
||||
}
|
||||
end
|
||||
function pad (widget)
|
||||
return lpg.VBox {
|
||||
lpg.Filler {-1, 2},
|
||||
lpg.HBox {lpg.Filler {4}, widget, lpg.Filler {}, lpg.Filler {4}},
|
||||
lpg.Filler {-1, 2},
|
||||
}
|
||||
end
|
||||
|
||||
local page1 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
|
||||
h1("lpg User Manual"),
|
||||
p("<b>lpg</b> is a Lua-based PDF document generator, exposing a trivial " ..
|
||||
"layouting engine on top of the Cairo graphics library, " ..
|
||||
"with manual paging."),
|
||||
p("The author has primarily been using this system to typeset invoices."),
|
||||
|
||||
h2("Synopsis"),
|
||||
p("<b>lpg</b> <i>program.lua</i> [<i>args...</i>]"),
|
||||
|
||||
h2("API"),
|
||||
p("The Lua program receives <b>lpg</b>'s and its own path joined " ..
|
||||
"as <tt>arg[0]</tt>. Any remaining sequential elements " ..
|
||||
"of this table represent the passed <i>args</i>."),
|
||||
|
||||
h3("Utilities"),
|
||||
|
||||
define("lpg.cm (centimeters)",
|
||||
p("Returns how many document points are needed " ..
|
||||
"for the given physical length.")),
|
||||
|
||||
define("lpg.ntoa {number [, precision=…]\n" ..
|
||||
"\t[, thousands_sep=…] [, decimal_point=…] [, grouping=…]}",
|
||||
p("Formats a number using the C++ localization " ..
|
||||
"and I/O libraries. " ..
|
||||
"For example, the following call results in “3 141,59”:"),
|
||||
code("ntoa {3141.592, precision=2,\n" ..
|
||||
" thousands_sep=\" \", decimal_point=\",\", " ..
|
||||
"grouping=\"\\003\"}")),
|
||||
|
||||
define("lpg.escape (values...)",
|
||||
p("Interprets all values as strings, " ..
|
||||
"and escapes them to be used as literal text—" ..
|
||||
"all text within <b>lpg</b> is parsed as Pango markup, " ..
|
||||
"which is a subset of XML.")),
|
||||
|
||||
h3("PDF documents"),
|
||||
|
||||
define("lpg.Document (filename, width, height [, margin])",
|
||||
p("Returns a new <i>Document</i> object, whose pages are all " ..
|
||||
"the same size in 72 DPI points, as specified by <b>width</b> " ..
|
||||
"and <b>height</b>. The <b>margin</b> is used by <b>show</b> " ..
|
||||
"on all sides of pages."),
|
||||
p("The file is finalized when the object is garbage collected.")),
|
||||
|
||||
define("<i>Document</i>.title, author, subject, keywords, " ..
|
||||
"creator, create_date, mod_date",
|
||||
p("Write-only PDF <i>Info</i> dictionary metadata strings.")),
|
||||
|
||||
define("<i>Document</i>:show ([widget...])",
|
||||
p("Starts a new document page, and renders <i>Widget</i> trees over " ..
|
||||
"the whole print area.")),
|
||||
|
||||
lpg.Filler {},
|
||||
}
|
||||
|
||||
local page2 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
|
||||
h3("Widgets"),
|
||||
p("The layouting system makes heavy use of composition, " ..
|
||||
"and thus stays simple."),
|
||||
p("For convenience, anywhere a <i>Widget</i> is expected but another " ..
|
||||
"kind of value is received, <b>lpg.Text</b> widget will be invoked " ..
|
||||
"on that value."),
|
||||
p("Once a <i>Widget</i> is included in another <i>Widget</i>, " ..
|
||||
"the original Lua object can no longer be used, " ..
|
||||
"as its reference has been consumed."),
|
||||
p("<i>Widgets</i> can be indexed by strings to get or set " ..
|
||||
"their <i>attributes</i>. All <i>Widget</i> constructor tables " ..
|
||||
"also accept attributes, for convenience. Attributes can be " ..
|
||||
"either strings or numbers, mostly only act " ..
|
||||
"on specific <i>Widget</i> kinds, and are hereditary. " ..
|
||||
"Prefix their names with an underscore to set them ‘privately’."),
|
||||
p("<i>Widget</i> sizes can be set negative, which signals to their " ..
|
||||
"container that they should take any remaining space, " ..
|
||||
"after all their siblings’ requests have been satisfied. " ..
|
||||
"When multiple widgets make this request, that space is distributed " ..
|
||||
"in proportion to these negative values."),
|
||||
|
||||
define("lpg.Filler {[width] [, height]}",
|
||||
p("Returns a new blank widget with the given dimensions, " ..
|
||||
"which default to -1, -1.")),
|
||||
define("lpg.HLine {[thickness]}",
|
||||
p("Returns a new widget that draws a simple horizontal line " ..
|
||||
"of the given <b>thickness</b>.")),
|
||||
define("lpg.VLine {[thickness]}",
|
||||
p("Returns a new widget that draws a simple vertical line " ..
|
||||
"of the given <b>thickness</b>.")),
|
||||
define("lpg.Text {[value...]}",
|
||||
p("Returns a new text widget that renders the concatenation of all " ..
|
||||
"passed values filtered through Lua’s <b>tostring</b> " ..
|
||||
"function. Non-strings will additionally be escaped."),
|
||||
define("<i>Text</i>.fontfamily, fontsize, fontweight, lineheight",
|
||||
p("Various font properties, similar to their CSS counterparts."))),
|
||||
define("lpg.Frame {widget}",
|
||||
p("Returns a special container widget that can override " ..
|
||||
"a few interesting properties."),
|
||||
define("<i>Frame</i>.color",
|
||||
p("Text and line colour, for example <tt>0xff0000</tt> for red.")),
|
||||
define("<i>Frame</i>.w_override",
|
||||
p("Forcefully changes the child <i>Widget</i>’s " ..
|
||||
"requested width, such as to negative values.")),
|
||||
define("<i>Frame</i>.h_override",
|
||||
p("Forcefully changes the child <i>Widget</i>’s " ..
|
||||
"requested height, such as to negative values."))),
|
||||
|
||||
lpg.Filler {},
|
||||
}
|
||||
|
||||
local page3 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
|
||||
define("lpg.Link {target, widget}",
|
||||
p("Returns a new hyperlink widget pointing to the <b>target</b>, " ..
|
||||
"which is a URL. The hyperlink applies " ..
|
||||
"to the entire area of the child widget. " ..
|
||||
"It has no special appearance.")),
|
||||
define("lpg.HBox {[widget...]}",
|
||||
p("Returns a new container widget that places children " ..
|
||||
"horizontally, from left to right."),
|
||||
p("If any space remains after satisfying the children widgets’ " ..
|
||||
"requisitions, it is distributed equally amongst all of them. " ..
|
||||
"Also see the note about negative sizes.")),
|
||||
define("lpg.VBox {[widget...]}",
|
||||
p("Returns a new container widget that places children " ..
|
||||
"vertically, from top to bottom.")),
|
||||
define("lpg.Picture {filename}",
|
||||
p("Returns a new picture widget, showing the given <b>filename</b>, " ..
|
||||
"which currently must be in the PNG format. " ..
|
||||
"Pictures are rescaled to fit, but keep their aspect ratio.")),
|
||||
define("lpg.QR {contents, module}",
|
||||
p("Returns a new QR code widget, encoding the <b>contents</b> " ..
|
||||
"string using the given <b>module</b> size. " ..
|
||||
"The QR code version is chosen automatically.")),
|
||||
|
||||
h2("Examples"),
|
||||
p("See the source code of this user manual " ..
|
||||
"for the general structure of scripts."),
|
||||
|
||||
h3("Size distribution and composition"),
|
||||
lpg.VBox {
|
||||
lpg.HLine {},
|
||||
lpg.HBox {
|
||||
lpg.VLine {}, lpg.Frame {_w_override=lpg.cm(3), pad "3cm"},
|
||||
lpg.VLine {}, lpg.Frame {pad "Measured"},
|
||||
lpg.VLine {}, lpg.Frame {_w_override=-1, pad "-1"},
|
||||
lpg.VLine {}, lpg.Frame {_w_override=-2, pad "-2"},
|
||||
lpg.VLine {},
|
||||
},
|
||||
lpg.HLine {},
|
||||
},
|
||||
lpg.Filler {-1, 6},
|
||||
code([[
|
||||
<small><b>function</b> pad (widget)
|
||||
<b>local function</b> f (...) <b>return</b> lpg.Filler {...} <b>end</b>
|
||||
<b>return</b> lpg.VBox {f(-1, 2), lpg.HBox {f(4), w, f(), f(4)}, f(-1, 2)}
|
||||
<b>end</b>
|
||||
|
||||
lpg.VBox {lpg.HLine {}, lpg.HBox {
|
||||
lpg.VLine {}, lpg.Frame {_w_override=lpg.cm(3), pad "3cm"},
|
||||
lpg.VLine {}, lpg.Frame {pad "Measured"},
|
||||
lpg.VLine {}, lpg.Frame {_w_override=-1, pad "-1"},
|
||||
lpg.VLine {}, lpg.Frame {_w_override=-2, pad "-2"},
|
||||
lpg.VLine {},
|
||||
}, lpg.HLine {}}</small>]]),
|
||||
|
||||
h3("Clickable QR code link"),
|
||||
lpg.HBox {
|
||||
lpg.VBox {
|
||||
p("Go here to report bugs, request features, " ..
|
||||
"or submit pull requests:"),
|
||||
code(([[
|
||||
url = "%s"
|
||||
lpg.Link {url, lpg.QR {url, 2.5}}]]):format(project_url)),
|
||||
},
|
||||
lpg.Filler {},
|
||||
lpg.Link {project_url, lpg.QR {project_url, 2.5}},
|
||||
},
|
||||
|
||||
lpg.Filler {},
|
||||
}
|
||||
|
||||
if #arg < 1 then
|
||||
io.stderr:write("Usage: " .. arg[0] .. " OUTPUT-PDF..." .. "\n")
|
||||
os.exit(false)
|
||||
end
|
||||
local width, height, margin = lpg.cm(21), lpg.cm(29.7), lpg.cm(2.0)
|
||||
for i = 1, #arg do
|
||||
local pdf = lpg.Document(arg[i], width, height, margin)
|
||||
pdf.title = "lpg User Manual"
|
||||
pdf.subject = "lpg User Manual"
|
||||
pdf.author = "Přemysl Eric Janouch"
|
||||
pdf.creator = ("lpg (%s)"):format(project_url)
|
||||
|
||||
pdf:show(page1)
|
||||
pdf:show(page2)
|
||||
pdf:show(page3)
|
||||
end
|
||||
24
lpg/meson.build
Normal file
24
lpg/meson.build
Normal file
@@ -0,0 +1,24 @@
|
||||
project('lpg', 'cpp', default_options : ['cpp_std=c++17'],
|
||||
version : '1.1.1')
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set_quoted('PROJECT_NAME', meson.project_name())
|
||||
conf.set_quoted('PROJECT_VERSION', meson.project_version())
|
||||
configure_file(output : 'config.h', configuration : conf)
|
||||
|
||||
luapp = dependency('lua++', allow_fallback : true)
|
||||
cairo = dependency('cairo')
|
||||
pangocairo = dependency('pangocairo')
|
||||
libqrencode = dependency('libqrencode')
|
||||
lpg_exe = executable('lpg', 'lpg.cpp',
|
||||
install : true,
|
||||
dependencies : [luapp, cairo, pangocairo, libqrencode])
|
||||
|
||||
# XXX: https://github.com/mesonbuild/meson/issues/825
|
||||
docdir = get_option('datadir') / 'doc' / meson.project_name()
|
||||
lpg_pdf = custom_target('lpg.pdf',
|
||||
output : 'lpg.pdf',
|
||||
input : 'lpg.lua',
|
||||
command : [lpg_exe, '@INPUT@', '@OUTPUT@'],
|
||||
install_dir : docdir,
|
||||
build_by_default : true)
|
||||
10
lpg/subprojects/lua++.wrap
Normal file
10
lpg/subprojects/lua++.wrap
Normal file
@@ -0,0 +1,10 @@
|
||||
[wrap-file]
|
||||
directory = lua-5.4.7
|
||||
source_url = https://github.com/lua/lua/archive/refs/tags/v5.4.7.tar.gz
|
||||
source_filename = lua-5.4.7.tar.gz
|
||||
source_hash = 5c39111b3fc4c1c9e56671008955a1730f54a15b95e1f1bd0752b868b929d8e3
|
||||
patch_directory = lua-5.4.7
|
||||
|
||||
[provide]
|
||||
lua++-5.4 = lua_dep
|
||||
lua++ = lua_dep
|
||||
20
lpg/subprojects/packagefiles/lua-5.4.7/LICENSE.build
Normal file
20
lpg/subprojects/packagefiles/lua-5.4.7/LICENSE.build
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2025 Přemysl Eric Janouch <p@janouch.name>
|
||||
Copyright (c) 2021 The Meson development team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
50
lpg/subprojects/packagefiles/lua-5.4.7/meson.build
Normal file
50
lpg/subprojects/packagefiles/lua-5.4.7/meson.build
Normal file
@@ -0,0 +1,50 @@
|
||||
project(
|
||||
'lua-5.4',
|
||||
'cpp',
|
||||
license : 'MIT',
|
||||
meson_version : '>=0.49.2',
|
||||
version : '5.4.7',
|
||||
default_options : ['c_std=c99', 'warning_level=2'],
|
||||
)
|
||||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
# Skip bogus warning.
|
||||
add_project_arguments(cxx.get_supported_arguments(
|
||||
'-Wno-string-plus-int', '-Wno-stringop-overflow'), language : 'cpp')
|
||||
|
||||
# Platform-specific defines.
|
||||
is_posix = host_machine.system() in ['cygwin', 'darwin', 'dragonfly', 'freebsd',
|
||||
'gnu', 'haiku', 'linux', 'netbsd', 'openbsd', 'sunos']
|
||||
if is_posix
|
||||
add_project_arguments('-DLUA_USE_POSIX', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Library dependencies.
|
||||
lua_lib_deps = [cxx.find_library('m', required : false)]
|
||||
|
||||
if meson.version().version_compare('>= 0.62')
|
||||
dl_dep = dependency('dl', required : get_option('loadlib'))
|
||||
else
|
||||
dl_dep = cxx.find_library('dl', required : get_option('loadlib'))
|
||||
endif
|
||||
|
||||
if dl_dep.found()
|
||||
lua_lib_deps += dl_dep
|
||||
add_project_arguments('-DLUA_USE_DLOPEN', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Targets.
|
||||
add_project_arguments('-DMAKE_LIB', language : 'cpp')
|
||||
lua_lib = static_library(
|
||||
'lua',
|
||||
'onelua.cpp',
|
||||
dependencies : lua_lib_deps,
|
||||
implicit_include_directories : false,
|
||||
)
|
||||
|
||||
inc = include_directories('.')
|
||||
lua_dep = declare_dependency(
|
||||
link_with : lua_lib,
|
||||
include_directories : inc,
|
||||
)
|
||||
4
lpg/subprojects/packagefiles/lua-5.4.7/meson_options.txt
Normal file
4
lpg/subprojects/packagefiles/lua-5.4.7/meson_options.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
option(
|
||||
'loadlib', type : 'feature',
|
||||
description : 'Allow Lua to "require" C extension modules'
|
||||
)
|
||||
1
lpg/subprojects/packagefiles/lua-5.4.7/onelua.cpp
Normal file
1
lpg/subprojects/packagefiles/lua-5.4.7/onelua.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "onelua.c"
|
||||
@@ -14,10 +14,10 @@ executable('pdf-simple-sign', 'pdf-simple-sign.cpp',
|
||||
asciidoctor = find_program('asciidoctor')
|
||||
foreach page : ['pdf-simple-sign']
|
||||
custom_target('manpage for ' + page,
|
||||
input: page + '.adoc', output: page + '.1',
|
||||
command: [asciidoctor, '-b', 'manpage',
|
||||
input : page + '.adoc', output: page + '.1',
|
||||
command : [asciidoctor, '-b', 'manpage',
|
||||
'-a', 'release-version=' + meson.project_version(),
|
||||
'@INPUT@', '-o', '@OUTPUT@'],
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('mandir'), 'man1'))
|
||||
install : true,
|
||||
install_dir : join_paths(get_option('mandir'), 'man1'))
|
||||
endforeach
|
||||
|
||||
3
test.sh
3
test.sh
@@ -66,7 +66,8 @@ for tool in "$@"; do
|
||||
|
||||
# Only some of our generators use PDF versions higher than 1.5
|
||||
log "Testing $tool for version detection"
|
||||
grep -q "/Version /1.6" "$result" || grep -q "^%PDF-1.6" "$result" \
|
||||
grep -q "/Version /1[.]6" "$result" \
|
||||
|| grep -q "^%PDF-1[.][67]" "$result" \
|
||||
|| die "Version detection seems to misbehave (no upgrade)"
|
||||
done
|
||||
|
||||
|
||||
Reference in New Issue
Block a user