ughost 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ughost-1.0.0/PKG-INFO +27 -0
- ughost-1.0.0/README +6 -0
- ughost-1.0.0/pyproject.toml +67 -0
- ughost-1.0.0/setup.cfg +4 -0
- ughost-1.0.0/tests/test_cli.py +35 -0
- ughost-1.0.0/tests/test_extract.py +38 -0
- ughost-1.0.0/tests/test_optimize.py +26 -0
- ughost-1.0.0/tests/test_part.py +48 -0
- ughost-1.0.0/tests/test_pdfwrite.py +51 -0
- ughost-1.0.0/ughost/__init__.py +27 -0
- ughost-1.0.0/ughost/cli.py +89 -0
- ughost-1.0.0/ughost/extract.py +52 -0
- ughost-1.0.0/ughost/optimize.py +25 -0
- ughost-1.0.0/ughost/parts.py +161 -0
- ughost-1.0.0/ughost/utils.py +56 -0
- ughost-1.0.0/ughost.egg-info/PKG-INFO +27 -0
- ughost-1.0.0/ughost.egg-info/SOURCES.txt +19 -0
- ughost-1.0.0/ughost.egg-info/dependency_links.txt +1 -0
- ughost-1.0.0/ughost.egg-info/entry_points.txt +2 -0
- ughost-1.0.0/ughost.egg-info/requires.txt +9 -0
- ughost-1.0.0/ughost.egg-info/top_level.txt +1 -0
ughost-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ughost
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Author-email: Helmut Konrad Schewe <helmutus@outlook.com>
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/anaticulae/ughost
|
|
7
|
+
Project-URL: Repository, https://github.com/anaticulae/ughost
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: utilo<3.0.0,>=2.107.5
|
|
14
|
+
Requires-Dist: iamraw<5.0.0,>=4.91.4
|
|
15
|
+
Requires-Dist: Pillow<13.0.0,>=12.2.0
|
|
16
|
+
Requires-Dist: pdflog<2.0.0,>=1.0.2
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: utilotest<2.0.0,>=1.0.3; extra == "dev"
|
|
19
|
+
Requires-Dist: hoverpower<2.0.0,>=1.1.0; extra == "dev"
|
|
20
|
+
Requires-Dist: upainter<2.0.0,>=1.0.0; extra == "dev"
|
|
21
|
+
|
|
22
|
+
# ughost
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
minidocks/ughostscript
|
|
26
|
+
minidocks/python
|
|
27
|
+
```
|
ughost-1.0.0/README
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ughost"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = ""
|
|
5
|
+
readme = {file = "README", content-type = "text/markdown"}
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
authors = [
|
|
8
|
+
{name = "Helmut Konrad Schewe", email = "helmutus@outlook.com"}
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
"utilo>=2.107.5,<3.0.0",
|
|
13
|
+
"iamraw>=4.91.4,<5.0.0",
|
|
14
|
+
"Pillow>=12.2.0,<13.0.0",
|
|
15
|
+
"pdflog>=1.0.2,<2.0.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
# Optional but recommended metadata
|
|
19
|
+
keywords = []
|
|
20
|
+
classifiers = [
|
|
21
|
+
'Programming Language :: Python :: 3.12',
|
|
22
|
+
'Programming Language :: Python :: 3.13',
|
|
23
|
+
'Programming Language :: Python :: 3.14',
|
|
24
|
+
]
|
|
25
|
+
license = "MIT"
|
|
26
|
+
license-files = ["LICENSE"]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"utilotest>=1.0.3,<2.0.0",
|
|
31
|
+
"hoverpower>=1.1.0,<2.0.0",
|
|
32
|
+
"upainter>=1.0.0,<2.0.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/anaticulae/ughost"
|
|
37
|
+
Repository = "https://github.com/anaticulae/ughost"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
ghost = "ughost.cli:main"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = [
|
|
44
|
+
"setuptools>=82.0.1",
|
|
45
|
+
"wheel>=0.47.0"
|
|
46
|
+
]
|
|
47
|
+
build-backend = "setuptools.build_meta"
|
|
48
|
+
|
|
49
|
+
[tool.semantic_release]
|
|
50
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
51
|
+
[tool.semantic_release.changelog.default_templates]
|
|
52
|
+
changelog_file = "CHANGELOG"
|
|
53
|
+
[tool.semantic_release.changelog]
|
|
54
|
+
mode = "init"
|
|
55
|
+
output_format = "md"
|
|
56
|
+
[tool.semantic_release.commit_parser_options]
|
|
57
|
+
# allways generate a new version
|
|
58
|
+
patch_tags = ["fix", "perf", "build", "chore", "ci", "docs", "style", "refactor", "test", "deps"]
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.packages.find]
|
|
61
|
+
where = ["."]
|
|
62
|
+
include = [
|
|
63
|
+
'ughost',
|
|
64
|
+
]
|
|
65
|
+
exclude = [
|
|
66
|
+
"tests*",
|
|
67
|
+
]
|
ughost-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import hoverpower
|
|
11
|
+
import utilo
|
|
12
|
+
import utilotest
|
|
13
|
+
|
|
14
|
+
import tests
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_help(mp):
|
|
18
|
+
tests.run('--help', mp=mp)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@tests.ughostscript
|
|
22
|
+
def test_run(td, mp):
|
|
23
|
+
outpath = td.tmpdir
|
|
24
|
+
cmd = f'-i {hoverpower.TECH019_PDF} -o {outpath} --pages=3:8,12:15'
|
|
25
|
+
tests.run(cmd, mp=mp)
|
|
26
|
+
assert utilo.file_count(outpath) == 8
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@tests.ughostscript
|
|
30
|
+
@utilotest.longrun
|
|
31
|
+
def test_all(td, mp):
|
|
32
|
+
outpath = td.tmpdir
|
|
33
|
+
cmd = f'-i {hoverpower.BACHELOR032_PDF} -o {outpath}'
|
|
34
|
+
tests.run(cmd, mp=mp)
|
|
35
|
+
assert utilo.file_count(outpath) == 32
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import hoverpower
|
|
13
|
+
import iamraw
|
|
14
|
+
import utilo
|
|
15
|
+
import utilotest
|
|
16
|
+
|
|
17
|
+
import tests
|
|
18
|
+
import ughost
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@tests.ughostscript
|
|
22
|
+
def test_images(td):
|
|
23
|
+
boundings = [
|
|
24
|
+
iamraw.ImageInformation(
|
|
25
|
+
page=0,
|
|
26
|
+
bounding=(250, 250, 1000, 1000),
|
|
27
|
+
),
|
|
28
|
+
]
|
|
29
|
+
loaded = ughost.images(
|
|
30
|
+
source=hoverpower.BACHELOR051_PDF,
|
|
31
|
+
boundings=boundings,
|
|
32
|
+
)
|
|
33
|
+
assert len(loaded) == 1
|
|
34
|
+
png = os.path.join(td.tmpdir, 'test.png')
|
|
35
|
+
image = loaded[0]
|
|
36
|
+
utilo.file_create_binary(png, content=image)
|
|
37
|
+
# verify result
|
|
38
|
+
utilotest.assert_bin(image, (3555017284, 3680991176, 1320373311))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import hoverpower
|
|
13
|
+
import pdflog
|
|
14
|
+
|
|
15
|
+
import tests
|
|
16
|
+
import ughost
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@tests.ughostscript
|
|
20
|
+
def test_optimize_small(td):
|
|
21
|
+
"""Shrink pdf to given number of pages."""
|
|
22
|
+
source = hoverpower.PAPER06B_PDF
|
|
23
|
+
outpath = os.path.join(td.tmpdir, 'optimo.pdf')
|
|
24
|
+
ughost.small(source, outpath, pages=(3, 4))
|
|
25
|
+
pages = pdflog.pagecount(outpath)
|
|
26
|
+
assert pages == 2
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import hoverpower
|
|
11
|
+
import upainter
|
|
12
|
+
|
|
13
|
+
import tests
|
|
14
|
+
import ughost.parts
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@tests.ughostscript
|
|
18
|
+
def test_parts(td): # pylint:disable=W0613
|
|
19
|
+
source = hoverpower.BACHELOR028_PDF
|
|
20
|
+
boundings = [
|
|
21
|
+
ughost.parts.Part(
|
|
22
|
+
page=0,
|
|
23
|
+
bounding=(800.0, 1000.0, 1000.0, 1800),
|
|
24
|
+
)
|
|
25
|
+
]
|
|
26
|
+
extracted = ughost.parts.parts(
|
|
27
|
+
source=source,
|
|
28
|
+
boundings=boundings,
|
|
29
|
+
)
|
|
30
|
+
assert len(extracted) == 1
|
|
31
|
+
upainter.show_figure(extracted[0])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@tests.ughostscript
|
|
35
|
+
def test_run_extractor(td):
|
|
36
|
+
source = hoverpower.BACHELOR028_PDF
|
|
37
|
+
boundings = [
|
|
38
|
+
ughost.parts.Part(
|
|
39
|
+
page=0,
|
|
40
|
+
bounding=(800.0, 1000.0, 1000.0, 1800),
|
|
41
|
+
)
|
|
42
|
+
]
|
|
43
|
+
extracted = ughost.parts.run(
|
|
44
|
+
src=source,
|
|
45
|
+
dst=td.tmpdir,
|
|
46
|
+
boundings=boundings,
|
|
47
|
+
)
|
|
48
|
+
assert len(extracted) == 1
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import hoverpower
|
|
13
|
+
import utilo
|
|
14
|
+
import utilotest
|
|
15
|
+
|
|
16
|
+
import tests
|
|
17
|
+
import ughost
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@tests.ughostscript
|
|
21
|
+
@utilotest.longrun
|
|
22
|
+
def test_pdfwrite_all():
|
|
23
|
+
source = hoverpower.TECH019_PDF
|
|
24
|
+
path = ughost.pdfwrite(source)
|
|
25
|
+
extracted = utilo.file_list(path)
|
|
26
|
+
assert len(extracted) == 19
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@tests.ughostscript
|
|
30
|
+
def test_pdfwrite_pages():
|
|
31
|
+
"""ughost script page numbers are ascending instead of names by page
|
|
32
|
+
number."""
|
|
33
|
+
source = hoverpower.TECH019_PDF
|
|
34
|
+
path = ughost.pdfwrite(source, pages=(3, 7))
|
|
35
|
+
extracted = utilo.file_list(path)
|
|
36
|
+
expected = ['1.png', '2.png']
|
|
37
|
+
assert extracted == expected
|
|
38
|
+
loaded = [
|
|
39
|
+
utilo.file_read_binary(os.path.join(path, item)) for item in extracted
|
|
40
|
+
]
|
|
41
|
+
# verify that page number converting works
|
|
42
|
+
utilotest.assert_bin(loaded[0], (3201675645, 1609777475, 2024650708))
|
|
43
|
+
utilotest.assert_bin(loaded[1], (1204049905, 3839788996, 2476290319))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@tests.ughostscript
|
|
47
|
+
def test_pdfwrite_with_spaces(td):
|
|
48
|
+
dst = td.tmpdir.join('space with space.pdf')
|
|
49
|
+
utilo.file_copy(src=hoverpower.TECH019_PDF, dst=dst)
|
|
50
|
+
ughost.pdfwrite(dst, root=td.tmpdir, pages=1)
|
|
51
|
+
assert len(utilo.file_list(path=td.tmpdir)) == 2
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
#------------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
#==============================================================================
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import utilo
|
|
13
|
+
|
|
14
|
+
from ughost.extract import images
|
|
15
|
+
from ughost.optimize import small
|
|
16
|
+
from ughost.parts import Part
|
|
17
|
+
from ughost.parts import bounding_convert
|
|
18
|
+
from ughost.parts import run
|
|
19
|
+
from ughost.utils import pdfwrite
|
|
20
|
+
|
|
21
|
+
__version__ = '0.9.1'
|
|
22
|
+
|
|
23
|
+
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
|
24
|
+
PROCESS = 'ughost'
|
|
25
|
+
|
|
26
|
+
INSTALLED = utilo.hasprog('gs') or utilo.hasprog('gswin64c')
|
|
27
|
+
HAS_GHOST = INSTALLED
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import pdflog
|
|
11
|
+
import utilo
|
|
12
|
+
import utilo.cli
|
|
13
|
+
|
|
14
|
+
import ughost
|
|
15
|
+
|
|
16
|
+
DESCRIPTION = ''
|
|
17
|
+
CONFIG = utilo.ParserConfiguration(
|
|
18
|
+
inputparameter=True,
|
|
19
|
+
outputparameter=True,
|
|
20
|
+
prefix=False,
|
|
21
|
+
multiprocessed=False,
|
|
22
|
+
cacheflag=False,
|
|
23
|
+
waitingflag=False,
|
|
24
|
+
)
|
|
25
|
+
DPI = 216.0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@utilo.saveme
|
|
29
|
+
def main():
|
|
30
|
+
inpath, outpath, dpi, pages = eval_cli()
|
|
31
|
+
write_images(
|
|
32
|
+
inpath,
|
|
33
|
+
outpath=outpath,
|
|
34
|
+
dpi=dpi,
|
|
35
|
+
pages=pages,
|
|
36
|
+
)
|
|
37
|
+
return utilo.SUCCESS
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def eval_cli():
|
|
41
|
+
parser = utilo.cli.create_parser(
|
|
42
|
+
config=CONFIG,
|
|
43
|
+
description=DESCRIPTION,
|
|
44
|
+
todo=[
|
|
45
|
+
utilo.Parameter(
|
|
46
|
+
longcut='dpi',
|
|
47
|
+
message='use 216 as default',
|
|
48
|
+
args={'default': DPI},
|
|
49
|
+
)
|
|
50
|
+
],
|
|
51
|
+
prog=ughost.PROCESS,
|
|
52
|
+
version=ughost.__version__,
|
|
53
|
+
)
|
|
54
|
+
args = utilo.parse(parser)
|
|
55
|
+
inpath, outpath = utilo.sources(args, singleinput=True) # pylint:disable=W0632
|
|
56
|
+
# It is only single path supported. Run program multiple times if more
|
|
57
|
+
# than one analysis is required.
|
|
58
|
+
inpath = inpath[0]
|
|
59
|
+
dpi = args.get('dpi', DPI)
|
|
60
|
+
pages = parse_pages(
|
|
61
|
+
args.get('pages', None),
|
|
62
|
+
inpath=inpath,
|
|
63
|
+
)
|
|
64
|
+
return inpath, outpath, dpi, pages
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def write_images(inpath, outpath, dpi: float, pages: tuple = None):
|
|
68
|
+
root = ughost.pdfwrite(
|
|
69
|
+
source=inpath,
|
|
70
|
+
dpi=dpi,
|
|
71
|
+
pages=pages,
|
|
72
|
+
)
|
|
73
|
+
written = utilo.file_list(root, include='png', absolute=True)
|
|
74
|
+
for path, filename in zip(written, pages):
|
|
75
|
+
filename = f'{str(filename).zfill(3)}.png'
|
|
76
|
+
dst = utilo.join(outpath, filename)
|
|
77
|
+
utilo.debug(f'write {dst}')
|
|
78
|
+
utilo.file_copy(path, dst=dst)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def parse_pages(pages: tuple, inpath: str) -> tuple:
|
|
82
|
+
pagecount = pdflog.pagecount(inpath)
|
|
83
|
+
if not pages:
|
|
84
|
+
return utilo.rtuple(pagecount)
|
|
85
|
+
pages = utilo.parse_pages(
|
|
86
|
+
pages[0],
|
|
87
|
+
pagecount=pagecount,
|
|
88
|
+
)
|
|
89
|
+
return pages
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import io
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import iamraw
|
|
14
|
+
import PIL.Image
|
|
15
|
+
import utilo
|
|
16
|
+
|
|
17
|
+
import ughost
|
|
18
|
+
|
|
19
|
+
DPI = 72
|
|
20
|
+
RENDERER = 300
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def images(source: str, boundings: iamraw.ImageInformations, dpi=DPI) -> list:
|
|
24
|
+
# ensure that bounding box matches with correct page
|
|
25
|
+
pages = sorted(set(item.page for item in boundings))
|
|
26
|
+
root = ughost.pdfwrite(source, dpi=RENDERER, pages=pages)
|
|
27
|
+
pagenr = {page: index for index, page in enumerate(pages, start=1)}
|
|
28
|
+
loaded = [
|
|
29
|
+
load_image(
|
|
30
|
+
bounding,
|
|
31
|
+
path=os.path.join(root, f'{pagenr[bounding.page]}.png'),
|
|
32
|
+
dpi=dpi,
|
|
33
|
+
) for bounding in boundings
|
|
34
|
+
]
|
|
35
|
+
return loaded
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_image(bounding: iamraw.ImageInformation, path: str, dpi=DPI) -> bytes:
|
|
39
|
+
raw = io.BytesIO()
|
|
40
|
+
with PIL.Image.open(path, formats=('png',)) as loaded:
|
|
41
|
+
# left, upper, right, lower
|
|
42
|
+
bounding = utilo.tuple_mult(
|
|
43
|
+
bounding.bounding,
|
|
44
|
+
value=RENDERER / dpi,
|
|
45
|
+
)
|
|
46
|
+
croped = loaded.crop(bounding)
|
|
47
|
+
croped.save(raw, format='png')
|
|
48
|
+
# rewind the buffer
|
|
49
|
+
raw.seek(0)
|
|
50
|
+
# convert to bytes
|
|
51
|
+
result = raw.getvalue()
|
|
52
|
+
return result
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
"""Optimize PDF
|
|
10
|
+
============
|
|
11
|
+
|
|
12
|
+
Use ughostScript to optimize pdf file.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import utilo
|
|
16
|
+
|
|
17
|
+
import ughost.utils
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def small(source: str, destination: str, pages: tuple = None):
|
|
21
|
+
pages = ughost.utils.gpages_fromtuple(pages)
|
|
22
|
+
config = '-sDEVICE=pdfwrite -dBATCH -dNOPAUSE -SAFE'
|
|
23
|
+
source = f'"{source}"'
|
|
24
|
+
cmd = f'{ughost.utils.GHOST} {config} {pages} -sOutputFile={destination} {source}'
|
|
25
|
+
utilo.run(cmd)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import dataclasses
|
|
11
|
+
import io
|
|
12
|
+
|
|
13
|
+
import PIL.Image
|
|
14
|
+
import PIL.ImageChops
|
|
15
|
+
import PIL.ImageDraw
|
|
16
|
+
import utilo
|
|
17
|
+
|
|
18
|
+
import ughost
|
|
19
|
+
import ughost.cli
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclasses.dataclass
|
|
23
|
+
class Part:
|
|
24
|
+
page: int = None
|
|
25
|
+
bounding: tuple = None
|
|
26
|
+
color: tuple = None
|
|
27
|
+
name: str = None
|
|
28
|
+
|
|
29
|
+
def __getitem__(self, index):
|
|
30
|
+
if not index:
|
|
31
|
+
return self.page
|
|
32
|
+
if index == 1:
|
|
33
|
+
return self.bounding
|
|
34
|
+
if index == 2:
|
|
35
|
+
return self.color
|
|
36
|
+
if index == 3:
|
|
37
|
+
return self.name
|
|
38
|
+
raise IndexError
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run(src: str, dst: str, boundings: list) -> list:
|
|
42
|
+
result = []
|
|
43
|
+
extracted = parts(source=src, boundings=boundings)
|
|
44
|
+
for image, bounding in zip(extracted, boundings):
|
|
45
|
+
name = bounding.name
|
|
46
|
+
if name is None:
|
|
47
|
+
name = utilo.tmpname()
|
|
48
|
+
outpath = utilo.join(dst, f'{name}.png')
|
|
49
|
+
utilo.debug(outpath)
|
|
50
|
+
# TODO: SECURITY: BEFORE RELASE: USE PRIVATE LATER
|
|
51
|
+
utilo.file_replace_binary(outpath, content=image)
|
|
52
|
+
result.append(outpath)
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# TODO: HOLY VALUE
|
|
57
|
+
DPI_PDF = 72.0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def bounding_convert(pdf: tuple, dpi: int = ughost.cli.DPI) -> tuple:
|
|
61
|
+
"""\
|
|
62
|
+
>>> bounding_convert((120, 120, 520, 520))
|
|
63
|
+
(360.0, 360.0, 1560.0, 1560.0)
|
|
64
|
+
"""
|
|
65
|
+
assert dpi and DPI_PDF, f'invalid dpi: {dpi}, {DPI_PDF}'
|
|
66
|
+
result = utilo.tuple_mult(
|
|
67
|
+
pdf,
|
|
68
|
+
value=dpi / DPI_PDF,
|
|
69
|
+
)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def parts(source: str, boundings: list) -> list:
|
|
74
|
+
extracted = extract(
|
|
75
|
+
source=source,
|
|
76
|
+
boundings=boundings,
|
|
77
|
+
)
|
|
78
|
+
result = []
|
|
79
|
+
for item in boundings:
|
|
80
|
+
page, bounding = item.page, item.bounding
|
|
81
|
+
color = item.color
|
|
82
|
+
path = extracted[page]
|
|
83
|
+
part = extract_part(path, bounding, color)
|
|
84
|
+
result.append(part)
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def extract(source, boundings: list) -> dict:
|
|
89
|
+
workdir = utilo.tmpdir(root=ughost.ROOT)
|
|
90
|
+
pages = set(item[0] for item in boundings)
|
|
91
|
+
pages = sorted(pages)
|
|
92
|
+
pagesraw: str = ','.join(str(item) for item in pages)
|
|
93
|
+
cmd = f'{ughost.PROCESS} -i {source} -o {workdir} --pages={pagesraw}'
|
|
94
|
+
utilo.run(cmd)
|
|
95
|
+
files = utilo.file_list(
|
|
96
|
+
workdir,
|
|
97
|
+
absolute=True,
|
|
98
|
+
)
|
|
99
|
+
result = dict(zip(
|
|
100
|
+
pages,
|
|
101
|
+
files,
|
|
102
|
+
))
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def extract_part(path: str, bounding: tuple, color=None) -> bytes:
|
|
107
|
+
raw = io.BytesIO()
|
|
108
|
+
with PIL.Image.open(path, formats=('png',)) as loaded:
|
|
109
|
+
size = loaded._size # pylint:disable=W0212
|
|
110
|
+
mask = create_mask(
|
|
111
|
+
bounding,
|
|
112
|
+
size=size,
|
|
113
|
+
)
|
|
114
|
+
image = PIL.ImageChops.multiply(loaded, mask)
|
|
115
|
+
image = colorize(image, color=color)
|
|
116
|
+
image.save(raw, format='png')
|
|
117
|
+
# rewind the buffer
|
|
118
|
+
raw.seek(0)
|
|
119
|
+
# convert to bytes
|
|
120
|
+
result = raw.getvalue()
|
|
121
|
+
return result
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def create_mask(bounding: tuple, size: tuple) -> PIL.Image:
|
|
125
|
+
"""Color list of rectangles defined in `bounding`."""
|
|
126
|
+
single = isinstance(bounding[0], (int, float))
|
|
127
|
+
if single:
|
|
128
|
+
bounding = [bounding]
|
|
129
|
+
image = PIL.Image.new(
|
|
130
|
+
mode='RGBA',
|
|
131
|
+
size=size,
|
|
132
|
+
)
|
|
133
|
+
draw = PIL.ImageDraw.Draw(image)
|
|
134
|
+
color = (0, 0, 0, 255)
|
|
135
|
+
for bbox in bounding:
|
|
136
|
+
# TODO: VERYIFY OVERLAPPING RECTANLGES
|
|
137
|
+
draw.rectangle(
|
|
138
|
+
bbox,
|
|
139
|
+
fill=color,
|
|
140
|
+
)
|
|
141
|
+
return image
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def colorize(image, color):
|
|
145
|
+
if color is None:
|
|
146
|
+
color = (255, 0, 0)
|
|
147
|
+
# split the image into individual bands
|
|
148
|
+
source = image.split()
|
|
149
|
+
alpha = 3
|
|
150
|
+
# select regions where alpha is selected
|
|
151
|
+
mask = source[alpha].point(lambda i: i)
|
|
152
|
+
# process the green band
|
|
153
|
+
for band, col in enumerate(color):
|
|
154
|
+
source[band].paste(
|
|
155
|
+
source[band].point(lambda i: col), # pylint:disable=cell-var-from-loop
|
|
156
|
+
None,
|
|
157
|
+
mask,
|
|
158
|
+
)
|
|
159
|
+
# build a new multiband image
|
|
160
|
+
image = PIL.Image.merge(image.mode, source)
|
|
161
|
+
return image
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# C O P Y R I G H T
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
|
|
5
|
+
# This file is property of Helmut Konrad Schewe. Any unauthorized copy,
|
|
6
|
+
# use or distribution is an offensive act against international law and may
|
|
7
|
+
# be prosecuted under federal law. Its content is company confidential.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import utilo
|
|
13
|
+
|
|
14
|
+
import ughost
|
|
15
|
+
|
|
16
|
+
GHOST = 'gswin64c' if utilo.iswin() else 'gs'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def pdfwrite(
|
|
20
|
+
source: str,
|
|
21
|
+
dpi: int = 300,
|
|
22
|
+
formats: str = 'pngalpha',
|
|
23
|
+
root: str = None,
|
|
24
|
+
pages: tuple = None,
|
|
25
|
+
):
|
|
26
|
+
root = utilo.tmpdir(root=ughost.ROOT) if root is None else root
|
|
27
|
+
if isinstance(pages, int): # pylint:disable=W0160
|
|
28
|
+
destination = os.path.join(root, f'{pages}.png')
|
|
29
|
+
else:
|
|
30
|
+
destination = os.path.join(root, '%d.png')
|
|
31
|
+
pages = gpages_fromtuple(pages)
|
|
32
|
+
config = f'-sDEVICE={formats} -r{dpi} -dBATCH -dNOPAUSE -SAFE'
|
|
33
|
+
source = f'"{source}"'
|
|
34
|
+
cmd = f'{GHOST} {config} {pages} -sOutputFile={destination} {source}'
|
|
35
|
+
utilo.run(cmd)
|
|
36
|
+
return root
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def gpages_fromtuple(pages: tuple = None) -> str:
|
|
40
|
+
"""\
|
|
41
|
+
>>> gpages_fromtuple((1, 2, 3))
|
|
42
|
+
'-sPageList=2,3,4'
|
|
43
|
+
>>> gpages_fromtuple()
|
|
44
|
+
''
|
|
45
|
+
"""
|
|
46
|
+
if pages is None:
|
|
47
|
+
return ''
|
|
48
|
+
if isinstance(pages, int):
|
|
49
|
+
pages = (pages,)
|
|
50
|
+
# ughost requires sorted page numbers
|
|
51
|
+
pages = sorted(pages)
|
|
52
|
+
# -sPageList=1,3,5
|
|
53
|
+
pages = utilo.tuple_plus(pages, value=1)
|
|
54
|
+
pages: str = utilo.from_tuple(pages, separator=',')
|
|
55
|
+
pages: str = f'-sPageList={pages}'
|
|
56
|
+
return pages
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ughost
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Author-email: Helmut Konrad Schewe <helmutus@outlook.com>
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/anaticulae/ughost
|
|
7
|
+
Project-URL: Repository, https://github.com/anaticulae/ughost
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: utilo<3.0.0,>=2.107.5
|
|
14
|
+
Requires-Dist: iamraw<5.0.0,>=4.91.4
|
|
15
|
+
Requires-Dist: Pillow<13.0.0,>=12.2.0
|
|
16
|
+
Requires-Dist: pdflog<2.0.0,>=1.0.2
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: utilotest<2.0.0,>=1.0.3; extra == "dev"
|
|
19
|
+
Requires-Dist: hoverpower<2.0.0,>=1.1.0; extra == "dev"
|
|
20
|
+
Requires-Dist: upainter<2.0.0,>=1.0.0; extra == "dev"
|
|
21
|
+
|
|
22
|
+
# ughost
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
minidocks/ughostscript
|
|
26
|
+
minidocks/python
|
|
27
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
README
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tests/test_cli.py
|
|
4
|
+
tests/test_extract.py
|
|
5
|
+
tests/test_optimize.py
|
|
6
|
+
tests/test_part.py
|
|
7
|
+
tests/test_pdfwrite.py
|
|
8
|
+
ughost/__init__.py
|
|
9
|
+
ughost/cli.py
|
|
10
|
+
ughost/extract.py
|
|
11
|
+
ughost/optimize.py
|
|
12
|
+
ughost/parts.py
|
|
13
|
+
ughost/utils.py
|
|
14
|
+
ughost.egg-info/PKG-INFO
|
|
15
|
+
ughost.egg-info/SOURCES.txt
|
|
16
|
+
ughost.egg-info/dependency_links.txt
|
|
17
|
+
ughost.egg-info/entry_points.txt
|
|
18
|
+
ughost.egg-info/requires.txt
|
|
19
|
+
ughost.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ughost
|