kovapdf 0.1.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.
kovapdf/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """KovaPDF for Python: compress, merge, split, convert, read scans, redact,
2
+ check signatures and send documents for signature.
3
+
4
+ from kovapdf import KovaPDF
5
+ kova = KovaPDF() # the key comes from KOVAPDF_API_KEY
6
+ result = kova.compress("in.pdf", level="strong", save_to="out.pdf")
7
+ print(result.attribution) # Done with KovaPDF (kovapdf.com).
8
+ """
9
+
10
+ from ._generated import API_BASE, ATTRIBUTION, ENDPOINTS, SDK_VERSION, SITE_URL, TOOL_IDS, TOOLS
11
+ from ._http import ClientIdentity, Transport, parse_retry_after
12
+ from .client import KovaPDF, ResultFile, RunResult
13
+ from .errors import FRIENDLY, KovaPDFError
14
+ from .webhooks import verify_webhook
15
+
16
+ __version__ = SDK_VERSION
17
+ __all__ = [
18
+ "API_BASE",
19
+ "ATTRIBUTION",
20
+ "ENDPOINTS",
21
+ "FRIENDLY",
22
+ "SDK_VERSION",
23
+ "SITE_URL",
24
+ "TOOLS",
25
+ "TOOL_IDS",
26
+ "ClientIdentity",
27
+ "KovaPDF",
28
+ "KovaPDFError",
29
+ "ResultFile",
30
+ "RunResult",
31
+ "Transport",
32
+ "parse_retry_after",
33
+ "verify_webhook",
34
+ ]
kovapdf/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())
kovapdf/_generated.py ADDED
@@ -0,0 +1,598 @@
1
+ # Generated by packages/sdk-js/scripts/generate.mjs from integrations/generated/manifest.json and frontend/public/developers/openapi.json. Do not edit.
2
+ # fmt: off
3
+ """The generated part of the KovaPDF SDK: tools, their options and the API's endpoints."""
4
+
5
+ from typing import Any, Dict, List, Literal, Optional, Sequence, TypedDict, Union
6
+
7
+ SDK_VERSION = "0.1.0"
8
+ ATTRIBUTION = "Done with KovaPDF (kovapdf.com)."
9
+ SITE_URL = "https://www.kovapdf.com"
10
+ API_BASE = "https://api.kovapdf.com/api/v1"
11
+
12
+ TOOL_IDS = ("verify", "compress", "merge", "split", "organize", "convert", "ocr", "redact", "bijoy_to_unicode", "unicode_to_bijoy", "kruti_to_unicode",)
13
+ ToolId = Literal["verify", "compress", "merge", "split", "organize", "convert", "ocr", "redact", "bijoy_to_unicode", "unicode_to_bijoy", "kruti_to_unicode"]
14
+
15
+
16
+ class VerifyOptions(TypedDict, total=False):
17
+ """Options of verify (Check PDF signatures): No options. Needs the verify permission."""
18
+
19
+ pass
20
+
21
+
22
+ class CompressOptions(TypedDict, total=False):
23
+ """Options of compress (Compress PDF): level (basic, strong or quality) or targetKb."""
24
+
25
+ level: Literal["basic", "strong", "quality"]
26
+ targetKb: int
27
+
28
+
29
+ class MergeOptions(TypedDict, total=False):
30
+ """Options of merge (Merge PDFs): Two or more PDFs, in order."""
31
+
32
+ pass
33
+
34
+
35
+ class SplitOptions(TypedDict, total=False):
36
+ """Options of split (Split PDF): mode all, range (with pages), every (with pagesPerPart) or size (with maxMb)."""
37
+
38
+ mode: Literal["all", "range", "every", "size"]
39
+ pages: str
40
+ pagesPerPart: int
41
+ maxMb: float
42
+
43
+
44
+ class OrganizeOptions(TypedDict, total=False):
45
+ """Options of organize (Reorder, rotate or delete pages): pages: the new order, each a number or { page, rotate }."""
46
+
47
+ pages: List[Union[int, Dict[str, Any]]]
48
+
49
+
50
+ class ConvertOptions(TypedDict, total=False):
51
+ """Options of convert (Convert files): to word, excel, jpg or png for a PDF; to pdf for an office file or pictures."""
52
+
53
+ to: Literal["word", "excel", "jpg", "png", "pdf"]
54
+ pages: str
55
+ dpi: Literal[72, 150, 300]
56
+
57
+
58
+ class OcrOptions(TypedDict, total=False):
59
+ """Options of ocr (Read text from a scan): language and pages."""
60
+
61
+ language: str
62
+ pages: str
63
+
64
+
65
+ class RedactOptions(TypedDict, total=False):
66
+ """Options of redact (Redact a PDF): terms and or patterns, matchCase, wholeWord, label. The report lists every removal."""
67
+
68
+ terms: List[str]
69
+ patterns: List[Literal["email", "phone", "card", "date", "url", "iban", "ssn"]]
70
+ matchCase: bool
71
+ wholeWord: bool
72
+ label: str
73
+
74
+
75
+ class BijoyToUnicodeOptions(TypedDict, total=False):
76
+ """Options of bijoy_to_unicode (Bijoy to Unicode): text, or one .txt file; keepEnglish."""
77
+
78
+ text: str
79
+ keepEnglish: bool
80
+
81
+
82
+ class UnicodeToBijoyOptions(TypedDict, total=False):
83
+ """Options of unicode_to_bijoy (Unicode to Bijoy): text, or one .txt file."""
84
+
85
+ text: str
86
+
87
+
88
+ class KrutiToUnicodeOptions(TypedDict, total=False):
89
+ """Options of kruti_to_unicode (Kruti Dev to Unicode): text, or one .txt file; keepEnglish."""
90
+
91
+ text: str
92
+ keepEnglish: bool
93
+
94
+
95
+ TOOLS: List[Dict[str, Any]] = [
96
+ {
97
+ "id": "verify",
98
+ "command": "verify",
99
+ "method": "verify_job",
100
+ "title": "Check PDF signatures",
101
+ "description": "Checks every digital signature in a PDF: who signed, when, whether the document changed after signing, and whether the certificate is trusted. Returns a report. The file is not changed.",
102
+ "scope": "verify",
103
+ "limitsClass": "light",
104
+ "inputs": {
105
+ "kinds": ["pdf"],
106
+ "min": 1,
107
+ "max": 1,
108
+ "multi": False,
109
+ },
110
+ "optionsNote": "No options. Needs the verify permission.",
111
+ "options": [],
112
+ },
113
+ {
114
+ "id": "compress",
115
+ "command": "compress",
116
+ "method": "compress",
117
+ "title": "Compress PDF",
118
+ "description": "Makes a PDF smaller. Choose a level, or a target size in KB. Returns a link to the smaller PDF; the original is not changed.",
119
+ "scope": "tools:run",
120
+ "limitsClass": "light",
121
+ "inputs": {
122
+ "kinds": ["pdf"],
123
+ "min": 1,
124
+ "max": 1,
125
+ "multi": False,
126
+ },
127
+ "optionsNote": "level (basic, strong or quality) or targetKb.",
128
+ "options": [
129
+ {
130
+ "name": "level",
131
+ "flag": "level",
132
+ "kind": "string",
133
+ "choices": ["basic", "strong", "quality"],
134
+ "required": False,
135
+ "description": "basic (the usual) keeps good quality, strong makes it smallest, quality changes the least.",
136
+ "default": "basic",
137
+ },
138
+ {
139
+ "name": "targetKb",
140
+ "flag": "target-kb",
141
+ "kind": "integer",
142
+ "required": False,
143
+ "description": "Aim for this size in KB instead of a level.",
144
+ },
145
+ ],
146
+ },
147
+ {
148
+ "id": "merge",
149
+ "command": "merge",
150
+ "method": "merge",
151
+ "title": "Merge PDFs",
152
+ "description": "Joins two or more PDFs into one, in the order given. Returns a link to the merged PDF.",
153
+ "scope": "tools:run",
154
+ "limitsClass": "light",
155
+ "inputs": {
156
+ "kinds": ["pdf"],
157
+ "min": 2,
158
+ "max": 20,
159
+ "multi": True,
160
+ },
161
+ "optionsNote": "Two or more PDFs, in order.",
162
+ "options": [],
163
+ },
164
+ {
165
+ "id": "split",
166
+ "command": "split",
167
+ "method": "split",
168
+ "title": "Split PDF",
169
+ "description": "Splits a PDF: every page on its own (all), chosen pages into one PDF (range), every N pages (every), or into parts under a size (size). Returns a link to a PDF or a ZIP of PDFs.",
170
+ "scope": "tools:run",
171
+ "limitsClass": "heavy",
172
+ "inputs": {
173
+ "kinds": ["pdf"],
174
+ "min": 1,
175
+ "max": 1,
176
+ "multi": False,
177
+ },
178
+ "optionsNote": "mode all, range (with pages), every (with pagesPerPart) or size (with maxMb).",
179
+ "options": [
180
+ {
181
+ "name": "mode",
182
+ "flag": "mode",
183
+ "kind": "string",
184
+ "choices": ["all", "range", "every", "size"],
185
+ "required": False,
186
+ "description": "all, range, every or size.",
187
+ "default": "all",
188
+ },
189
+ {
190
+ "name": "pages",
191
+ "flag": "pages",
192
+ "kind": "string",
193
+ "required": False,
194
+ "description": "For range: pages like 1-3,5.",
195
+ },
196
+ {
197
+ "name": "pagesPerPart",
198
+ "flag": "pages-per-part",
199
+ "kind": "integer",
200
+ "required": False,
201
+ "description": "For every: pages in each part.",
202
+ },
203
+ {
204
+ "name": "maxMb",
205
+ "flag": "max-mb",
206
+ "kind": "number",
207
+ "required": False,
208
+ "description": "For size: the most MB in each part.",
209
+ },
210
+ ],
211
+ },
212
+ {
213
+ "id": "organize",
214
+ "command": "organize",
215
+ "method": "organize",
216
+ "title": "Reorder, rotate or delete pages",
217
+ "description": "Builds a new PDF from the pages listed, in that order. Each item is a page number, or { page, rotate } with rotate 90, 180 or 270. Pages left out are deleted. The original is not changed.",
218
+ "scope": "tools:run",
219
+ "limitsClass": "heavy",
220
+ "inputs": {
221
+ "kinds": ["pdf"],
222
+ "min": 1,
223
+ "max": 1,
224
+ "multi": False,
225
+ },
226
+ "optionsNote": "pages: the new order, each a number or { page, rotate }.",
227
+ "options": [
228
+ {
229
+ "name": "pages",
230
+ "flag": "pages",
231
+ "kind": "json",
232
+ "required": True,
233
+ "description": "The new document's pages in order: page numbers, or { page, rotate }.",
234
+ },
235
+ ],
236
+ },
237
+ {
238
+ "id": "convert",
239
+ "command": "convert",
240
+ "method": "convert",
241
+ "title": "Convert files",
242
+ "description": "Converts a PDF to Word (word), Excel (excel) or pictures (jpg, png); or a Word, Excel, PowerPoint or OpenDocument file, or pictures, to PDF (pdf). Returns a link to the result.",
243
+ "scope": "tools:run",
244
+ "limitsClass": "heavy",
245
+ "inputs": {
246
+ "kinds": ["pdf", "image", "office"],
247
+ "min": 1,
248
+ "max": 20,
249
+ "multi": True,
250
+ },
251
+ "optionsNote": "to word, excel, jpg or png for a PDF; to pdf for an office file or pictures.",
252
+ "options": [
253
+ {
254
+ "name": "to",
255
+ "flag": "to",
256
+ "kind": "string",
257
+ "choices": ["word", "excel", "jpg", "png", "pdf"],
258
+ "required": True,
259
+ "description": "What to make: word, excel, jpg, png or pdf.",
260
+ },
261
+ {
262
+ "name": "pages",
263
+ "flag": "pages",
264
+ "kind": "string",
265
+ "required": False,
266
+ "description": "For pictures: pages like 1-3.",
267
+ },
268
+ {
269
+ "name": "dpi",
270
+ "flag": "dpi",
271
+ "kind": "number",
272
+ "choices": [72, 150, 300],
273
+ "required": False,
274
+ "description": "For pictures: 72, 150 or 300.",
275
+ },
276
+ ],
277
+ },
278
+ {
279
+ "id": "ocr",
280
+ "command": "ocr",
281
+ "method": "ocr",
282
+ "title": "Read text from a scan",
283
+ "description": "Reads the text in a scanned PDF or a picture with text recognition. Returns the text and a link to a .txt file. Up to 30 pages per call; use pages to choose.",
284
+ "scope": "tools:run",
285
+ "limitsClass": "heavy",
286
+ "inputs": {
287
+ "kinds": ["pdf", "image"],
288
+ "min": 1,
289
+ "max": 1,
290
+ "multi": False,
291
+ },
292
+ "optionsNote": "language and pages.",
293
+ "options": [
294
+ {
295
+ "name": "language",
296
+ "flag": "language",
297
+ "kind": "string",
298
+ "required": False,
299
+ "description": "A language code such as eng, ben or hin; left out, it is detected.",
300
+ },
301
+ {
302
+ "name": "pages",
303
+ "flag": "pages",
304
+ "kind": "string",
305
+ "required": False,
306
+ "description": "Pages like 1-3,5.",
307
+ },
308
+ ],
309
+ },
310
+ {
311
+ "id": "redact",
312
+ "command": "redact",
313
+ "method": "redact",
314
+ "title": "Redact a PDF",
315
+ "description": "Permanently removes words or kinds of details (email, phone, card, date, url, iban, ssn) from a copy of the PDF, and lists every place something was removed as proof. The original is not changed.",
316
+ "scope": "tools:run",
317
+ "limitsClass": "heavy",
318
+ "inputs": {
319
+ "kinds": ["pdf"],
320
+ "min": 1,
321
+ "max": 1,
322
+ "multi": False,
323
+ },
324
+ "optionsNote": "terms and or patterns, matchCase, wholeWord, label. The report lists every removal.",
325
+ "options": [
326
+ {
327
+ "name": "terms",
328
+ "flag": "terms",
329
+ "kind": "string[]",
330
+ "required": False,
331
+ "description": "Words or phrases to remove.",
332
+ },
333
+ {
334
+ "name": "patterns",
335
+ "flag": "patterns",
336
+ "kind": "string[]",
337
+ "choices": ["email", "phone", "card", "date", "url", "iban", "ssn"],
338
+ "required": False,
339
+ "description": "Kinds of details to remove.",
340
+ },
341
+ {
342
+ "name": "matchCase",
343
+ "flag": "match-case",
344
+ "kind": "boolean",
345
+ "required": False,
346
+ "description": "Match capital letters exactly.",
347
+ },
348
+ {
349
+ "name": "wholeWord",
350
+ "flag": "whole-word",
351
+ "kind": "boolean",
352
+ "required": False,
353
+ "description": "Match whole words only.",
354
+ },
355
+ {
356
+ "name": "label",
357
+ "flag": "label",
358
+ "kind": "string",
359
+ "required": False,
360
+ "description": "Words printed in each box, such as REDACTED.",
361
+ },
362
+ ],
363
+ },
364
+ {
365
+ "id": "bijoy_to_unicode",
366
+ "command": "bijoy-to-unicode",
367
+ "method": "bijoy_to_unicode",
368
+ "title": "Bijoy to Unicode",
369
+ "description": "Converts Bangla text typed in Bijoy (SutonnyMJ and similar fonts) to Unicode Bangla. English words are kept as they are. Pass the text, or a .txt file.",
370
+ "scope": "tools:run",
371
+ "limitsClass": "text",
372
+ "inputs": {
373
+ "kinds": ["text"],
374
+ "min": 0,
375
+ "max": 1,
376
+ "multi": False,
377
+ },
378
+ "optionsNote": "text, or one .txt file; keepEnglish.",
379
+ "options": [
380
+ {
381
+ "name": "text",
382
+ "flag": "text",
383
+ "kind": "string",
384
+ "required": False,
385
+ "description": "The text to convert.",
386
+ },
387
+ {
388
+ "name": "keepEnglish",
389
+ "flag": "keep-english",
390
+ "kind": "boolean",
391
+ "required": False,
392
+ "description": "Keep English words as they are (the usual).",
393
+ },
394
+ ],
395
+ },
396
+ {
397
+ "id": "unicode_to_bijoy",
398
+ "command": "unicode-to-bijoy",
399
+ "method": "unicode_to_bijoy",
400
+ "title": "Unicode to Bijoy",
401
+ "description": "Converts Unicode Bangla text to Bijoy, for SutonnyMJ and similar fonts. Pass the text, or a .txt file.",
402
+ "scope": "tools:run",
403
+ "limitsClass": "text",
404
+ "inputs": {
405
+ "kinds": ["text"],
406
+ "min": 0,
407
+ "max": 1,
408
+ "multi": False,
409
+ },
410
+ "optionsNote": "text, or one .txt file.",
411
+ "options": [
412
+ {
413
+ "name": "text",
414
+ "flag": "text",
415
+ "kind": "string",
416
+ "required": False,
417
+ "description": "The text to convert.",
418
+ },
419
+ ],
420
+ },
421
+ {
422
+ "id": "kruti_to_unicode",
423
+ "command": "kruti-to-unicode",
424
+ "method": "kruti_to_unicode",
425
+ "title": "Kruti Dev to Unicode",
426
+ "description": "Converts Hindi text typed in Kruti Dev to Unicode Hindi. English words are kept as they are. Pass the text, or a .txt file.",
427
+ "scope": "tools:run",
428
+ "limitsClass": "text",
429
+ "inputs": {
430
+ "kinds": ["text"],
431
+ "min": 0,
432
+ "max": 1,
433
+ "multi": False,
434
+ },
435
+ "optionsNote": "text, or one .txt file; keepEnglish.",
436
+ "options": [
437
+ {
438
+ "name": "text",
439
+ "flag": "text",
440
+ "kind": "string",
441
+ "required": False,
442
+ "description": "The text to convert.",
443
+ },
444
+ {
445
+ "name": "keepEnglish",
446
+ "flag": "keep-english",
447
+ "kind": "boolean",
448
+ "required": False,
449
+ "description": "Keep English words as they are (the usual).",
450
+ },
451
+ ],
452
+ },
453
+ ]
454
+
455
+ ENDPOINTS: Dict[str, Dict[str, Any]] = {
456
+ "cancelSignRequest": {
457
+ "method": "POST",
458
+ "path": "/sign-requests/{id}/cancel",
459
+ "scope": "sign-requests:write",
460
+ },
461
+ "createJob": {
462
+ "method": "POST",
463
+ "path": "/jobs",
464
+ "scope": "tools",
465
+ },
466
+ "createSignRequest": {
467
+ "method": "POST",
468
+ "path": "/sign-requests",
469
+ "scope": "sign-requests:write",
470
+ },
471
+ "digitalSign": {
472
+ "method": "POST",
473
+ "path": "/digital-sign",
474
+ "scope": "digital-sign",
475
+ },
476
+ "downloadResult": {
477
+ "method": "GET",
478
+ "path": "/downloads/{token}",
479
+ "scope": None,
480
+ },
481
+ "getFile": {
482
+ "method": "GET",
483
+ "path": "/files/{id}",
484
+ "scope": "tools",
485
+ },
486
+ "getJob": {
487
+ "method": "GET",
488
+ "path": "/jobs/{id}",
489
+ "scope": "tools",
490
+ },
491
+ "getMe": {
492
+ "method": "GET",
493
+ "path": "/me",
494
+ "scope": None,
495
+ },
496
+ "getOpenApi": {
497
+ "method": "GET",
498
+ "path": "/openapi.json",
499
+ "scope": None,
500
+ },
501
+ "getSignRequest": {
502
+ "method": "GET",
503
+ "path": "/sign-requests/{id}",
504
+ "scope": "sign-requests:read",
505
+ },
506
+ "getSignRequestDocument": {
507
+ "method": "GET",
508
+ "path": "/sign-requests/{id}/document",
509
+ "scope": "sign-requests:read",
510
+ },
511
+ "listSignRequests": {
512
+ "method": "GET",
513
+ "path": "/sign-requests",
514
+ "scope": "sign-requests:read",
515
+ },
516
+ "listTools": {
517
+ "method": "GET",
518
+ "path": "/tools",
519
+ "scope": None,
520
+ },
521
+ "remindSigners": {
522
+ "method": "POST",
523
+ "path": "/sign-requests/{id}/remind",
524
+ "scope": "sign-requests:write",
525
+ },
526
+ "uploadFile": {
527
+ "method": "POST",
528
+ "path": "/files",
529
+ "scope": "tools",
530
+ },
531
+ "verifyPdf": {
532
+ "method": "POST",
533
+ "path": "/verify",
534
+ "scope": "verify",
535
+ },
536
+ }
537
+
538
+
539
+ class ToolMethods:
540
+ """One method per tool, made from the manifest. Each uploads, runs the job, waits and returns the result."""
541
+
542
+ def run(self, tool: str, inputs: Sequence[Any] = (), options: Optional[Dict[str, Any]] = None, *, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any: # pragma: no cover
543
+ raise NotImplementedError
544
+
545
+ def verify_job(self, file: Any, *, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
546
+ """Check PDF signatures. Checks every digital signature in a PDF: who signed, when, whether the document changed after signing, and whether the certificate is trusted. Returns a report. The file is not changed."""
547
+ options = {k: v for k, v in {}.items() if v is not None}
548
+ return self.run("verify", [file], options, timeout=timeout, save_to=save_to)
549
+
550
+ def compress(self, file: Any, *, level: Optional[Literal["basic", "strong", "quality"]] = None, target_kb: Optional[int] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
551
+ """Compress PDF. Makes a PDF smaller. Choose a level, or a target size in KB. Returns a link to the smaller PDF; the original is not changed."""
552
+ options = {k: v for k, v in {"level": level, "targetKb": target_kb}.items() if v is not None}
553
+ return self.run("compress", [file], options, timeout=timeout, save_to=save_to)
554
+
555
+ def merge(self, files: Sequence[Any], *, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
556
+ """Merge PDFs. Joins two or more PDFs into one, in the order given. Returns a link to the merged PDF."""
557
+ options = {k: v for k, v in {}.items() if v is not None}
558
+ return self.run("merge", list(files), options, timeout=timeout, save_to=save_to)
559
+
560
+ def split(self, file: Any, *, mode: Optional[Literal["all", "range", "every", "size"]] = None, pages: Optional[str] = None, pages_per_part: Optional[int] = None, max_mb: Optional[float] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
561
+ """Split PDF. Splits a PDF: every page on its own (all), chosen pages into one PDF (range), every N pages (every), or into parts under a size (size). Returns a link to a PDF or a ZIP of PDFs."""
562
+ options = {k: v for k, v in {"mode": mode, "pages": pages, "pagesPerPart": pages_per_part, "maxMb": max_mb}.items() if v is not None}
563
+ return self.run("split", [file], options, timeout=timeout, save_to=save_to)
564
+
565
+ def organize(self, file: Any, *, pages: List[Union[int, Dict[str, Any]]], timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
566
+ """Reorder, rotate or delete pages. Builds a new PDF from the pages listed, in that order. Each item is a page number, or { page, rotate } with rotate 90, 180 or 270. Pages left out are deleted. The original is not changed."""
567
+ options = {k: v for k, v in {"pages": pages}.items() if v is not None}
568
+ return self.run("organize", [file], options, timeout=timeout, save_to=save_to)
569
+
570
+ def convert(self, files: Sequence[Any], *, to: Literal["word", "excel", "jpg", "png", "pdf"], pages: Optional[str] = None, dpi: Optional[Literal[72, 150, 300]] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
571
+ """Convert files. Converts a PDF to Word (word), Excel (excel) or pictures (jpg, png); or a Word, Excel, PowerPoint or OpenDocument file, or pictures, to PDF (pdf). Returns a link to the result."""
572
+ options = {k: v for k, v in {"to": to, "pages": pages, "dpi": dpi}.items() if v is not None}
573
+ return self.run("convert", list(files), options, timeout=timeout, save_to=save_to)
574
+
575
+ def ocr(self, file: Any, *, language: Optional[str] = None, pages: Optional[str] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
576
+ """Read text from a scan. Reads the text in a scanned PDF or a picture with text recognition. Returns the text and a link to a .txt file. Up to 30 pages per call; use pages to choose."""
577
+ options = {k: v for k, v in {"language": language, "pages": pages}.items() if v is not None}
578
+ return self.run("ocr", [file], options, timeout=timeout, save_to=save_to)
579
+
580
+ def redact(self, file: Any, *, terms: Optional[List[str]] = None, patterns: Optional[List[Literal["email", "phone", "card", "date", "url", "iban", "ssn"]]] = None, match_case: Optional[bool] = None, whole_word: Optional[bool] = None, label: Optional[str] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
581
+ """Redact a PDF. Permanently removes words or kinds of details (email, phone, card, date, url, iban, ssn) from a copy of the PDF, and lists every place something was removed as proof. The original is not changed."""
582
+ options = {k: v for k, v in {"terms": terms, "patterns": patterns, "matchCase": match_case, "wholeWord": whole_word, "label": label}.items() if v is not None}
583
+ return self.run("redact", [file], options, timeout=timeout, save_to=save_to)
584
+
585
+ def bijoy_to_unicode(self, file: Any = None, *, text: Optional[str] = None, keep_english: Optional[bool] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
586
+ """Bijoy to Unicode. Converts Bangla text typed in Bijoy (SutonnyMJ and similar fonts) to Unicode Bangla. English words are kept as they are. Pass the text, or a .txt file."""
587
+ options = {k: v for k, v in {"text": text, "keepEnglish": keep_english}.items() if v is not None}
588
+ return self.run("bijoy_to_unicode", [] if file is None else [file], options, timeout=timeout, save_to=save_to)
589
+
590
+ def unicode_to_bijoy(self, file: Any = None, *, text: Optional[str] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
591
+ """Unicode to Bijoy. Converts Unicode Bangla text to Bijoy, for SutonnyMJ and similar fonts. Pass the text, or a .txt file."""
592
+ options = {k: v for k, v in {"text": text}.items() if v is not None}
593
+ return self.run("unicode_to_bijoy", [] if file is None else [file], options, timeout=timeout, save_to=save_to)
594
+
595
+ def kruti_to_unicode(self, file: Any = None, *, text: Optional[str] = None, keep_english: Optional[bool] = None, timeout: Optional[float] = None, save_to: Optional[str] = None) -> Any:
596
+ """Kruti Dev to Unicode. Converts Hindi text typed in Kruti Dev to Unicode Hindi. English words are kept as they are. Pass the text, or a .txt file."""
597
+ options = {k: v for k, v in {"text": text, "keepEnglish": keep_english}.items() if v is not None}
598
+ return self.run("kruti_to_unicode", [] if file is None else [file], options, timeout=timeout, save_to=save_to)