convert-stream 2.3.2__tar.gz → 2.3.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.
Files changed (39) hide show
  1. {convert_stream-2.3.2 → convert_stream-2.3.5}/PKG-INFO +5 -3
  2. {convert_stream-2.3.2 → convert_stream-2.3.5}/README.md +4 -2
  3. convert_stream-2.3.5/convert_stream/__init__.py +84 -0
  4. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/doc_stream.py +1 -1
  5. convert_stream-2.3.5/convert_stream/image/__init__.py +7 -0
  6. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/image/img_object.py +87 -22
  7. convert_stream-2.3.5/convert_stream/mod_types/__init__.py +100 -0
  8. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/mod_types/table_types.py +95 -100
  9. convert_stream-2.3.5/convert_stream/pdf/__init__.py +14 -0
  10. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/pdf/pdf_document.py +113 -118
  11. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/pdf/pdf_page.py +3 -3
  12. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/sheets/parse.py +1 -1
  13. convert_stream-2.3.5/convert_stream/table_files.py +153 -0
  14. convert_stream-2.3.5/convert_stream/text/__init__.py +11 -0
  15. convert_stream-2.3.2/convert_stream/text/string.py → convert_stream-2.3.5/convert_stream/text/find_text.py +1 -1
  16. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream.egg-info/PKG-INFO +5 -3
  17. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream.egg-info/SOURCES.txt +1 -1
  18. {convert_stream-2.3.2 → convert_stream-2.3.5}/pyproject.toml +1 -1
  19. convert_stream-2.3.2/convert_stream/__init__.py +0 -101
  20. convert_stream-2.3.2/convert_stream/image/__init__.py +0 -7
  21. convert_stream-2.3.2/convert_stream/mod_types/__init__.py +0 -32
  22. convert_stream-2.3.2/convert_stream/pdf/__init__.py +0 -15
  23. convert_stream-2.3.2/convert_stream/table_files.py +0 -142
  24. convert_stream-2.3.2/convert_stream/text/__init__.py +0 -7
  25. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/__main__.py +0 -0
  26. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/_version.py +0 -0
  27. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/mod_types/enums.py +0 -0
  28. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/mod_types/modules.py +0 -0
  29. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/pdf/image_to_pdf.py +0 -0
  30. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/pdf/pdf_to_image.py +0 -0
  31. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/sheets/__init__.py +0 -0
  32. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/sheets/load.py +0 -0
  33. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/sheets/save.py +0 -0
  34. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/text/date.py +0 -0
  35. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream/text/terminal.py +0 -0
  36. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream.egg-info/dependency_links.txt +0 -0
  37. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream.egg-info/requires.txt +0 -0
  38. {convert_stream-2.3.2 → convert_stream-2.3.5}/convert_stream.egg-info/top_level.txt +0 -0
  39. {convert_stream-2.3.2 → convert_stream-2.3.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: convert_stream
3
- Version: 2.3.2
3
+ Version: 2.3.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
@@ -21,8 +21,9 @@ pip3 install -r requirements.txt
21
21
 
22
22
 
23
23
  # Uso dividir páginas de um arquivo PDF:
24
- >>> from file import File, Directory
24
+ >>> from soup_files import File, Directory
25
25
  >>> from pdflib import PdfStream
26
+
26
27
  # instâncie o caminho do arquivo PDF e o diretório para exportar os dados.
27
28
  file_pdf = File('path/to/file.pdf')
28
29
  output_dir = Directory('path/to/save')
@@ -32,8 +33,9 @@ pip3 install -r requirements.txt
32
33
  pdf_stream.to_files_pdf(output_dir) # isso irá converter cada página em um arquivo PDF
33
34
 
34
35
  # Uso para unir vários arquivos PDF.
35
- >>> from file import File, Directory
36
+ >>> from soup_files import File, Directory
36
37
  >>> from pdflib import PdfStream
38
+
37
39
  # instâncie o caminho dos arquivos PDF que deseja unir.
38
40
  f = File('path/to/file.pdf')
39
41
  f2 = File('path/to/file2.pdf)
@@ -3,8 +3,9 @@ pip3 install -r requirements.txt
3
3
 
4
4
 
5
5
  # Uso dividir páginas de um arquivo PDF:
6
- >>> from file import File, Directory
6
+ >>> from soup_files import File, Directory
7
7
  >>> from pdflib import PdfStream
8
+
8
9
  # instâncie o caminho do arquivo PDF e o diretório para exportar os dados.
9
10
  file_pdf = File('path/to/file.pdf')
10
11
  output_dir = Directory('path/to/save')
@@ -14,8 +15,9 @@ pip3 install -r requirements.txt
14
15
  pdf_stream.to_files_pdf(output_dir) # isso irá converter cada página em um arquivo PDF
15
16
 
16
17
  # Uso para unir vários arquivos PDF.
17
- >>> from file import File, Directory
18
+ >>> from soup_files import File, Directory
18
19
  >>> from pdflib import PdfStream
20
+
19
21
  # instâncie o caminho dos arquivos PDF que deseja unir.
20
22
  f = File('path/to/file.pdf')
21
23
  f2 = File('path/to/file2.pdf)
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+ from io import BytesIO
4
+ from typing import Union
5
+ from soup_files import File, Directory, LibraryDocs, InputFiles
6
+
7
+ from ._version import (
8
+ __module_name__, __author__, __license__, __modify_date__, __version__
9
+ )
10
+ from .mod_types import (
11
+ MetaDataFile, MetaDataItem, create_metadata, ColumnsTable, ListString, ArrayString,
12
+ HeadCell, HeadValues, ColumnBody, TextTable, LibDate, LibPDF, LibImage, LibPdfToImage,
13
+ LibImageToPdf, create_dict_map_from_file, create_void_dict_map, RotationAngle
14
+ )
15
+ from .image import ImageObject, CollectionImages
16
+ from .pdf import DocumentPdf, PageDocumentPdf, CollectionPagePdf
17
+ from .pdf.pdf_document import SearchableTextPdf
18
+ from .doc_stream import PdfStream, SplitPdf
19
+ from .table_files import PdfFinder
20
+
21
+
22
+ def __create_stream() -> PdfStream:
23
+ st = PdfStream()
24
+ st.clear()
25
+ return st
26
+
27
+
28
+ def read_pdf(file: Union[str, File, bytes, BytesIO]) -> PdfStream:
29
+ _pdf_stream = __create_stream()
30
+
31
+ if isinstance(file, File):
32
+ _pdf_stream.add_file_pdf(file)
33
+ elif isinstance(file, str):
34
+ _pdf_stream.add_file_pdf(File(file))
35
+ elif isinstance(file, bytes):
36
+ _pdf_stream.add_document(DocumentPdf(BytesIO(file)))
37
+ elif isinstance(file, BytesIO):
38
+ _pdf_stream.add_document(DocumentPdf(file))
39
+ else:
40
+ raise ValueError()
41
+ return _pdf_stream
42
+
43
+
44
+ def read_files_pdf(files: Union[list[str], list[File]]) -> PdfStream:
45
+ _pdf_stream = __create_stream()
46
+ if len(files) == 0:
47
+ return _pdf_stream
48
+
49
+ if isinstance(files[0], str):
50
+ for f in files:
51
+ _pdf_stream.add_file_pdf(File(f))
52
+ elif isinstance(files[0], File):
53
+ _pdf_stream.add_files_pdf(files)
54
+ else:
55
+ raise ValueError()
56
+ return _pdf_stream
57
+
58
+
59
+ def read_image(img: Union[str, File, bytes, BytesIO]) -> PdfStream:
60
+ _pdf_stream = __create_stream()
61
+ if isinstance(img, str):
62
+ _pdf_stream.add_file_image(File(img))
63
+ elif isinstance(img, File):
64
+ _pdf_stream.add_image(ImageObject.create_from_file(img))
65
+ elif isinstance(img, bytes):
66
+ _pdf_stream.add_image(ImageObject.create_from_bytes(img))
67
+ elif isinstance(img, BytesIO):
68
+ img.seek(0)
69
+ _pdf_stream.add_image(ImageObject.create_from_bytes(img.getvalue()))
70
+ else:
71
+ raise ValueError()
72
+ return _pdf_stream
73
+
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', 'read_pdf', 'read_files_pdf', 'read_image',
80
+ 'MetaDataFile', 'MetaDataItem', 'create_metadata', 'ColumnsTable', 'ListString', 'ArrayString',
81
+ 'HeadCell', 'HeadValues', 'ColumnBody', 'TextTable', 'LibDate', 'LibPDF', 'LibImage', 'LibPdfToImage',
82
+ 'LibImageToPdf', 'create_dict_map_from_file', 'create_void_dict_map', 'RotationAngle'
83
+ ]
84
+
@@ -183,7 +183,7 @@ class PdfStream(object):
183
183
 
184
184
  class SplitPdf(object):
185
185
 
186
- def __init__(self, pages: list[PageDocumentPdf] = [], pbar: ProgressBarAdapter = ProgressBarAdapter()):
186
+ def __init__(self, pages: list[PageDocumentPdf], pbar: ProgressBarAdapter = ProgressBarAdapter()):
187
187
  self.pbar: ProgressBarAdapter = pbar
188
188
  self.__collection_pages: CollectionPagePdf = CollectionPagePdf(pages)
189
189
  self.__collection_pages.set_pbar(self.pbar)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from .img_object import ImageObject, CollectionImages, ProcessImageScanner
4
+ __all__ = ['ImageObject', 'CollectionImages', 'ProcessImageScanner']
5
+
6
+
7
+
@@ -10,12 +10,12 @@ from soup_files import File, Directory, InputFiles, LibraryDocs, ProgressBarAdap
10
10
  from io import BytesIO
11
11
  import numpy as np
12
12
  from scipy.ndimage import uniform_filter
13
- from convert_stream.mod_types import get_void_metadata, get_hash_from_bytes
13
+ from convert_stream.mod_types import get_hash_from_bytes, MetaDataFile, MetaDataItem, create_metadata
14
14
  from convert_stream.mod_types.enums import LibImage, RotationAngle
15
15
  from convert_stream.mod_types.table_types import ListItems
16
16
  from convert_stream.mod_types.modules import (
17
17
  MOD_IMG_PIL, ModuleImage, DEFAULT_LIB_IMAGE,
18
- cv2, MatLike, Image, ImageFilter
18
+ cv2, MatLike, Image, ImageFilter, LibImage
19
19
  )
20
20
 
21
21
 
@@ -26,11 +26,11 @@ class ABCImageObject(ABC):
26
26
  self.max_size: tuple[int, int] = (1980, 720)
27
27
  self.module_image: ModuleImage = module_image
28
28
  self.lib_image: LibImage = LibImage.OPENCV
29
- self.metadata: dict[str, str | None] = get_void_metadata()
29
+ self.metadata: MetaDataFile = MetaDataFile()
30
30
 
31
31
  @property
32
32
  def name(self) -> str | None:
33
- return self.metadata['name']
33
+ return self.metadata.name if not self.metadata.name.is_empty else None
34
34
 
35
35
  @abstractmethod
36
36
  def get_dimensions(self) -> tuple[int, int]:
@@ -205,11 +205,7 @@ class ImplementOpenCv(ABCImageObject):
205
205
  if img is None:
206
206
  raise ValueError(f"Não foi possível abrir a imagem: {f.absolute()}")
207
207
  im_obj = cls(img)
208
- im_obj.metadata['file_path'] = f.absolute()
209
- im_obj.metadata['md5'] = f.md5()
210
- im_obj.metadata['name'] = f.absolute()
211
- im_obj.metadata['size'] = f.size()
212
- im_obj.metadata['extension'] = f.extension()
208
+ im_obj.metadata = create_metadata(f)
213
209
  return im_obj
214
210
 
215
211
  @classmethod
@@ -221,10 +217,10 @@ class ImplementOpenCv(ABCImageObject):
221
217
  bytes_io = BytesIO(bt)
222
218
  name: str = get_hash_from_bytes(bytes_io)
223
219
  bytes_io.close()
224
- _obj = cls(img)
225
- _obj.metadata['md5'] = name
226
- _obj.metadata['name'] = name
227
- return _obj
220
+ im_obj = cls(img)
221
+ im_obj.metadata.name = MetaDataItem(name)
222
+ im_obj.metadata.md5 = MetaDataItem(name)
223
+ return im_obj
228
224
 
229
225
 
230
226
  class ImplementPIL(ABCImageObject):
@@ -352,13 +348,9 @@ class ImplementPIL(ABCImageObject):
352
348
 
353
349
  @classmethod
354
350
  def create_from_file(cls, f: File) -> ABCImageObject:
355
- img = Image.open(f.path)
351
+ img = Image.open(f.absolute())
356
352
  im_obj = cls(img)
357
- im_obj.metadata['file_path'] = f.absolute()
358
- im_obj.metadata['md5'] = f.md5()
359
- im_obj.metadata['name'] = f.absolute()
360
- im_obj.metadata['size'] = f.size()
361
- im_obj.metadata['extension'] = f.extension()
353
+ im_obj.metadata = create_metadata(f)
362
354
  return im_obj
363
355
 
364
356
  @classmethod
@@ -368,8 +360,8 @@ class ImplementPIL(ABCImageObject):
368
360
  name = get_hash_from_bytes(bytes_io)
369
361
  bytes_io.close()
370
362
  _obj = cls(img)
371
- _obj.metadata['md5'] = name
372
- _obj.metadata['name'] = name
363
+ _obj.metadata.name = MetaDataItem(name)
364
+ _obj.metadata.md5 = MetaDataItem(name)
373
365
  return _obj
374
366
 
375
367
 
@@ -419,9 +411,13 @@ class ImageObject(object):
419
411
  return self.img_adapter.name
420
412
 
421
413
  @property
422
- def metadata(self) -> dict[str, str | None]:
414
+ def metadata(self) -> MetaDataFile:
423
415
  return self.img_adapter.metadata
424
416
 
417
+ @metadata.setter
418
+ def metadata(self, metadata: MetaDataFile) -> None:
419
+ self.img_adapter.metadata = metadata
420
+
425
421
  def to_pil(self) -> Image.Image:
426
422
  """
427
423
  Converte o ImageObject para um objeto PIL.Image.
@@ -600,3 +596,72 @@ class CollectionImages(ListItems):
600
596
  def set_pbar(self, p: ProgressBarAdapter) -> None:
601
597
  self.pbar = p
602
598
 
599
+
600
+ class ProcessImageScanner(object):
601
+
602
+ def __init__(self, image: ImageObject):
603
+ self.image: ImageObject = image
604
+
605
+ def select_doc(self) -> ImageObject | None:
606
+ """
607
+ Detecta e recorta automaticamente a área do documento principal
608
+ (texto ou folha digitalizada) em uma imagem.
609
+ Retorna um novo ImageObject contendo apenas o recorte detectado.
610
+ """
611
+ # Converte para matriz OpenCV
612
+ img_cv: np.ndarray = cv2.imdecode(self.image.to_numpy(), cv2.IMREAD_COLOR)
613
+ if img_cv is None:
614
+ print(f'[NDARRAY] Falha na imagem: {self.image.metadata.name}')
615
+ return None
616
+
617
+ # Etapa 1: converter para tons de cinza
618
+ gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
619
+
620
+ # Etapa 2: aplicar blur para suavizar ruídos
621
+ gray_blur = cv2.GaussianBlur(gray, (5, 5), 0)
622
+
623
+ # Etapa 3: detecção de bordas
624
+ edges = cv2.Canny(gray_blur, 50, 150)
625
+
626
+ # Etapa 4: encontrar contornos
627
+ contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
628
+ if not contours:
629
+ print(f'[CONTOURS] Falha na imagem: {self.image.metadata.name}')
630
+ return None
631
+
632
+ # Etapa 5: selecionar o maior contorno plausível (maior área retangular)
633
+ contours = sorted(contours, key=cv2.contourArea, reverse=True)
634
+ doc_contour = None
635
+
636
+ for c in contours:
637
+ perimeter = cv2.arcLength(c, True)
638
+ approx = cv2.approxPolyDP(c, 0.02 * perimeter, True)
639
+ if len(approx) == 4:
640
+ doc_contour = approx
641
+ break
642
+
643
+ if doc_contour is None:
644
+ # Caso não encontre um quadrilátero, usa o maior contorno
645
+ doc_contour = contours[0]
646
+
647
+ # Etapa 6: criar bounding box e recortar
648
+ x, y, w, h = cv2.boundingRect(doc_contour)
649
+ cropped = img_cv[y:y + h, x:x + w]
650
+
651
+ # Evitar recortes muito pequenos (ruído)
652
+ if w < 50 or h < 50:
653
+ return None
654
+
655
+ # Etapa 7: converter o recorte em ImageObject
656
+ success, buffer = cv2.imencode(".png", cropped)
657
+ if not success:
658
+ print(f'Falha na imagem: {self.image.metadata.name}')
659
+ return None
660
+
661
+ bytes_io = BytesIO(buffer.tobytes())
662
+ new_image = ImageObject(bytes_io, lib_image=LibImage.OPENCV)
663
+ # Herdar metadados originais
664
+ new_image.metadata = self.image.metadata
665
+ new_image.metadata.name = MetaDataItem(f"{self.image.metadata.name}_crop")
666
+ return new_image
667
+
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ from __future__ import annotations
4
+ from hashlib import md5 as md5_hash
5
+ from io import BytesIO
6
+ from soup_files import File
7
+ from .enums import (
8
+ RotationAngle, ColumnsTable, LibImage, LibPdfToImage, LibPDF, LibImageToPdf, LibDate
9
+ )
10
+
11
+ from .table_types import (
12
+ TextTable, ListString, ArrayString, HeadCell, HeadValues,
13
+ ColumnBody, create_void_dict_map, create_dict_map_values, create_void_df_map,
14
+ create_dict_map_from_file, concat_maps, contains, get_text_from_file
15
+ )
16
+
17
+
18
+ def get_hash_from_bytes(bt: BytesIO | bytes) -> str:
19
+ if isinstance(bt, BytesIO):
20
+ return md5_hash(bt.getvalue()).hexdigest().upper()
21
+ elif isinstance(bt, bytes):
22
+ return md5_hash(bt).hexdigest().upper()
23
+ else:
24
+ raise TypeError(f"Unsupported type {type(bt)}")
25
+
26
+
27
+ class MetaDataItem(str):
28
+
29
+ def __init__(self, text: str = 'nan'):
30
+ super().__init__()
31
+ self.text: str = text
32
+
33
+ @property
34
+ def is_empty(self) -> bool:
35
+ return self.text == 'nan'
36
+
37
+
38
+ class MetaDataFile(object):
39
+
40
+ def __init__(
41
+ self, *,
42
+ file_path: MetaDataItem = MetaDataItem(),
43
+ dir_path: MetaDataItem = MetaDataItem(),
44
+ name: MetaDataItem = MetaDataItem(),
45
+ md5: MetaDataItem = MetaDataItem(),
46
+ size: MetaDataItem = MetaDataItem(),
47
+ extension: MetaDataItem = MetaDataItem(),
48
+ origin_src: MetaDataItem = MetaDataItem(),
49
+ ):
50
+ self.file_path: MetaDataItem = file_path
51
+ self.dir_path: MetaDataItem = dir_path
52
+ self.name: MetaDataItem = name
53
+ self.md5: MetaDataItem = md5
54
+ self.size: MetaDataItem = size
55
+ self.extension: MetaDataItem = extension
56
+ self.origin_src: MetaDataItem = origin_src
57
+
58
+ def to_dict(self) -> dict[str, str | None]:
59
+ return {
60
+ 'file_path': self.file_path if not self.file_path.is_empty else None,
61
+ 'dir_path': self.dir_path if not self.dir_path.is_empty else None,
62
+ 'name': self.name if not self.name.is_empty else None,
63
+ 'md5': self.md5 if not self.md5.is_empty else None,
64
+ 'size': self.size if not self.size.is_empty else None,
65
+ 'extension': self.extension if not self.extension.is_empty else None,
66
+ 'origin_src': self.origin_src if not self.origin_src.is_empty else None,
67
+ }
68
+
69
+
70
+ def get_void_metadata() -> dict[str, str | None]:
71
+ """
72
+ Salvar os metadados básico de documentos/imagens lidas em disco.
73
+ """
74
+ return MetaDataFile().to_dict()
75
+
76
+
77
+ def create_metadata(file: str | File) -> MetaDataFile:
78
+ if isinstance(file, str):
79
+ file = File(file)
80
+ mt = MetaDataFile(
81
+ file_path=MetaDataItem(file.absolute()),
82
+ dir_path=MetaDataItem(file.dirname()),
83
+ extension=MetaDataItem(file.extension()),
84
+ origin_src=MetaDataItem('file'),
85
+ name=MetaDataItem(file.name()),
86
+ )
87
+ if file.exists():
88
+ mt.size = MetaDataItem(file.size())
89
+ mt.md5 = MetaDataItem(file.md5())
90
+ return mt
91
+
92
+
93
+ __all__ = [
94
+ 'get_hash_from_bytes', 'get_void_metadata', 'get_void_metadata', 'contains',
95
+ 'concat_maps', 'create_dict_map_from_file', 'create_dict_map_values',
96
+ 'create_void_df_map', 'create_void_dict_map', 'ColumnBody', 'ColumnsTable',
97
+ 'ListString', 'HeadCell', 'HeadValues', 'TextTable', 'RotationAngle', 'LibPDF',
98
+ 'LibImageToPdf', 'LibPdfToImage', 'LibImage', 'LibDate', 'ArrayString',
99
+ 'MetaDataFile', 'MetaDataItem', 'create_metadata',
100
+ ]