convert-stream 1.1__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.

Potentially problematic release.


This version of convert-stream might be problematic. Click here for more details.

@@ -0,0 +1,534 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ from __future__ import annotations
5
+ from typing import List, Dict
6
+ from abc import ABC, abstractmethod
7
+ from io import BytesIO
8
+ from pandas import DataFrame
9
+ import pandas
10
+ from PIL import Image
11
+ from reportlab.pdfgen import canvas
12
+ from reportlab.pdfgen.canvas import Canvas
13
+ from reportlab.lib.pagesizes import letter
14
+ from reportlab.lib.utils import ImageReader
15
+ from reportlab.platypus import Image as ReportLabImage
16
+ from PyPDF2 import PdfReader, PdfWriter, PageObject
17
+
18
+ try:
19
+ from fitz import Page, Pixmap, Document
20
+ except:
21
+ from pymupdf import Page, Pixmap, Document
22
+
23
+ from soup_files import File, Directory
24
+ from convertlib.models.m_pdf_page import LibraryPDF, ABCPagePdf
25
+ from convertlib.models.m_imgelib import LibraryImage
26
+ from convertlib.models.m_pdf_document import ABCDocumentPdf
27
+ from convertlib.imagelib import ImageObject, ImageInverColor
28
+ from convertlib.pdf_page import PageDocumentPdf, FitzPagePdf, PyPdf2PagePdf
29
+
30
+ #=======================================================================#
31
+ # Documento PDF
32
+ #=======================================================================#
33
+ class DocumentPyPdf2(ABCDocumentPdf):
34
+ def __init__(self):
35
+ super().__init__(LibraryPDF.PYPDF)
36
+
37
+ def is_null(self):
38
+ if len(self.pages) < 1:
39
+ return True
40
+ return False
41
+
42
+ def add_page(self, page:PageDocumentPdf):
43
+ if not isinstance(page, PageDocumentPdf):
44
+ raise ValueError(f'Use: PageDocumentPdf(), não {type(page)}')
45
+ if self.num_pages >= self.max_num_pages:
46
+ print(f'Número máximo de páginas está definido em: {self.max_num_pages}')
47
+ return False
48
+ self.num_pages += 1
49
+ page.page_number = self.num_pages
50
+ self.pages.append(page)
51
+ return True
52
+
53
+ def add_pages(self, pages:List[PageDocumentPdf]):
54
+ for p in pages:
55
+ self.add_page(p)
56
+
57
+ def add_file_pdf(self, file:File):
58
+ pdf_reader = PdfReader(file.absolute())
59
+ for page in pdf_reader.pages:
60
+ self.add_page(PageDocumentPdf.create_from_page_pypdf2(page))
61
+
62
+ def add_files_pdf(self, files:List[File]):
63
+ for f in files:
64
+ self.add_file_pdf(f)
65
+
66
+ def add_bytes_page(self, page_bytes:bytes):
67
+ self.add_page(PageDocumentPdf.create_from_page_bytes(page_bytes))
68
+
69
+ def add_bytes_file_pdf(self, bytes_file_pdf:BytesIO):
70
+ try:
71
+ # Cria um objeto PdfReader a partir dos bytes
72
+ pdf_reader = PdfReader(bytes_file_pdf)
73
+
74
+ except Exception as e:
75
+ print(f"Erro ao adicionar PDF a partir de bytes: {e}")
76
+ else:
77
+ # Itera pelas páginas do documento e adiciona ao objeto atual
78
+ page:PageObject = None
79
+ for num, page in enumerate(pdf_reader.pages):
80
+ self.add_page(
81
+ PageDocumentPdf(PyPdf2PagePdf(page, num+1))
82
+ )
83
+ finally:
84
+ pass
85
+
86
+ def add_file_url(self, url):
87
+ try:
88
+ import requests
89
+ except Exception as e:
90
+ print(e)
91
+ return
92
+
93
+ try:
94
+ response:requests.Response = requests.get(url)
95
+ except Exception as e:
96
+ print(e)
97
+ return
98
+
99
+ if not response.status_code == 200:
100
+ return
101
+ bt:BytesIO = BytesIO(response.content)
102
+ self.add_bytes_file_pdf(bt)
103
+
104
+ def remove_page(self, page_number:int) -> None:
105
+ if (page_number > self.num_pages) or (page_number < 1):
106
+ return
107
+ del self.pages[page_number - 1]
108
+ for num, page in enumerate(self.pages):
109
+ page.page_number = num + 1
110
+ self.num_pages = len(self.pages)
111
+
112
+ def remove_pages(self, num_pages:List[int]):
113
+ new:List[PageDocumentPdf] = []
114
+ for idx, page in enumerate(self.pages):
115
+ if (idx + 1) in num_pages:
116
+ continue
117
+ new.append(page)
118
+ self.clear()
119
+ self.add_pages(new)
120
+ #self.pages = new
121
+
122
+ def rotate_page(self, page_number: int, degrees:int):
123
+ if page_number > self.num_pages:
124
+ return
125
+ if page_number < 1:
126
+ return
127
+ self.pages[page_number - 1].rotate(degrees)
128
+
129
+ def set_paisagem(self):
130
+ for page in self.pages:
131
+ page.set_paisagem()
132
+
133
+ def to_file_pdf(self, fp: File):
134
+ """
135
+ Salva o documento no disco, usando PyPDF2.
136
+ """
137
+ writer = PdfWriter()
138
+ for page in self.pages:
139
+ if not isinstance(page.page, PyPdf2PagePdf): # Verifica se a página é do tipo PyPdf2
140
+ raise TypeError("Todas as páginas devem ser do tipo PyPdf2PagePdf para salvar com PyPDF2.")
141
+ writer.add_page(page.page.page) # Adiciona a página ao writer
142
+ with open(fp.absolute(), "wb+") as f:
143
+ writer.write(f)
144
+
145
+ def to_data(self, separator:str='\n') -> DataFrame:
146
+ data:List[DataFrame] = []
147
+ for page in self.pages:
148
+ current_df = page.to_table(separator).to_data()
149
+ if current_df.empty:
150
+ continue
151
+ current_num = len(current_df)
152
+ current_df['NUM_PÁGINA'] = [page.page_number] * current_num
153
+ data.append(current_df)
154
+ return pandas.concat(data)
155
+
156
+ def to_excel(self, file, *, separator:str='\n'):
157
+ try:
158
+ self.to_data(separator).to_excel(file.absolute(), index=False)
159
+ except Exception as e:
160
+ print(e)
161
+
162
+ def to_list(self, separator='\n') -> List[str]:
163
+ values: List[str] = []
164
+ for p in self.pages:
165
+ text = p.to_string()
166
+ if text is not None:
167
+ values.append(text)
168
+ return values
169
+
170
+ def clear(self):
171
+ self.num_pages = 0
172
+ self.pages.clear()
173
+
174
+ def get_page_number(self, num) -> PageDocumentPdf:
175
+ """
176
+ Retorna a página correspondente ao número fornecido.
177
+ Se o número estiver fora do intervalo, retorna None.
178
+ """
179
+ if not isinstance(num, int):
180
+ raise ValueError(f'{__class__.__name__} Use um número inteiro para referência de páginas, não {type(num)}')
181
+ if (num > self.num_pages) or (num < 1):
182
+ return None
183
+ if self.is_null():
184
+ return None
185
+ return self.pages[num - 1]
186
+
187
+ def get_pages_numbers(self, numbers:List[int]) -> List[PageDocumentPdf]:
188
+ """
189
+ Retorna uma lista de páginas correspondentes aos números fornecidos.
190
+ Se algum número estiver fora do intervalo, ele será ignorado.
191
+ """
192
+ if not isinstance(numbers, list):
193
+ raise ValueError(f'{__class__.__name__} Use uma lista de números inteiros para referência de páginas, não {type(pages_numbers)}')
194
+ if self.is_null():
195
+ return []
196
+
197
+ new_pages:List[PageDocumentPdf] = []
198
+ for num in numbers:
199
+ pg = self.get_page_number(num)
200
+ if pg is not None:
201
+ new_pages.append(pg)
202
+ return new_pages
203
+
204
+ def get_document_with_text(self, text) -> DocumentPdf:
205
+ """
206
+ Retorna uma lista de páginas que contêm o texto especificado.
207
+ Se o texto não for encontrado, retorna uma lista vazia.
208
+ """
209
+ doc = DocumentPdf(self.library)
210
+ for page in self.pages:
211
+ current_text:str = page.to_string()
212
+ if (current_text is not None) and (text in current_text):
213
+ doc.add_page(page)
214
+ return doc
215
+
216
+
217
+ class DocumentFitz(ABCDocumentPdf):
218
+ def __init__(self):
219
+ super().__init__(LibraryPDF.FITZ)
220
+
221
+ def is_null(self):
222
+ if len(self.pages) < 1:
223
+ return True
224
+ return False
225
+
226
+ def add_page(self, page:PageDocumentPdf):
227
+ if not isinstance(page, PageDocumentPdf):
228
+ raise ValueError(f'Use: PageDocumentPdf(), não {type(page)}')
229
+ if self.num_pages >= self.max_num_pages:
230
+ print(f'Número máximo de páginas está definido em: {self.max_num_pages}')
231
+ return False
232
+ self.num_pages += 1
233
+ page.page_number = self.num_pages
234
+ self.pages.append(page)
235
+ return True
236
+
237
+ def add_pages(self, pages:List[ABCPagePdf]):
238
+ for p in pages:
239
+ self.add_page(p)
240
+
241
+ def add_file_pdf(self, file:File):
242
+ doc = Document(file.absolute())
243
+ for page in doc:
244
+ self.add_page(PageDocumentPdf.create_from_page_fitz(page))
245
+
246
+ def add_files_pdf(self, files:List[File]):
247
+ for f in files:
248
+ self.add_file_pdf(f)
249
+
250
+ def add_bytes_page(self, page_bytes:bytes):
251
+ self.add_page(PageDocumentPdf.create_from_page_bytes(page_bytes, library=LibraryPDF.FITZ))
252
+
253
+ def add_bytes_file_pdf(self, bytes_file_pdf:BytesIO):
254
+ try:
255
+ # Cria um objeto fitz.Document a partir dos bytes
256
+ pdf_document:Document = Document(stream=bytes_file_pdf, filetype="pdf")
257
+ # Itera pelas páginas do documento e adiciona ao objeto atual
258
+ for page in pdf_document:
259
+ self.add_page(
260
+ PageDocumentPdf(FitzPagePdf(page, page.number))
261
+ )
262
+ except Exception as e:
263
+ print(f"Erro ao adicionar PDF a partir de bytes: {e}")
264
+ finally:
265
+ pass
266
+ #pdf_document.close() # Fecha o documento após o uso
267
+
268
+ def add_file_url(self, url):
269
+ try:
270
+ import requests
271
+ except Exception as e:
272
+ print(e)
273
+ return
274
+
275
+ try:
276
+ response:requests.Response = requests.get(url)
277
+ except Exception as e:
278
+ print(e)
279
+ return
280
+
281
+ if not response.status_code == 200:
282
+ return
283
+ bt:BytesIO = BytesIO(response.content)
284
+ self.add_bytes_file_pdf(bt)
285
+
286
+ def remove_page(self, page_number:int) -> None:
287
+ if not isinstance(page_number, int):
288
+ raise ValueError(f'{__class__.__name__} Use um número inteiro para referência de páginas, não {type(page_number)}')
289
+
290
+ if (page_number > self.num_pages) or (page_number < 1):
291
+ return
292
+ del self.pages[page_number - 1]
293
+ for num, page in enumerate(self.pages):
294
+ page.page_number = num + 1
295
+ self.num_pages = len(self.pages)
296
+
297
+ def remove_pages(self, num_pages:List[int]):
298
+ new:List[PageDocumentPdf] = []
299
+ for idx, page in enumerate(self.pages):
300
+ if (idx + 1) in num_pages:
301
+ continue
302
+ new.append(page)
303
+ self.clear()
304
+ self.add_pages(new)
305
+ #self.pages = new
306
+
307
+ def rotate_page(self, page_number: int, degrees:int):
308
+ if (page_number > self.num_pages) or (page_number < 1):
309
+ return
310
+ self.pages[page_number - 1].rotate(degrees)
311
+
312
+ def set_paisagem(self):
313
+ for page in self.pages:
314
+ page.set_paisagem()
315
+
316
+ def to_file_pdf(self, fp: File):
317
+ """
318
+ Salva o documento no disco, incluíndo todas as páginas (atuais e adicionadas)
319
+ com a lib fitz.
320
+ """
321
+ pdf_document = Document() # Cria um novo documento PDF
322
+ for page in self.pages:
323
+ if not isinstance(page.page.page, Page): # Verifica se a página é do tipo fitz.Page
324
+ raise TypeError(f"Todas as páginas devem ser do tipo [fitz.Page], não {type(page.page.page)}.")
325
+ # Insere as páginas no novo documento
326
+ pdf_document.insert_pdf(
327
+ page.page.page.parent,
328
+ from_page=page.page.page.number,
329
+ to_page=page.page.page.number
330
+ )
331
+
332
+ # Salva o documento final no disco
333
+ pdf_document.save(fp.absolute())
334
+ pdf_document.close()
335
+
336
+ def to_data(self, separator:str='\n') -> DataFrame:
337
+ data:List[DataFrame] = []
338
+ for page in self.pages:
339
+ tb = page.to_table(separator)
340
+ current_df = tb.to_data()
341
+ if current_df.empty:
342
+ continue
343
+ current_num = len(current_df)
344
+ current_df['NUM_PÁGINA'] = [page.page_number] * current_num
345
+ data.append(current_df)
346
+ return pandas.concat(data)
347
+
348
+ def to_excel(self, file, *, separator:str='\n'):
349
+ try:
350
+ self.to_data(separator).to_excel(file.absolute(), index=False)
351
+ except Exception as e:
352
+ print(e)
353
+
354
+ def to_list(self, separator='\n') -> List[str]:
355
+ values: List[str] = []
356
+ for p in self.pages:
357
+ text = p.to_string()
358
+ if text is not None:
359
+ values.append(text)
360
+ return values
361
+
362
+ def clear(self):
363
+ self.num_pages = 0
364
+ self.pages.clear()
365
+
366
+ def get_page_number(self, num) -> PageDocumentPdf:
367
+ """
368
+ Retorna a página correspondente ao número fornecido.
369
+ Se o número estiver fora do intervalo, retorna None.
370
+ """
371
+ if not isinstance(num, int):
372
+ raise ValueError(f'{__class__.__name__} Use um número inteiro para referência de páginas, não {type(num)}')
373
+ if (num > self.num_pages) or (num < 1):
374
+ return None
375
+ if self.is_null():
376
+ return None
377
+ return self.pages[num - 1]
378
+
379
+ def get_pages_numbers(self, numbers:List[int]) -> List[PageDocumentPdf]:
380
+ """
381
+ Retorna uma lista de páginas correspondentes aos números fornecidos.
382
+ Se algum número estiver fora do intervalo, ele será ignorado.
383
+ """
384
+ if not isinstance(numbers, list):
385
+ raise ValueError(f'{__class__.__name__} Use uma lista de números inteiros para referência de páginas, não {type(pages_numbers)}')
386
+ if self.is_null():
387
+ return []
388
+
389
+ new_pages:List[PageDocumentPdf] = []
390
+ for num in numbers:
391
+ pg = self.get_page_number(num)
392
+ if pg is not None:
393
+ new_pages.append(pg)
394
+ return new_pages
395
+
396
+ def get_document_with_text(self, text) -> DocumentPdf:
397
+ """
398
+ Retorna uma lista de páginas que contêm o texto especificado.
399
+ Se o texto não for encontrado, retorna uma lista vazia.
400
+ """
401
+ doc = DocumentPdf(self.library)
402
+ for page in self.pages:
403
+ current_text:str = page.to_string()
404
+ if (current_text is not None) and (text in current_text):
405
+ doc.add_page(page)
406
+ return doc
407
+
408
+
409
+ class DocumentPdf(ABCDocumentPdf):
410
+ def __init__(self, library = LibraryPDF.FITZ):
411
+ self.library:LibraryPDF = library
412
+ if self.library == LibraryPDF.PYPDF:
413
+ self.document:ABCDocumentPdf = DocumentPyPdf2()
414
+ elif self.library == LibraryPDF.FITZ:
415
+ self.document:ABCDocumentPdf = DocumentFitz()
416
+ else:
417
+ raise NotImplementedError(f'{__class__.__name__}\nLibraryPDF não implementada: {type(library)}')
418
+
419
+ @property
420
+ def max_num_pages(self) -> int:
421
+ return self.document.max_num_pages
422
+
423
+ @max_num_pages.setter
424
+ def max_num_pages(self, new:int):
425
+ if isinstance(new, int):
426
+ self.document.max_num_pages = new
427
+
428
+ @property
429
+ def num_pages(self) -> int:
430
+ return self.document.num_pages
431
+
432
+ @num_pages.setter
433
+ def num_pages(self, new:int):
434
+ self.document.num_pages = new
435
+
436
+ @property
437
+ def pages(self) -> List[PageDocumentPdf]:
438
+ return self.document.pages
439
+
440
+ @pages.setter
441
+ def pages(self, new:List[PageDocumentPdf]):
442
+ self.num_pages = 0
443
+ for pg in new:
444
+ self.add_page(pg)
445
+
446
+ def is_null(self) -> bool:
447
+ return self.document.is_null()
448
+
449
+ def add_page(self, page:PageDocumentPdf):
450
+ if self.library != page.current_library:
451
+ if self.library == LibraryPDF.FITZ:
452
+ page.set_page_fitz()
453
+ elif self.library == LibraryPDF.PYPDF:
454
+ page.set_page_pypdf2()
455
+ if self.document.add_page(page):
456
+ print(f'Página PDF adicionada: {self.num_pages}')
457
+
458
+ def add_pages(self, pages:List[ABCPagePdf]):
459
+ for p in pages:
460
+ self.add_page(p)
461
+
462
+ def add_file_pdf(self, file:File):
463
+ print(f'Adicionando páginas do arquivo: {file.basename()}')
464
+ self.document.add_file_pdf(file)
465
+
466
+ def add_files_pdf(self, files:List[File]):
467
+ for f in files:
468
+ self.add_file_pdf(f)
469
+
470
+ def add_bytes_page(self, page_bytes:bytes):
471
+ self.document.add_bytes_page(page_bytes)
472
+
473
+ def add_bytes_file_pdf(self, file_bytes:BytesIO):
474
+ self.document.add_bytes_file_pdf(file_bytes)
475
+
476
+ def add_file_url(self, url):
477
+ print(f'Adicionando URL: {url}')
478
+ self.document.add_file_url(url)
479
+
480
+ def remove_page(self, page_number:int) -> None:
481
+ self.document.remove_page(page_number)
482
+
483
+ def remove_pages(self, num_pages:List[int]):
484
+ self.document.remove_pages(num_pages)
485
+
486
+ def rotate_page(self, page_number: int, degrees:int):
487
+ print(f'Rotacionando a página {page_number} [{degrees}]')
488
+ self.document.rotate_page(page_number, degrees)
489
+
490
+ def set_paisagem(self):
491
+ self.document.set_paisagem()
492
+
493
+ def to_file_pdf(self, file:File) -> bool:
494
+ if self.is_null():
495
+ print(f'O documento está vazio, adicione páginas ou arquivos para prosseguir!')
496
+ return False
497
+ print(f'Salvando arquivo: {file.basename()}')
498
+ self.document.to_file_pdf(file)
499
+ return False
500
+
501
+ def to_data(self, separator:str='\n') -> DataFrame:
502
+ return self.document.to_data(separator)
503
+
504
+ def to_excel(self, file, *, separator = '\n'):
505
+ self.document.to_excel(file, separator=separator)
506
+
507
+ def to_list(self, separator='\n') -> List[str]:
508
+ return self.document.to_list(separator)
509
+
510
+ def clear(self):
511
+ self.document.clear()
512
+
513
+ def get_page_number(self, num) -> PageDocumentPdf:
514
+ return self.document.get_page_number(num)
515
+
516
+ def get_pages_numbers(self, numbers) -> List[PageDocumentPdf]:
517
+ return self.document.get_pages_numbers(numbers)
518
+
519
+ def get_document_with_text(self, text) -> DocumentPdf:
520
+ """
521
+ Retorna um novo documento com as páginas que contém o texto informado no parâmetro,
522
+ se o texto não for encontrado, retorna um documento vazio.
523
+ """
524
+ return self.document.get_document_with_text(text)
525
+
526
+ @classmethod
527
+ def create_from_file(cls, file:File, *, library:LibraryPDF=LibraryPDF.PYPDF) -> DocumentPdf:
528
+ doc = cls(library)
529
+ doc.add_file_pdf(file)
530
+ return doc
531
+
532
+
533
+
534
+