ughost 1.0.0__py3-none-any.whl

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/__init__.py ADDED
@@ -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
ughost/cli.py ADDED
@@ -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
ughost/extract.py ADDED
@@ -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
ughost/optimize.py ADDED
@@ -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)
ughost/parts.py ADDED
@@ -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
ughost/utils.py ADDED
@@ -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,11 @@
1
+ ughost/__init__.py,sha256=KrSDVN4ReaJcZhGNQlZ9kg2s-OOcpE3K62VFQIP0ssI,978
2
+ ughost/cli.py,sha256=RaJzpo083YwWzmDftlVWmaSrhrRyIqmJMfqvtbtIv8Q,2497
3
+ ughost/extract.py,sha256=mMSBRgCc7m0_sU6BW_Tse9y8qXWgz2vjXNTq3z5S5GY,1693
4
+ ughost/optimize.py,sha256=_wv0iBGcDvZwW44T-PhvwZEXoDrQfomxYra7xZgAIxU,964
5
+ ughost/parts.py,sha256=xURqlPCOhhwe2NwSi51J0TnuWucKI0kzTaMc16Zm9GE,4452
6
+ ughost/utils.py,sha256=WhgV7_jpBUq8liy5eU2TK-IvYy_ddjzjfCOLzmqKroo,1759
7
+ ughost-1.0.0.dist-info/METADATA,sha256=O-sF1vEec-eQ2w1qFn6AhWLYCKSh4H01If6O7HOUVEs,871
8
+ ughost-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ ughost-1.0.0.dist-info/entry_points.txt,sha256=vkkyU1gm3piJUkQmliNkz0R38mNuvuQcYa6z3jMO4rk,42
10
+ ughost-1.0.0.dist-info/top_level.txt,sha256=FodITgpiFEkTILdICCFDfFIf2DZBK8gPhh8cWnITS00,7
11
+ ughost-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ghost = ughost.cli:main
@@ -0,0 +1 @@
1
+ ughost