convert-stream 2.4.4__tar.gz → 2.5__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.
- {convert_stream-2.4.4 → convert_stream-2.5}/PKG-INFO +3 -3
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/__init__.py +10 -15
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/_version.py +2 -2
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/doc_stream.py +19 -22
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/image/img_object.py +26 -15
- convert_stream-2.5/convert_stream/mod_types/__init__.py +11 -0
- convert_stream-2.5/convert_stream/mod_types/enums.py +37 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/mod_types/modules.py +6 -6
- convert_stream-2.5/convert_stream/pdf/__init__.py +12 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/pdf/pdf_document.py +14 -13
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/pdf/pdf_page.py +12 -9
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/table_files.py +1 -2
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/text/__init__.py +1 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream.egg-info/PKG-INFO +3 -3
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream.egg-info/SOURCES.txt +0 -3
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream.egg-info/requires.txt +2 -2
- {convert_stream-2.4.4 → convert_stream-2.5}/pyproject.toml +3 -3
- convert_stream-2.4.4/convert_stream/mod_types/__init__.py +0 -13
- convert_stream-2.4.4/convert_stream/mod_types/enums.py +0 -65
- convert_stream-2.4.4/convert_stream/mod_types/table_types.py +0 -19
- convert_stream-2.4.4/convert_stream/pdf/__init__.py +0 -14
- convert_stream-2.4.4/convert_stream/pdf/image_to_pdf.py +0 -349
- convert_stream-2.4.4/convert_stream/pdf/pdf_to_image.py +0 -223
- {convert_stream-2.4.4 → convert_stream-2.5}/README.md +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/__main__.py +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/image/__init__.py +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/text/find_text.py +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream/text/terminal.py +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream.egg-info/dependency_links.txt +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/convert_stream.egg-info/top_level.txt +0 -0
- {convert_stream-2.4.4 → convert_stream-2.5}/setup.cfg +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: convert_stream
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5
|
|
4
4
|
Summary: Manipulação de documentos PDF e Imagens. Converter, Ler, rotacionar exportar etc.
|
|
5
5
|
Author: Bruno Chaves
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: soup_files>=1.4
|
|
8
|
+
Requires-Dist: soup_files>=1.4.2
|
|
9
9
|
Requires-Dist: PyMuPDF
|
|
10
10
|
Requires-Dist: scipy
|
|
11
11
|
Requires-Dist: reportlab
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pandas
|
|
|
15
15
|
Requires-Dist: Pillow>=11.3.0
|
|
16
16
|
Requires-Dist: opencv-python
|
|
17
17
|
Requires-Dist: requests>=2.32.4
|
|
18
|
-
Requires-Dist: sheet_stream
|
|
18
|
+
Requires-Dist: sheet_stream>=0.1.3
|
|
19
19
|
|
|
20
20
|
# Configuração instalação:
|
|
21
21
|
pip3 install -r requirements.txt
|
|
@@ -8,11 +8,17 @@ from ._version import (
|
|
|
8
8
|
__module_name__, __author__, __license__, __modify_date__, __version__
|
|
9
9
|
)
|
|
10
10
|
from .mod_types import (
|
|
11
|
-
LibPDF, LibImage, LibPdfToImage,
|
|
11
|
+
LibPDF, LibImage, LibPdfToImage, LibImageToPdf, RotationAngle,
|
|
12
12
|
)
|
|
13
|
-
from .image import ImageObject, CollectionImages
|
|
14
|
-
from .pdf import
|
|
15
|
-
|
|
13
|
+
from .image import ImageObject, CollectionImages, ProcessImageScanner
|
|
14
|
+
from .pdf import (
|
|
15
|
+
DocumentPdf, PageDocumentPdf, CollectionPagePdf, SearchableTextPdf
|
|
16
|
+
)
|
|
17
|
+
from .convert import (
|
|
18
|
+
ConvertPdfToImages, ConvertImageToPdf, ABCConvertPdf, ABCConvertImageToPdf,
|
|
19
|
+
DEFAULT_LIB_PDF_TO_IMG, DEFAULT_LIB_IMAGE_TO_PDF, LibPdfToImage, LibImageToPdf,
|
|
20
|
+
)
|
|
21
|
+
|
|
16
22
|
from .doc_stream import PdfStream, SplitPdf
|
|
17
23
|
from .table_files import PdfFinder
|
|
18
24
|
from .text import print_title, print_line, FindStrings
|
|
@@ -71,14 +77,3 @@ def read_image(img: Union[str, File, bytes, BytesIO]) -> PdfStream:
|
|
|
71
77
|
raise ValueError()
|
|
72
78
|
return _pdf_stream
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
__all__ = [
|
|
76
|
-
'__modify_date__', '__author__', '__license__', '__module_name__', '__version__',
|
|
77
|
-
'PdfStream', 'DocumentPdf', 'PageDocumentPdf', 'CollectionPagePdf', 'CollectionImages',
|
|
78
|
-
'ImageObject', 'File', 'Directory', 'LibraryDocs', 'InputFiles', 'SearchableTextPdf',
|
|
79
|
-
'PdfFinder', 'SplitPdf', 'LibImageToPdf', 'RotationAngle', 'ConvertImageToPdf',
|
|
80
|
-
'ConvertPdfToImages', 'LibPDF', 'LibImage', 'LibPdfToImage',
|
|
81
|
-
'read_pdf', 'read_files_pdf', 'read_image', 'DictTextTable',
|
|
82
|
-
'print_title', 'print_line', 'FindStrings',
|
|
83
|
-
]
|
|
84
|
-
|
|
@@ -4,21 +4,18 @@
|
|
|
4
4
|
Módulo para trabalhar com pdfs e imagens
|
|
5
5
|
"""
|
|
6
6
|
from __future__ import annotations
|
|
7
|
-
from convert_stream.mod_types.modules import (
|
|
8
|
-
DEFAULT_LIB_PDF, DEFAULT_LIB_IMAGE, DEFAULT_LIB_PDF_TO_IMG, DEFAULT_LIB_IMAGE_TO_PDF,
|
|
9
|
-
)
|
|
10
7
|
from convert_stream.mod_types.enums import LibPDF, LibImage, LibPdfToImage
|
|
11
8
|
from convert_stream.pdf.pdf_page import PageDocumentPdf
|
|
12
9
|
from convert_stream.image.img_object import ImageObject, CollectionImages
|
|
13
10
|
from convert_stream.pdf.pdf_document import DocumentPdf, CollectionPagePdf
|
|
14
|
-
from convert_stream.
|
|
15
|
-
from convert_stream.
|
|
11
|
+
from convert_stream.convert.image_to_pdf import LibImageToPdf, ConvertImageToPdf
|
|
12
|
+
from convert_stream.convert.pdf_to_image import ConvertPdfToImages
|
|
16
13
|
from soup_files import File, Directory, ProgressBarAdapter
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
class ImageStream(CollectionImages):
|
|
20
17
|
|
|
21
|
-
def __init__(self, images: list[ImageObject]) -> None:
|
|
18
|
+
def __init__(self, images: list[ImageObject] = None) -> None:
|
|
22
19
|
super().__init__(images)
|
|
23
20
|
|
|
24
21
|
def add_image_bytes(self, bt: bytes) -> None:
|
|
@@ -39,7 +36,7 @@ class PdfStream(object):
|
|
|
39
36
|
) -> None:
|
|
40
37
|
self.dpi = dpi
|
|
41
38
|
self.progress: ProgressBarAdapter = pbar
|
|
42
|
-
self.collection_pages: CollectionPagePdf = CollectionPagePdf(
|
|
39
|
+
self.collection_pages: CollectionPagePdf = CollectionPagePdf()
|
|
43
40
|
self.collection_images: CollectionImages = CollectionImages([], lib_img=lib_img)
|
|
44
41
|
self.collection_images.set_pbar(self.progress)
|
|
45
42
|
self.collection_pages.set_pbar(self.progress)
|
|
@@ -132,25 +129,25 @@ class PdfStream(object):
|
|
|
132
129
|
return _doc
|
|
133
130
|
|
|
134
131
|
def thresold(self, *, land_scape: bool = False, a4: bool = False) -> DocumentPdf:
|
|
135
|
-
if self.collection_images.length == 0:
|
|
136
|
-
images: list[ImageObject] = []
|
|
137
|
-
else:
|
|
138
|
-
images: list[ImageObject] = self.collection_images.copy()
|
|
139
132
|
|
|
140
133
|
if not self.collection_pages.is_empty:
|
|
141
134
|
# Converter as páginas PDF em imagens.
|
|
142
135
|
conv_pdf_to_img: ConvertPdfToImages = self.convert_pdf_to_images
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
136
|
+
_images: list[ImageObject] = conv_pdf_to_img.to_images(dpi=self.dpi, lib_image=self.lib_img)
|
|
137
|
+
_collection = CollectionImages(_images)
|
|
138
|
+
else:
|
|
139
|
+
_collection = CollectionImages()
|
|
140
|
+
|
|
141
|
+
if self.collection_images.length > 0:
|
|
142
|
+
_collection.add_images(self.collection_images)
|
|
143
|
+
if _collection.length == 0:
|
|
147
144
|
raise ValueError('Adicione imagens para prosseguir!')
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
conv_img_to_pdf = ConvertImageToPdf(
|
|
145
|
+
|
|
146
|
+
for num, i in enumerate(_collection):
|
|
147
|
+
if land_scape:
|
|
148
|
+
_collection[num].set_landscape()
|
|
149
|
+
_collection[num].set_threshold_black(max_value=255)
|
|
150
|
+
conv_img_to_pdf = ConvertImageToPdf(_collection, lib_img_to_pdf=self.lib_img_to_pdf)
|
|
154
151
|
return conv_img_to_pdf.to_document(a4=a4)
|
|
155
152
|
|
|
156
153
|
def to_files_images(
|
|
@@ -163,7 +160,7 @@ class PdfStream(object):
|
|
|
163
160
|
raise ValueError('Adicione imagens e/ou documentos para prosseguir!')
|
|
164
161
|
self.progress.start()
|
|
165
162
|
|
|
166
|
-
image_stream = ImageStream(
|
|
163
|
+
image_stream = ImageStream()
|
|
167
164
|
image_stream.set_pbar(self.progress)
|
|
168
165
|
if not self.collection_images.is_empty:
|
|
169
166
|
image_stream.add_images(self.collection_images)
|
|
@@ -4,17 +4,16 @@
|
|
|
4
4
|
Módulo para trabalhar com imagens
|
|
5
5
|
"""
|
|
6
6
|
from __future__ import annotations
|
|
7
|
-
|
|
8
7
|
import os.path
|
|
9
8
|
from typing import Union
|
|
10
9
|
from abc import ABC, abstractmethod
|
|
11
|
-
from soup_files import File, Directory, InputFiles, LibraryDocs, ProgressBarAdapter
|
|
10
|
+
from soup_files import File, Directory, InputFiles, LibraryDocs, ProgressBarAdapter, TextProgress
|
|
12
11
|
from io import BytesIO
|
|
13
12
|
import numpy as np
|
|
14
13
|
from scipy.ndimage import uniform_filter
|
|
15
14
|
from sheet_stream.type_utils import get_hash_from_bytes, MetaDataFile, MetaDataItem
|
|
16
|
-
from convert_stream.mod_types.enums import
|
|
17
|
-
from
|
|
15
|
+
from convert_stream.mod_types.enums import RotationAngle
|
|
16
|
+
from sheet_stream import ListItems
|
|
18
17
|
from convert_stream.mod_types.modules import (
|
|
19
18
|
MOD_IMG_PIL, ModuleImage, DEFAULT_LIB_IMAGE,
|
|
20
19
|
cv2, MatLike, Image, ImageFilter, LibImage
|
|
@@ -500,16 +499,28 @@ class ImageObject(object):
|
|
|
500
499
|
|
|
501
500
|
class CollectionImages(ListItems):
|
|
502
501
|
|
|
503
|
-
def __init__(self, images: list[ImageObject], *, lib_img: LibImage = LibImage.OPENCV) -> None:
|
|
502
|
+
def __init__(self, images: list[ImageObject] = None, *, lib_img: LibImage = LibImage.OPENCV) -> None:
|
|
504
503
|
"""
|
|
505
504
|
Gerir uma lista de Imagens
|
|
506
505
|
:type images: list[ImageObject]
|
|
507
506
|
"""
|
|
508
507
|
super().__init__(images)
|
|
509
508
|
self.lib_img: LibImage = lib_img
|
|
510
|
-
self.
|
|
509
|
+
self.__pbar: TextProgress = TextProgress()
|
|
510
|
+
self.set_list_type(ImageObject)
|
|
511
511
|
self.__count: int = 0
|
|
512
512
|
|
|
513
|
+
@property
|
|
514
|
+
def pbar(self) -> ProgressBarAdapter:
|
|
515
|
+
return self.__pbar.pbar
|
|
516
|
+
|
|
517
|
+
@pbar.setter
|
|
518
|
+
def pbar(self, pbar: ProgressBarAdapter) -> None:
|
|
519
|
+
self.__pbar.pbar = pbar
|
|
520
|
+
|
|
521
|
+
def set_list_type(self, cls_type=object):
|
|
522
|
+
super().set_list_type(ImageObject)
|
|
523
|
+
|
|
513
524
|
def clear(self) -> None:
|
|
514
525
|
super().clear()
|
|
515
526
|
self.__count = 0
|
|
@@ -533,12 +544,12 @@ class CollectionImages(ListItems):
|
|
|
533
544
|
|
|
534
545
|
def add_files_image(self, files: list[File]) -> None:
|
|
535
546
|
self.pbar.start()
|
|
536
|
-
|
|
547
|
+
self.__pbar.total = len(files)
|
|
548
|
+
self.__pbar.start = 0
|
|
549
|
+
self.__pbar.text = 'Adicionando imagem'
|
|
550
|
+
|
|
537
551
|
for num, f in enumerate(files):
|
|
538
|
-
self.
|
|
539
|
-
((num+1) / max_num) * 100,
|
|
540
|
-
f'Adicionando imagem: [{num+1} de {max_num}]'
|
|
541
|
-
)
|
|
552
|
+
self.__pbar.set_update()
|
|
542
553
|
self.append(ImageObject.create_from_file(f, lib_image=self.lib_img))
|
|
543
554
|
self.pbar.stop()
|
|
544
555
|
|
|
@@ -559,7 +570,7 @@ class CollectionImages(ListItems):
|
|
|
559
570
|
max_num = self.length
|
|
560
571
|
img: ImageObject
|
|
561
572
|
for n, img in enumerate(self):
|
|
562
|
-
if img.metadata.file_path is not None:
|
|
573
|
+
if (img.metadata.file_path is not None) and (img.metadata.file_path != ''):
|
|
563
574
|
filename = f'{os.path.basename(img.metadata.file_path)}-{n}.png'
|
|
564
575
|
else:
|
|
565
576
|
filename = f'{img.metadata.name}-{n}.png'
|
|
@@ -611,9 +622,9 @@ class ProcessImageScanner(object):
|
|
|
611
622
|
|
|
612
623
|
def select_doc(self) -> ImageObject | None:
|
|
613
624
|
"""
|
|
614
|
-
|
|
615
|
-
(texto ou folha digitalizada) em
|
|
616
|
-
Retorna um novo ImageObject contendo apenas o recorte
|
|
625
|
+
Deteta e recorta automaticamente a área do documento principal
|
|
626
|
+
(texto ou folha digitalizada) em imagem.
|
|
627
|
+
Retorna um novo ImageObject contendo apenas o recorte detetado.
|
|
617
628
|
"""
|
|
618
629
|
# Converte para matriz OpenCV
|
|
619
630
|
img_cv: np.ndarray = cv2.imdecode(self.image.to_numpy(), cv2.IMREAD_COLOR)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LibPdfToImage(Enum):
|
|
6
|
+
"""Enumerar as bibliotecas externas que convertem PDF em imagem"""
|
|
7
|
+
PDF_TO_IMG_FITZ = 'fitz'
|
|
8
|
+
NOT_IMPLEMENTED = 'null'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LibImageToPdf(Enum):
|
|
12
|
+
|
|
13
|
+
IMAGE_TO_PDF_FITZ = 'fitz'
|
|
14
|
+
IMAGE_TO_PDF_CANVAS = 'canvas'
|
|
15
|
+
IMAGE_TO_PDF_PIL = 'pil'
|
|
16
|
+
NOT_IMPLEMENTED = 'null'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LibPDF(Enum):
|
|
20
|
+
|
|
21
|
+
PYPDF = 'pypdf'
|
|
22
|
+
FITZ = 'fitz'
|
|
23
|
+
NOT_IMPLEMENTED = 'null'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LibImage(Enum):
|
|
27
|
+
|
|
28
|
+
PIL = 'pil'
|
|
29
|
+
OPENCV = 'opencv'
|
|
30
|
+
NOT_IMPLEMENTED = 'null'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RotationAngle(Enum):
|
|
34
|
+
|
|
35
|
+
ROTATION_90 = 90
|
|
36
|
+
ROTATION_180 = 180
|
|
37
|
+
ROTATION_270 = 270
|
|
@@ -29,15 +29,15 @@ try:
|
|
|
29
29
|
from cv2.typing import MatLike
|
|
30
30
|
MOD_IMG_OPENCV = True
|
|
31
31
|
except ImportError:
|
|
32
|
-
MatLike =
|
|
32
|
+
MatLike = object
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
35
|
from PIL import Image
|
|
36
36
|
from PIL import ImageOps, ImageFilter
|
|
37
37
|
MOD_IMG_PIL = True
|
|
38
38
|
except ImportError:
|
|
39
|
-
Image =
|
|
40
|
-
Image.Image =
|
|
39
|
+
Image = object
|
|
40
|
+
Image.Image = object
|
|
41
41
|
|
|
42
42
|
if MOD_IMG_OPENCV and MOD_IMG_PIL:
|
|
43
43
|
ModuleImage: TypeAlias = Union[MatLike, Image.Image]
|
|
@@ -50,7 +50,7 @@ elif MOD_IMG_PIL:
|
|
|
50
50
|
DEFAULT_LIB_IMAGE = LibImage.PIL
|
|
51
51
|
else:
|
|
52
52
|
DEFAULT_LIB_IMAGE = LibImage.NOT_IMPLEMENTED
|
|
53
|
-
ModuleImage =
|
|
53
|
+
ModuleImage: TypeAlias = Union[MatLike, Image]
|
|
54
54
|
|
|
55
55
|
#=================================================================#
|
|
56
56
|
# Módulos para PDF fitz e pypdf
|
|
@@ -90,8 +90,8 @@ elif MOD_PYPDF:
|
|
|
90
90
|
ModDocPdf = Union[PdfWriter]
|
|
91
91
|
DEFAULT_LIB_PDF = LibPDF.PYPDF
|
|
92
92
|
else:
|
|
93
|
-
ModPagePdf =
|
|
94
|
-
ModDocPdf =
|
|
93
|
+
ModPagePdf = Union[PageObject, fitz.Page]
|
|
94
|
+
ModDocPdf = Union[fitz.Document, PdfWriter]
|
|
95
95
|
DEFAULT_LIB_PDF = LibPDF.NOT_IMPLEMENTED
|
|
96
96
|
|
|
97
97
|
#=================================================================#
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from .pdf_page import PageDocumentPdf
|
|
3
|
+
from .pdf_document import (
|
|
4
|
+
DocumentPdf, CollectionPagePdf, SearchableTextPdf, ABCDocument,
|
|
5
|
+
MOD_FITZ, MOD_PYPDF, ModDocPdf,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'DocumentPdf', 'CollectionPagePdf', 'PageDocumentPdf',
|
|
10
|
+
'SearchableTextPdf', 'ModDocPdf', 'ABCDocument', 'MOD_PYPDF', 'MOD_FITZ',
|
|
11
|
+
]
|
|
12
|
+
|
|
@@ -9,14 +9,10 @@ from io import BytesIO
|
|
|
9
9
|
from typing import Union
|
|
10
10
|
import pandas as pd
|
|
11
11
|
from soup_files import File, Directory, LibraryDocs, InputFiles, ProgressBarAdapter, JsonConvert
|
|
12
|
-
from sheet_stream
|
|
13
|
-
|
|
12
|
+
from sheet_stream import (
|
|
13
|
+
ListItems, HeadCell, HeadValues, ColumnsTable, TableDocuments, ListColumnBody, ListString,
|
|
14
|
+
MetaDataItem, MetaDataFile, get_hash_from_bytes,
|
|
14
15
|
)
|
|
15
|
-
|
|
16
|
-
from sheet_stream.type_utils import (
|
|
17
|
-
ListItems, HeadCell, HeadValues, ColumnsTable, TableDocuments, ListColumnBody, ListString
|
|
18
|
-
)
|
|
19
|
-
from convert_stream.mod_types.table_types import DictTextTable, ColumnBody
|
|
20
16
|
from convert_stream.mod_types.enums import LibPDF
|
|
21
17
|
from convert_stream.mod_types.modules import (
|
|
22
18
|
DEFAULT_LIB_PDF, ModDocPdf, PdfWriter, PdfReader, fitz, MOD_FITZ, MOD_PYPDF
|
|
@@ -70,7 +66,7 @@ class SearchableTextPdf(object):
|
|
|
70
66
|
"""
|
|
71
67
|
def __init__(self, silent: bool = False):
|
|
72
68
|
self.silent = silent
|
|
73
|
-
self.elements:
|
|
69
|
+
self.elements: TableDocuments = TableDocuments.create_void_dict()
|
|
74
70
|
|
|
75
71
|
def __repr__(self):
|
|
76
72
|
return f'SearchableTextPdf: {self.elements}'
|
|
@@ -221,9 +217,9 @@ class ImplementDocumentFitz(ABCDocument):
|
|
|
221
217
|
"""
|
|
222
218
|
Implementação usando a biblioteca fitz.
|
|
223
219
|
"""
|
|
224
|
-
def __init__(self, mod_doc_pdf:
|
|
220
|
+
def __init__(self, mod_doc_pdf: fitz.Document):
|
|
225
221
|
super().__init__(mod_doc_pdf)
|
|
226
|
-
self.mod_doc_pdf:
|
|
222
|
+
self.mod_doc_pdf: fitz.Document = mod_doc_pdf
|
|
227
223
|
self.lib_pdf: LibPDF = LibPDF.FITZ
|
|
228
224
|
|
|
229
225
|
def lenght(self) -> int:
|
|
@@ -592,11 +588,12 @@ class DocumentPdf(object):
|
|
|
592
588
|
list_data: list[TableDocuments] = []
|
|
593
589
|
pages: list[PageDocumentPdf] = self.to_pages()
|
|
594
590
|
|
|
591
|
+
_mp: TableDocuments
|
|
595
592
|
for page_pdf in pages:
|
|
596
|
-
_mp
|
|
593
|
+
_mp = self._create_map(page_pdf, separator=separator)
|
|
597
594
|
list_data.append(_mp)
|
|
598
595
|
if len(list_data) == 0:
|
|
599
|
-
return
|
|
596
|
+
return TableDocuments.create_void_dict()
|
|
600
597
|
return concat_table(list_data)
|
|
601
598
|
|
|
602
599
|
def to_data(self, separator: str = '\n') -> pd.DataFrame:
|
|
@@ -712,9 +709,10 @@ class CollectionPagePdf(ListItems):
|
|
|
712
709
|
"""
|
|
713
710
|
Gerir uma coleção de páginas PDF.
|
|
714
711
|
"""
|
|
715
|
-
def __init__(self, pages: list[PageDocumentPdf]) -> None:
|
|
712
|
+
def __init__(self, pages: list[PageDocumentPdf] = None) -> None:
|
|
716
713
|
super().__init__(pages)
|
|
717
714
|
self.pbar: ProgressBarAdapter = ProgressBarAdapter()
|
|
715
|
+
self.set_list_type(PageDocumentPdf)
|
|
718
716
|
|
|
719
717
|
@property
|
|
720
718
|
def name(self) -> str | None:
|
|
@@ -722,6 +720,9 @@ class CollectionPagePdf(ListItems):
|
|
|
722
720
|
return None
|
|
723
721
|
return self[0].__hash__()
|
|
724
722
|
|
|
723
|
+
def set_list_type(self, cls_type=PageDocumentPdf):
|
|
724
|
+
super().set_list_type(PageDocumentPdf)
|
|
725
|
+
|
|
725
726
|
def set_pbar(self, p: ProgressBarAdapter):
|
|
726
727
|
self.pbar = p
|
|
727
728
|
|
|
@@ -6,11 +6,8 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
import pandas as pd
|
|
9
|
-
|
|
9
|
+
from sheet_stream import ListString, TableDocuments
|
|
10
10
|
from convert_stream.mod_types.enums import LibPDF
|
|
11
|
-
from convert_stream.mod_types.table_types import (
|
|
12
|
-
DictTextTable, ListString
|
|
13
|
-
)
|
|
14
11
|
from convert_stream.mod_types.modules import (
|
|
15
12
|
ModPagePdf, PageObject, fitz
|
|
16
13
|
)
|
|
@@ -230,10 +227,16 @@ class PageDocumentPdf(object):
|
|
|
230
227
|
return ListString([])
|
|
231
228
|
return ListString(txt.split(separator))
|
|
232
229
|
|
|
233
|
-
def to_dict(self, separator: str = '\n') ->
|
|
230
|
+
def to_dict(self, separator: str = '\n') -> TableDocuments:
|
|
231
|
+
"""
|
|
232
|
+
Converte o texto da página em tabela/dicionário
|
|
233
|
+
"""
|
|
234
234
|
_values = self.to_list(separator)
|
|
235
|
-
return
|
|
236
|
-
_values,
|
|
235
|
+
return TableDocuments.create_from_values(
|
|
236
|
+
_values,
|
|
237
|
+
page_num=f'{self.number_page}',
|
|
238
|
+
file_type='.pdf',
|
|
239
|
+
file_path=self.metadata.file_path
|
|
237
240
|
)
|
|
238
241
|
|
|
239
242
|
def to_map(self, separator: str = '\n') -> pd.DataFrame:
|
|
@@ -246,9 +249,9 @@ class PageDocumentPdf(object):
|
|
|
246
249
|
fd = FindText(self.to_string(), separator=separator)
|
|
247
250
|
return fd.find(text, iqual=iqual, case=case)
|
|
248
251
|
|
|
249
|
-
def find_all(self, text: str, *, separator: str = '\n', iqual: bool = False, case: bool = True) ->
|
|
252
|
+
def find_all(self, text: str, *, separator: str = '\n', iqual: bool = False, case: bool = True) -> ListString:
|
|
250
253
|
fd = FindText(self.to_string(), separator=separator)
|
|
251
|
-
return fd.find_all(text, iqual=iqual, case=case)
|
|
254
|
+
return ListString(fd.find_all(text, iqual=iqual, case=case))
|
|
252
255
|
|
|
253
256
|
def get_width(self) -> float:
|
|
254
257
|
return self.implement_page_pdf.get_width()
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
from io import BytesIO
|
|
5
5
|
from soup_files import File, Directory, InputFiles, LibraryDocs
|
|
6
|
-
|
|
7
6
|
from convert_stream import PageDocumentPdf
|
|
8
7
|
from convert_stream.text.find_text import FindText
|
|
9
8
|
from convert_stream.pdf.pdf_document import SearchableTextPdf, DocumentPdf
|
|
10
|
-
from convert_stream.mod_types.table_types import ColumnsTable
|
|
11
9
|
from convert_stream.image import ImageObject
|
|
10
|
+
from sheet_stream import ColumnsTable, TableDocuments
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class PdfFinder(object):
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: convert_stream
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5
|
|
4
4
|
Summary: Manipulação de documentos PDF e Imagens. Converter, Ler, rotacionar exportar etc.
|
|
5
5
|
Author: Bruno Chaves
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: soup_files>=1.4
|
|
8
|
+
Requires-Dist: soup_files>=1.4.2
|
|
9
9
|
Requires-Dist: PyMuPDF
|
|
10
10
|
Requires-Dist: scipy
|
|
11
11
|
Requires-Dist: reportlab
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pandas
|
|
|
15
15
|
Requires-Dist: Pillow>=11.3.0
|
|
16
16
|
Requires-Dist: opencv-python
|
|
17
17
|
Requires-Dist: requests>=2.32.4
|
|
18
|
-
Requires-Dist: sheet_stream
|
|
18
|
+
Requires-Dist: sheet_stream>=0.1.3
|
|
19
19
|
|
|
20
20
|
# Configuração instalação:
|
|
21
21
|
pip3 install -r requirements.txt
|
|
@@ -15,12 +15,9 @@ convert_stream/image/img_object.py
|
|
|
15
15
|
convert_stream/mod_types/__init__.py
|
|
16
16
|
convert_stream/mod_types/enums.py
|
|
17
17
|
convert_stream/mod_types/modules.py
|
|
18
|
-
convert_stream/mod_types/table_types.py
|
|
19
18
|
convert_stream/pdf/__init__.py
|
|
20
|
-
convert_stream/pdf/image_to_pdf.py
|
|
21
19
|
convert_stream/pdf/pdf_document.py
|
|
22
20
|
convert_stream/pdf/pdf_page.py
|
|
23
|
-
convert_stream/pdf/pdf_to_image.py
|
|
24
21
|
convert_stream/text/__init__.py
|
|
25
22
|
convert_stream/text/find_text.py
|
|
26
23
|
convert_stream/text/terminal.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "convert_stream"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.5"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Bruno Chaves"},
|
|
10
10
|
]
|
|
@@ -14,7 +14,7 @@ readme = "README.md"
|
|
|
14
14
|
requires-python = ">=3.11"
|
|
15
15
|
|
|
16
16
|
dependencies = [
|
|
17
|
-
"soup_files>=1.4",
|
|
17
|
+
"soup_files>=1.4.2",
|
|
18
18
|
"PyMuPDF",
|
|
19
19
|
"scipy",
|
|
20
20
|
"reportlab",
|
|
@@ -24,7 +24,7 @@ dependencies = [
|
|
|
24
24
|
"Pillow>=11.3.0",
|
|
25
25
|
"opencv-python",
|
|
26
26
|
"requests>=2.32.4",
|
|
27
|
-
"sheet_stream",
|
|
27
|
+
"sheet_stream>=0.1.3",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[tool.setuptools]
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
from .enums import (
|
|
5
|
-
RotationAngle, ColumnsTable, LibImage, LibPdfToImage, LibPDF, LibImageToPdf
|
|
6
|
-
)
|
|
7
|
-
from .table_types import DictTextTable
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
'DictTextTable', 'RotationAngle', 'LibPDF',
|
|
12
|
-
'LibImageToPdf', 'LibPdfToImage', 'LibImage',
|
|
13
|
-
]
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
from enum import Enum
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class ColumnsTable(Enum):
|
|
6
|
-
"""
|
|
7
|
-
Classe enum para padronizar os nomes das tabelas geradas por este módulo.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
NUM_LINE = 'LINHA'
|
|
11
|
-
NUM_PAGE = 'PÁGINA'
|
|
12
|
-
TEXT = 'TEXTO'
|
|
13
|
-
FILE_PATH = 'ARQUIVO'
|
|
14
|
-
FILE_NAME = 'NOME_ARQUIVO'
|
|
15
|
-
DIR = 'PASTA'
|
|
16
|
-
FILETYPE = 'TIPO_ARQUIVO'
|
|
17
|
-
KEY = 'KEY'
|
|
18
|
-
|
|
19
|
-
@classmethod
|
|
20
|
-
def to_list(cls) -> list[str]:
|
|
21
|
-
return [
|
|
22
|
-
cls.NUM_LINE.value,
|
|
23
|
-
cls.NUM_PAGE.value,
|
|
24
|
-
cls.TEXT.value,
|
|
25
|
-
cls.FILE_PATH.value,
|
|
26
|
-
cls.FILE_NAME.value,
|
|
27
|
-
cls.FILETYPE.value,
|
|
28
|
-
cls.DIR.value,
|
|
29
|
-
cls.KEY.value,
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class LibPdfToImage(Enum):
|
|
34
|
-
"""Enumerar as bibliotecas externas que convertem PDF em imagem"""
|
|
35
|
-
PDF_TO_IMG_FITZ = 'fitz'
|
|
36
|
-
NOT_IMPLEMENTED = 'null'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class LibImageToPdf(Enum):
|
|
40
|
-
|
|
41
|
-
IMAGE_TO_PDF_FITZ = 'fitz'
|
|
42
|
-
IMAGE_TO_PDF_CANVAS = 'canvas'
|
|
43
|
-
IMAGE_TO_PDF_PIL = 'pil'
|
|
44
|
-
NOT_IMPLEMENTED = 'null'
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class LibPDF(Enum):
|
|
48
|
-
|
|
49
|
-
PYPDF = 'pypdf'
|
|
50
|
-
FITZ = 'fitz'
|
|
51
|
-
NOT_IMPLEMENTED = 'null'
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class LibImage(Enum):
|
|
55
|
-
|
|
56
|
-
PIL = 'pil'
|
|
57
|
-
OPENCV = 'opencv'
|
|
58
|
-
NOT_IMPLEMENTED = 'null'
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class RotationAngle(Enum):
|
|
62
|
-
|
|
63
|
-
ROTATION_90 = 90
|
|
64
|
-
ROTATION_180 = 180
|
|
65
|
-
ROTATION_270 = 270
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
from sheet_stream import (
|
|
5
|
-
ColumnsTable, ListString, ListItems, ListColumnBody, ArrayString, TableDocuments, TableRow, TableTextKeyWord
|
|
6
|
-
)
|
|
7
|
-
from sheet_stream.type_utils import HeadCell
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ColumnBody(ListColumnBody):
|
|
11
|
-
|
|
12
|
-
def __init__(self, col_name: HeadCell | str, col_body: list[str]):
|
|
13
|
-
super().__init__(col_name, col_body)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class DictTextTable(TableDocuments):
|
|
17
|
-
|
|
18
|
-
def __init__(self, body_list: list[ListColumnBody]):
|
|
19
|
-
super().__init__(body_list)
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from .pdf_page import PageDocumentPdf
|
|
4
|
-
from .pdf_document import (
|
|
5
|
-
DocumentPdf, CollectionPagePdf
|
|
6
|
-
)
|
|
7
|
-
from .pdf_to_image import ConvertPdfToImages
|
|
8
|
-
from .image_to_pdf import ConvertImageToPdf
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
'ConvertPdfToImages', 'ConvertImageToPdf', 'DocumentPdf',
|
|
12
|
-
'CollectionPagePdf', 'PageDocumentPdf',
|
|
13
|
-
]
|
|
14
|
-
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Módulo para trabalhar com imagens
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
from io import BytesIO
|
|
9
|
-
from soup_files import File, Directory, ProgressBarAdapter
|
|
10
|
-
from abc import ABC, abstractmethod
|
|
11
|
-
from convert_stream.mod_types.enums import LibImageToPdf
|
|
12
|
-
from convert_stream.mod_types.modules import (
|
|
13
|
-
MOD_CANVAS, DEFAULT_LIB_IMAGE_TO_PDF, canvas, letter, ImageReader
|
|
14
|
-
)
|
|
15
|
-
from convert_stream.pdf import DocumentPdf
|
|
16
|
-
from convert_stream.pdf.pdf_page import fitz
|
|
17
|
-
from convert_stream.image.img_object import MOD_IMG_PIL, Image, ImageObject, CollectionImages
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ABCConvertImageToPdf(ABC):
|
|
21
|
-
|
|
22
|
-
def __init__(self, collection_images: CollectionImages):
|
|
23
|
-
self.collection_images: CollectionImages = collection_images
|
|
24
|
-
self.pbar: ProgressBarAdapter = ProgressBarAdapter()
|
|
25
|
-
|
|
26
|
-
@abstractmethod
|
|
27
|
-
def set_pbar(self, p: ProgressBarAdapter):
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
@abstractmethod
|
|
31
|
-
def to_document(self, land_scape: bool = True, a4: bool = False) -> DocumentPdf:
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
@abstractmethod
|
|
35
|
-
def to_file_pdf(self, output_file: File, *, land_scape: bool = True, a4: bool = False) -> None:
|
|
36
|
-
pass
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class ImplementImageToPdfCanvas(ABCConvertImageToPdf):
|
|
40
|
-
|
|
41
|
-
def __init__(self, collection_images: CollectionImages):
|
|
42
|
-
super().__init__(collection_images)
|
|
43
|
-
|
|
44
|
-
def set_pbar(self, p: ProgressBarAdapter):
|
|
45
|
-
self.pbar = p
|
|
46
|
-
self.collection_images.set_pbar(p)
|
|
47
|
-
|
|
48
|
-
def to_document(self, land_scape: bool = True, a4: bool = False) -> DocumentPdf:
|
|
49
|
-
if not MOD_CANVAS:
|
|
50
|
-
raise RuntimeError("A biblioteca 'reportlab' não está disponível.")
|
|
51
|
-
|
|
52
|
-
if self.collection_images.is_empty:
|
|
53
|
-
raise ValueError("A lista de imagens não pode estar vazia.")
|
|
54
|
-
|
|
55
|
-
buffer = BytesIO()
|
|
56
|
-
max_num: int = self.collection_images.length
|
|
57
|
-
self.pbar.start()
|
|
58
|
-
if land_scape:
|
|
59
|
-
self.collection_images.set_land_scape()
|
|
60
|
-
|
|
61
|
-
# Lógica para determinar o tamanho inicial da página
|
|
62
|
-
first_img_obj = self.collection_images[0]
|
|
63
|
-
first_img_width, first_img_height = first_img_obj.get_dimensions()
|
|
64
|
-
|
|
65
|
-
if a4:
|
|
66
|
-
# Se for A4, o tamanho da página é fixo desde o início
|
|
67
|
-
c = canvas.Canvas(buffer, pagesize=letter)
|
|
68
|
-
else:
|
|
69
|
-
# Se não for A4, o canvas é inicializado com as dimensões da primeira imagem
|
|
70
|
-
c = canvas.Canvas(buffer, pagesize=(first_img_width, first_img_height))
|
|
71
|
-
|
|
72
|
-
# Processa a primeira imagem
|
|
73
|
-
self.pbar.update(
|
|
74
|
-
(1 / max_num) * 100,
|
|
75
|
-
f'Adicionando imagem ao Documento [1 de {max_num}]'
|
|
76
|
-
)
|
|
77
|
-
img_bytes: BytesIO = first_img_obj.to_bytes()
|
|
78
|
-
img_bytes.seek(0)
|
|
79
|
-
img_reader = ImageReader(img_bytes)
|
|
80
|
-
|
|
81
|
-
if a4:
|
|
82
|
-
# Lógica de redimensionamento e centralização para A4
|
|
83
|
-
page_width, page_height = letter
|
|
84
|
-
scale_factor = min(page_width / first_img_width, page_height / first_img_height)
|
|
85
|
-
scaled_width = first_img_width * scale_factor
|
|
86
|
-
scaled_height = first_img_height * scale_factor
|
|
87
|
-
x_pos = (page_width - scaled_width) / 2
|
|
88
|
-
y_pos = (page_height - scaled_height) / 2
|
|
89
|
-
c.drawImage(img_reader, x_pos, y_pos, width=scaled_width, height=scaled_height)
|
|
90
|
-
else:
|
|
91
|
-
# Desenha a imagem preenchendo a página que já tem o tamanho correto
|
|
92
|
-
c.drawImage(img_reader, 0, 0, width=first_img_width, height=first_img_height)
|
|
93
|
-
|
|
94
|
-
# Processa as imagens restantes
|
|
95
|
-
for num, img_obj in enumerate(self.collection_images[1:], 1):
|
|
96
|
-
|
|
97
|
-
self.pbar.update(
|
|
98
|
-
((num + 1) / max_num) * 100,
|
|
99
|
-
f'Adicionando imagem ao Documento [{num + 1} de {max_num}]'
|
|
100
|
-
)
|
|
101
|
-
img_bytes = img_obj.to_bytes()
|
|
102
|
-
img_bytes.seek(0)
|
|
103
|
-
img_reader = ImageReader(img_bytes)
|
|
104
|
-
img_width, img_height = img_obj.get_dimensions()
|
|
105
|
-
# Cria nova página
|
|
106
|
-
c.showPage()
|
|
107
|
-
|
|
108
|
-
if a4:
|
|
109
|
-
# O tamanho da página é fixo, apenas desenha a imagem centralizada e redimensionada
|
|
110
|
-
page_width, page_height = letter
|
|
111
|
-
scale_factor = min(page_width / img_width, page_height / img_height)
|
|
112
|
-
scaled_width = img_width * scale_factor
|
|
113
|
-
scaled_height = img_height * scale_factor
|
|
114
|
-
x_pos = (page_width - scaled_width) / 2
|
|
115
|
-
y_pos = (page_height - scaled_height) / 2
|
|
116
|
-
c.drawImage(img_reader, x_pos, y_pos, width=scaled_width, height=scaled_height)
|
|
117
|
-
else:
|
|
118
|
-
# Ajusta o tamanho da nova página para as dimensões da imagem atual
|
|
119
|
-
c.setPageSize((img_width, img_height))
|
|
120
|
-
c.drawImage(img_reader, 0, 0, width=img_width, height=img_height)
|
|
121
|
-
|
|
122
|
-
c.save()
|
|
123
|
-
buffer.seek(0)
|
|
124
|
-
self.pbar.stop()
|
|
125
|
-
return DocumentPdf.create_from_bytes(buffer)
|
|
126
|
-
|
|
127
|
-
def to_file_pdf(self, output_file: File, *, land_scape: bool = True, a4: bool = False) -> None:
|
|
128
|
-
self.to_document(land_scape=land_scape, a4=a4).to_file(output_file)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
class ImplementImageToPdfPil(ABCConvertImageToPdf):
|
|
132
|
-
"""
|
|
133
|
-
Classe para converter uma lista de ImageObject em um DocumentPdf
|
|
134
|
-
usando a biblioteca Pillow (PIL).
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
def __init__(self, collection_images: CollectionImages):
|
|
138
|
-
super().__init__(collection_images)
|
|
139
|
-
|
|
140
|
-
def set_pbar(self, p: ProgressBarAdapter):
|
|
141
|
-
self.pbar = p
|
|
142
|
-
self.collection_images.set_pbar(p)
|
|
143
|
-
|
|
144
|
-
def to_document(self, land_scape: bool = True, a4: bool = False) -> DocumentPdf:
|
|
145
|
-
if not MOD_IMG_PIL:
|
|
146
|
-
raise RuntimeError("A biblioteca 'Pillow' não está disponível.")
|
|
147
|
-
|
|
148
|
-
if self.collection_images:
|
|
149
|
-
raise ValueError("A lista de imagens não pode estar vazia.")
|
|
150
|
-
|
|
151
|
-
if land_scape:
|
|
152
|
-
self.collection_images.set_land_scape()
|
|
153
|
-
|
|
154
|
-
pil_images_to_save = []
|
|
155
|
-
if a4:
|
|
156
|
-
# Obtém as dimensões da página A4 em píxels
|
|
157
|
-
# Usando uma resolução de 100 DPI para consistência
|
|
158
|
-
page_width, page_height = letter # Padrão do reportlab
|
|
159
|
-
page_size = (int(page_width), int(page_height))
|
|
160
|
-
|
|
161
|
-
self.pbar.start()
|
|
162
|
-
max_num = self.collection_images.length
|
|
163
|
-
for num, img_obj in enumerate(self.collection_images):
|
|
164
|
-
self.pbar.update(
|
|
165
|
-
((num + 1) / max_num) * 100,
|
|
166
|
-
f'Redimensionando e ajustando imagem para A4 [{num + 1} de {max_num}]'
|
|
167
|
-
)
|
|
168
|
-
original_pil_image = img_obj.to_pil()
|
|
169
|
-
img_width, img_height = original_pil_image.size
|
|
170
|
-
|
|
171
|
-
# Calcula o fator de escala para ajustar a imagem à página, mantendo a proporção
|
|
172
|
-
scale_factor = min(page_size[0] / img_width, page_size[1] / img_height)
|
|
173
|
-
scaled_width = int(img_width * scale_factor)
|
|
174
|
-
scaled_height = int(img_height * scale_factor)
|
|
175
|
-
|
|
176
|
-
# Redimensiona a imagem
|
|
177
|
-
resized_image = original_pil_image.resize(
|
|
178
|
-
(scaled_width, scaled_height), Image.Resampling.LANCZOS
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
# Criar imagem de fundo branca com o tamanho A4
|
|
182
|
-
new_image = Image.new('RGB', page_size, 'white')
|
|
183
|
-
|
|
184
|
-
# Centraliza a imagem redimensionada na nova imagem
|
|
185
|
-
x_pos = (page_size[0] - scaled_width) // 2
|
|
186
|
-
y_pos = (page_size[1] - scaled_height) // 2
|
|
187
|
-
new_image.paste(resized_image, (x_pos, y_pos))
|
|
188
|
-
|
|
189
|
-
pil_images_to_save.append(new_image)
|
|
190
|
-
self.pbar.stop()
|
|
191
|
-
else:
|
|
192
|
-
# Para a4 = False, não precisa redimensionar. Pillow ajusta o tamanho da página.
|
|
193
|
-
pil_images_to_save = [img.to_pil() for img in self.collection_images]
|
|
194
|
-
|
|
195
|
-
buffer = BytesIO()
|
|
196
|
-
try:
|
|
197
|
-
first_image_pil = pil_images_to_save[0]
|
|
198
|
-
other_images_pil = pil_images_to_save[1:]
|
|
199
|
-
|
|
200
|
-
first_image_pil.save(
|
|
201
|
-
buffer,
|
|
202
|
-
'PDF',
|
|
203
|
-
resolution=140.0,
|
|
204
|
-
save_all=True,
|
|
205
|
-
append_images=other_images_pil
|
|
206
|
-
)
|
|
207
|
-
except Exception as e:
|
|
208
|
-
raise RuntimeError(f"Erro ao criar o PDF com Pillow: {e}") from e
|
|
209
|
-
|
|
210
|
-
buffer.seek(0)
|
|
211
|
-
return DocumentPdf.create_from_bytes(buffer)
|
|
212
|
-
|
|
213
|
-
def to_file_pdf(self, output_file: File, *, land_scape: bool = True, a4: bool = False) -> None:
|
|
214
|
-
self.to_document(land_scape=land_scape, a4=a4).to_file(output_file)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class ImplementImageToPdfFitz(ABCConvertImageToPdf):
|
|
218
|
-
"""
|
|
219
|
-
Classe para converter uma lista de ImageObject em um DocumentPdf
|
|
220
|
-
usando a biblioteca PyMuPDF (fitz).
|
|
221
|
-
"""
|
|
222
|
-
|
|
223
|
-
def __init__(self, collection_images: CollectionImages):
|
|
224
|
-
super().__init__(collection_images)
|
|
225
|
-
|
|
226
|
-
def set_pbar(self, p: ProgressBarAdapter):
|
|
227
|
-
self.pbar = p
|
|
228
|
-
self.collection_images.set_pbar(p)
|
|
229
|
-
|
|
230
|
-
def to_document(self, land_scape: bool = True, a4: bool = False) -> DocumentPdf:
|
|
231
|
-
max_num: int = self.collection_images.length
|
|
232
|
-
if max_num == 0:
|
|
233
|
-
raise ValueError('Adicione imagens para prosseguir')
|
|
234
|
-
self.pbar.start()
|
|
235
|
-
self.pbar.update(0, f'Convertendo {max_num} imagens em Documento PDF')
|
|
236
|
-
buffer = BytesIO()
|
|
237
|
-
doc = fitz.open() # Cria um novo documento PDF vazio
|
|
238
|
-
|
|
239
|
-
for num, img_obj in enumerate(self.collection_images):
|
|
240
|
-
self.pbar.update(
|
|
241
|
-
((num + 1) / max_num) * 100,
|
|
242
|
-
f'Convertendo: [{num + 1} de {max_num}]'
|
|
243
|
-
)
|
|
244
|
-
if land_scape:
|
|
245
|
-
img_obj.set_landscape()
|
|
246
|
-
|
|
247
|
-
# Converte a imagem para bytes
|
|
248
|
-
img_bytes_obj = img_obj.to_bytes()
|
|
249
|
-
# Obtém as dimensões da imagem e da página
|
|
250
|
-
img_width, img_height = img_obj.get_dimensions()
|
|
251
|
-
|
|
252
|
-
# Adiciona uma nova página ao documento
|
|
253
|
-
if a4:
|
|
254
|
-
page = doc.new_page()
|
|
255
|
-
else:
|
|
256
|
-
page = doc.new_page(width=img_width, height=img_height)
|
|
257
|
-
|
|
258
|
-
# Cria um retângulo que preenche a página
|
|
259
|
-
rect = page.rect
|
|
260
|
-
page_width, page_height = rect.width, rect.height
|
|
261
|
-
|
|
262
|
-
# Calcula o fator de escala para ajustar a imagem à página
|
|
263
|
-
scale_factor_w = page_width / img_width
|
|
264
|
-
scale_factor_h = page_height / img_height
|
|
265
|
-
scale_factor = min(scale_factor_w, scale_factor_h)
|
|
266
|
-
|
|
267
|
-
# Ajusta o retângulo de inserção para centralizar a imagem
|
|
268
|
-
scaled_width = img_width * scale_factor
|
|
269
|
-
scaled_height = img_height * scale_factor
|
|
270
|
-
x0 = (page_width - scaled_width) / 2
|
|
271
|
-
y0 = (page_height - scaled_height) / 2
|
|
272
|
-
|
|
273
|
-
# Insere a imagem no retângulo calculado
|
|
274
|
-
page.insert_image(
|
|
275
|
-
fitz.Rect(x0, y0, x0 + scaled_width, y0 + scaled_height),
|
|
276
|
-
stream=img_bytes_obj
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
# Salva o documento no buffer de memória
|
|
280
|
-
doc.save(buffer)
|
|
281
|
-
doc.close()
|
|
282
|
-
buffer.seek(0)
|
|
283
|
-
self.pbar.update(100, 'Conversão concluída')
|
|
284
|
-
print()
|
|
285
|
-
self.pbar.stop()
|
|
286
|
-
return DocumentPdf.create_from_bytes(buffer)
|
|
287
|
-
|
|
288
|
-
def to_file_pdf(self, output_file: File, *, land_scape: bool = True, a4: bool = False) -> None:
|
|
289
|
-
self.to_document(land_scape=land_scape).to_file(output_file)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
class ConvertImageToPdf(object):
|
|
293
|
-
"""
|
|
294
|
-
Classe com padrão ADAPTER para converter Imagens em documento(s) PDF.
|
|
295
|
-
"""
|
|
296
|
-
|
|
297
|
-
def __init__(
|
|
298
|
-
self,
|
|
299
|
-
collection_images: CollectionImages, *,
|
|
300
|
-
lib_img_to_pdf: LibImageToPdf = LibImageToPdf.IMAGE_TO_PDF_FITZ
|
|
301
|
-
):
|
|
302
|
-
if lib_img_to_pdf == LibImageToPdf.IMAGE_TO_PDF_CANVAS:
|
|
303
|
-
self.convert: ABCConvertImageToPdf = ImplementImageToPdfCanvas(collection_images)
|
|
304
|
-
elif lib_img_to_pdf == LibImageToPdf.IMAGE_TO_PDF_PIL:
|
|
305
|
-
self.convert: ABCConvertImageToPdf = ImplementImageToPdfPil(collection_images)
|
|
306
|
-
elif lib_img_to_pdf == LibImageToPdf.IMAGE_TO_PDF_FITZ:
|
|
307
|
-
self.convert: ABCConvertImageToPdf = ImplementImageToPdfFitz(collection_images)
|
|
308
|
-
else:
|
|
309
|
-
raise NotImplementedError()
|
|
310
|
-
|
|
311
|
-
self._count: int = 0
|
|
312
|
-
|
|
313
|
-
@property
|
|
314
|
-
def is_null(self) -> bool:
|
|
315
|
-
return self.convert.collection_images.is_empty
|
|
316
|
-
|
|
317
|
-
@property
|
|
318
|
-
def pbar(self) -> ProgressBarAdapter:
|
|
319
|
-
return self.convert.pbar
|
|
320
|
-
|
|
321
|
-
def set_pbar(self, p: ProgressBarAdapter):
|
|
322
|
-
self.convert.set_pbar(p)
|
|
323
|
-
|
|
324
|
-
def add_file(self, file: File) -> None:
|
|
325
|
-
self.convert.collection_images.add_file_image(file)
|
|
326
|
-
|
|
327
|
-
def add_files(self, files: list[File]) -> None:
|
|
328
|
-
self.convert.collection_images.add_files_image(files)
|
|
329
|
-
|
|
330
|
-
def add_image(self, image: ImageObject) -> None:
|
|
331
|
-
self.convert.collection_images.add_image(image)
|
|
332
|
-
|
|
333
|
-
def add_images(self, images: list[ImageObject]) -> None:
|
|
334
|
-
self.convert.collection_images.add_images(images)
|
|
335
|
-
|
|
336
|
-
def add_directory(self, src_dir: Directory, max_files: int = 4000) -> None:
|
|
337
|
-
self.convert.collection_images.add_directory_images(src_dir, max_files=max_files)
|
|
338
|
-
|
|
339
|
-
def to_document(self, land_scape: bool = True, *, a4: bool = False) -> DocumentPdf:
|
|
340
|
-
return self.convert.to_document(land_scape, a4=a4)
|
|
341
|
-
|
|
342
|
-
def to_file_pdf(self, output_file: File, *, land_scape: bool = True, a4: bool = False) -> None:
|
|
343
|
-
self.convert.to_file_pdf(output_file, land_scape=land_scape, a4=a4)
|
|
344
|
-
|
|
345
|
-
def set_gausian_blur(self, sigma: float = 0.8) -> None:
|
|
346
|
-
self.convert.collection_images.set_gausian_blur(sigma)
|
|
347
|
-
|
|
348
|
-
def set_land_scape(self):
|
|
349
|
-
self.convert.collection_images.set_land_scape()
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
"""
|
|
4
|
-
Módulo para trabalhar com imagens
|
|
5
|
-
"""
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
from abc import ABC, abstractmethod
|
|
8
|
-
|
|
9
|
-
from sheet_stream.type_utils import MetaDataFile
|
|
10
|
-
|
|
11
|
-
from convert_stream import CollectionImages
|
|
12
|
-
from soup_files import Directory, ProgressBarAdapter, CreatePbar
|
|
13
|
-
from convert_stream.mod_types.enums import LibPdfToImage
|
|
14
|
-
from convert_stream.mod_types.modules import DEFAULT_LIB_PDF_TO_IMG, DEFAULT_LIB_IMAGE, LibPDF
|
|
15
|
-
from convert_stream.image.img_object import ImageObject, LibImage
|
|
16
|
-
from convert_stream.pdf.pdf_document import DocumentPdf
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
try:
|
|
20
|
-
import fitz
|
|
21
|
-
MOD_FITZ = True
|
|
22
|
-
except ImportError:
|
|
23
|
-
try:
|
|
24
|
-
import pymupdf as fitz
|
|
25
|
-
from pymupdf import Page, Pixmap
|
|
26
|
-
MOD_FITZ = True
|
|
27
|
-
except ImportError:
|
|
28
|
-
fitz = object
|
|
29
|
-
fitz.Page = object
|
|
30
|
-
fitz.TextPage = object
|
|
31
|
-
Page = object
|
|
32
|
-
Pixmap = object
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class ABCConvertPdf(ABC):
|
|
36
|
-
|
|
37
|
-
def __init__(self, document: DocumentPdf):
|
|
38
|
-
self.document: DocumentPdf = document
|
|
39
|
-
self.lib_pdf_to_image: LibPdfToImage = DEFAULT_LIB_PDF_TO_IMG
|
|
40
|
-
self.pbar: ProgressBarAdapter = CreatePbar().get()
|
|
41
|
-
|
|
42
|
-
def set_pbar(self, pbar: ProgressBarAdapter):
|
|
43
|
-
self.pbar = pbar
|
|
44
|
-
|
|
45
|
-
@abstractmethod
|
|
46
|
-
def to_images(
|
|
47
|
-
self, *,
|
|
48
|
-
dpi: int = 150,
|
|
49
|
-
lib_image: LibImage = DEFAULT_LIB_IMAGE,
|
|
50
|
-
) -> list[ImageObject]:
|
|
51
|
-
"""
|
|
52
|
-
Converte as páginas PDF do documento em lista de objetos imagem ImageObject
|
|
53
|
-
|
|
54
|
-
:param dpi: DPI do documento, resolução da renderização.
|
|
55
|
-
:param lib_image: Biblioteca para manipular imagens PIL/OpenCv
|
|
56
|
-
"""
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
|
-
@abstractmethod
|
|
60
|
-
def to_files_image(
|
|
61
|
-
self,
|
|
62
|
-
output_dir: Directory, *,
|
|
63
|
-
replace: bool = False,
|
|
64
|
-
gaussian_filter: bool = False,
|
|
65
|
-
dpi: int = 300,
|
|
66
|
-
lib_image: LibImage = DEFAULT_LIB_IMAGE,
|
|
67
|
-
) -> None:
|
|
68
|
-
"""
|
|
69
|
-
Converte todas as páginas do documento em objeto de imagem e salva no disco
|
|
70
|
-
em formato de imagem PNG.
|
|
71
|
-
"""
|
|
72
|
-
pass
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class ImplementConvertPdfFitz(ABCConvertPdf):
|
|
76
|
-
|
|
77
|
-
def __init__(self, document: DocumentPdf):
|
|
78
|
-
super().__init__(document)
|
|
79
|
-
self.lib_pdf_to_image: LibPdfToImage = LibPdfToImage.PDF_TO_IMG_FITZ
|
|
80
|
-
|
|
81
|
-
def __get_pages(self) -> list[Page]:
|
|
82
|
-
pages = self.document.to_pages()
|
|
83
|
-
fitz_pages = []
|
|
84
|
-
for p in pages:
|
|
85
|
-
fitz_pages.append(p.implement_page_pdf.mod_page)
|
|
86
|
-
return fitz_pages
|
|
87
|
-
|
|
88
|
-
def to_images(
|
|
89
|
-
self, *,
|
|
90
|
-
dpi: int = 200,
|
|
91
|
-
lib_image: LibImage = LibImage.PIL
|
|
92
|
-
) -> list[ImageObject]:
|
|
93
|
-
"""
|
|
94
|
-
Converte um Documento em lista de objetos ImageObject.
|
|
95
|
-
"""
|
|
96
|
-
images: list[ImageObject] = []
|
|
97
|
-
_metadata: MetaDataFile = self.document.metadata
|
|
98
|
-
|
|
99
|
-
pages_fitz: list[Page] = self.__get_pages()
|
|
100
|
-
_count = len(pages_fitz)
|
|
101
|
-
for n, pg in enumerate(pages_fitz):
|
|
102
|
-
self.pbar.update(
|
|
103
|
-
((n+1)/_count) * 100,
|
|
104
|
-
f'[Documento para Imagens]: página {n+1}/{_count}'
|
|
105
|
-
)
|
|
106
|
-
pix: Pixmap = pg.get_pixmap(dpi=dpi)
|
|
107
|
-
img = ImageObject.create_from_bytes(
|
|
108
|
-
pix.tobytes('png', jpg_quality=100),
|
|
109
|
-
lib_image=LibImage.PIL,
|
|
110
|
-
)
|
|
111
|
-
img.metadata.name = _metadata.name
|
|
112
|
-
img.metadata.file_path = _metadata.file_path
|
|
113
|
-
images.append(img)
|
|
114
|
-
return images
|
|
115
|
-
|
|
116
|
-
def to_files_image(
|
|
117
|
-
self,
|
|
118
|
-
output_dir: Directory, *,
|
|
119
|
-
replace: bool = False,
|
|
120
|
-
gaussian_filter: bool = False,
|
|
121
|
-
dpi: int = 300,
|
|
122
|
-
lib_image: LibImage = LibImage.PIL
|
|
123
|
-
) -> None:
|
|
124
|
-
pages_fitz: list[Page] = self.__get_pages()
|
|
125
|
-
_count = len(pages_fitz)
|
|
126
|
-
for n, pg in enumerate(pages_fitz):
|
|
127
|
-
self.pbar.update(
|
|
128
|
-
((n + 1) / _count) * 100,
|
|
129
|
-
f'[Documento para arquivos de imagens]: {n + 1}/{_count}'
|
|
130
|
-
)
|
|
131
|
-
out_file = output_dir.join_file(f'pag-{n+1}.png')
|
|
132
|
-
if not replace:
|
|
133
|
-
if out_file.exists():
|
|
134
|
-
self.pbar.update_text(f'Pulando: {out_file.basename()}')
|
|
135
|
-
continue
|
|
136
|
-
pix: Pixmap = pg.get_pixmap(dpi=dpi)
|
|
137
|
-
img = ImageObject.create_from_bytes(
|
|
138
|
-
pix.tobytes('png', jpg_quality=100),
|
|
139
|
-
lib_image=LibImage.PIL,
|
|
140
|
-
)
|
|
141
|
-
img.to_file(out_file)
|
|
142
|
-
print()
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class ConvertPdfToImages(object):
|
|
146
|
-
|
|
147
|
-
def __init__(self, mod_conv_to_img: ABCConvertPdf | DocumentPdf):
|
|
148
|
-
if isinstance(mod_conv_to_img, ABCConvertPdf):
|
|
149
|
-
self.mod_convert_to_image: ABCConvertPdf = mod_conv_to_img
|
|
150
|
-
elif isinstance(mod_conv_to_img, ImplementConvertPdfFitz):
|
|
151
|
-
self.mod_convert_to_image: ABCConvertPdf = mod_conv_to_img
|
|
152
|
-
elif isinstance(mod_conv_to_img, DocumentPdf):
|
|
153
|
-
self.mod_convert_to_image = ImplementConvertPdfFitz(mod_conv_to_img)
|
|
154
|
-
else:
|
|
155
|
-
raise NotImplementedError()
|
|
156
|
-
|
|
157
|
-
def set_pbar(self, pbar: ProgressBarAdapter):
|
|
158
|
-
self.mod_convert_to_image.set_pbar(pbar)
|
|
159
|
-
|
|
160
|
-
def to_images(
|
|
161
|
-
self, *,
|
|
162
|
-
dpi: int = 300,
|
|
163
|
-
lib_image: LibImage = LibImage.PIL,
|
|
164
|
-
) -> list[ImageObject]:
|
|
165
|
-
return self.mod_convert_to_image.to_images(dpi=dpi, lib_image=lib_image)
|
|
166
|
-
|
|
167
|
-
def to_files_image(
|
|
168
|
-
self,
|
|
169
|
-
output_dir: Directory, *,
|
|
170
|
-
replace: bool = False,
|
|
171
|
-
gaussian_filter: bool = False,
|
|
172
|
-
dpi: int = 300,
|
|
173
|
-
lib_image: LibImage = LibImage.PIL,
|
|
174
|
-
) -> None:
|
|
175
|
-
self.mod_convert_to_image.to_files_image(
|
|
176
|
-
dpi=dpi,
|
|
177
|
-
lib_image=lib_image,
|
|
178
|
-
replace=replace,
|
|
179
|
-
output_dir=output_dir,
|
|
180
|
-
gaussian_filter=gaussian_filter
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
def inner_images(self) -> CollectionImages:
|
|
184
|
-
"""
|
|
185
|
-
Extrair imagens embutidas nas páginas PDF.
|
|
186
|
-
"""
|
|
187
|
-
# doc = Document(stream=page_bytes, filetype="pdf")
|
|
188
|
-
collection_imgs: CollectionImages = CollectionImages([])
|
|
189
|
-
tmp_doc: fitz.Document
|
|
190
|
-
if self.mod_convert_to_image.document.lib_pdf == LibPDF.FITZ:
|
|
191
|
-
tmp_doc: fitz.Document = self.mod_convert_to_image.document.get_real_document()
|
|
192
|
-
elif self.mod_convert_to_image.document.lib_pdf == LibPDF.PYPDF:
|
|
193
|
-
tmp_doc = fitz.Document(
|
|
194
|
-
stream=self.mod_convert_to_image.document.to_bytes().getvalue(), filetype="pdf"
|
|
195
|
-
)
|
|
196
|
-
else:
|
|
197
|
-
raise NotImplementedError()
|
|
198
|
-
|
|
199
|
-
page: fitz.Page
|
|
200
|
-
for num, page in enumerate(tmp_doc):
|
|
201
|
-
# Extrair imagens embutidas na página
|
|
202
|
-
images_list: list = page.get_images(full=True)
|
|
203
|
-
for img in images_list:
|
|
204
|
-
try:
|
|
205
|
-
# Referência do objeto da imagem
|
|
206
|
-
xref = img[0]
|
|
207
|
-
# Extrair imagem
|
|
208
|
-
base_image = tmp_doc.extract_image(xref)
|
|
209
|
-
# Bytes da imagem
|
|
210
|
-
image_bytes = base_image["image"]
|
|
211
|
-
# Extensão (jpg, png, etc.)
|
|
212
|
-
#image_ext = base_image["ext"]
|
|
213
|
-
except Exception as e:
|
|
214
|
-
print(e)
|
|
215
|
-
else:
|
|
216
|
-
img = ImageObject(image_bytes)
|
|
217
|
-
collection_imgs.append(img)
|
|
218
|
-
return collection_imgs
|
|
219
|
-
|
|
220
|
-
@classmethod
|
|
221
|
-
def create(cls, doc: DocumentPdf) -> ConvertPdfToImages:
|
|
222
|
-
_mod = ImplementConvertPdfFitz(doc)
|
|
223
|
-
return cls(_mod)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|