txtwrap 2.0.0__tar.gz → 2.2.0__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.
txtwrap-2.2.0/PKG-INFO ADDED
@@ -0,0 +1,545 @@
1
+ Metadata-Version: 2.1
2
+ Name: txtwrap
3
+ Version: 2.2.0
4
+ Summary: A tool for wrapping and filling text.
5
+ Home-page: https://github.com/azzammuhyala/txtwrap
6
+ Author: azzammuhyala
7
+ Author-email: azzammuhyala@gmail.com
8
+ License: MIT
9
+ Keywords: wrap,wrapper,wrapping,wrapped,wrapping tool,text wrap,text wrapper,simple wrap,align,aligner,aligning,aligned
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.3
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Requires-Python: >=3.3
15
+ Description-Content-Type: text/markdown
16
+
17
+ # TxTWrap🔡
18
+ A tool for wrapping and filling text.🔨
19
+
20
+ ## All constants, functions, and classes❕
21
+ - `LOREM_IPSUM_WORDS`
22
+ - `LOREM_IPSUM_SENTENCES`
23
+ - `LOREM_IPSUM_PARAGRAPHS`
24
+ - `TextWrapper` (Updated)
25
+ - `mono` (Updated)
26
+ - `word` (Updated)
27
+ - `wrap` (Updated)
28
+ - `align` (Updated)
29
+ - `fillstr` (Updated)
30
+ - `printwrap` (Updated)
31
+ - `indent` (Updated)
32
+ - `dedent` (Updated)
33
+ - `shorten` (Updated)
34
+
35
+ ## Documents📄
36
+ This module is inspired by the [`textwrap`](https://docs.python.org/3/library/textwrap.html) module,
37
+ which provides several useful functions, along with the [`TextWrapper`](#textwrapper), class that
38
+ handles all available functions.
39
+
40
+ The difference between [`txtwrap`](https://pypi.org/project/txtwrap) and
41
+ [`textwrap`](https://docs.python.org/3/library/textwrap.html) is that this module is designed not
42
+ only for wrapping and filling monospace fonts but also for other font types, such as Arial,
43
+ Times New Roman, and more. Additionally, this module offers extra functions that are not available
44
+ in the original [`textwrap`](https://docs.python.org/3/library/textwrap.html).
45
+
46
+ <!-- tag <h1> boundary line -->
47
+ <h1></h1>
48
+
49
+ ```py
50
+ LOREM_IPSUM_WORDS
51
+ LOREM_IPSUM_SENTENCES
52
+ LOREM_IPSUM_PARAGRAPHS
53
+ ```
54
+ A collection of words, sentences, and paragraphs that can be used as examples.
55
+ - `LOREM_IPSUM_WORDS` contains a short Lorem Ipsum sentence.
56
+ - `LOREM_IPSUM_SENTENCES` contains a slightly longer paragraph.
57
+ - `LOREM_IPSUM_PARAGRAPHS` contains several longer paragraphs.
58
+
59
+ <h1></h1>
60
+
61
+ ### `TextWrapper`
62
+ ```py
63
+ class TextWrapper:
64
+
65
+ def __init__(
66
+
67
+ self,
68
+ width: Union[int, float] = 70,
69
+ line_padding: Union[int, float] = 0,
70
+ method: Literal['mono', 'word'] = 'word',
71
+ alignment: Literal['left', 'center', 'right', 'fill',
72
+ 'fill-left', 'fill-center', 'fill-right'] = 'left',
73
+ fillchar: str = ' ',
74
+ placeholder: str = '...',
75
+ prefix: Optional[Union[str, Iterable[str]]] = None,
76
+ separator: Optional[Union[str, Iterable[str]]] = None,
77
+ max_lines: Optional[int] = None,
78
+ preserve_empty: bool = True,
79
+ use_minimum_width: bool = True,
80
+ justify_last_line: bool = False,
81
+ break_on_hyphens: bool = True,
82
+ sizefunc: Optional[Callable[[str], Tuple[Union[int, float], Union[int, float]]]] = None,
83
+ predicate: Optional[Callable[[str], bool]] = None
84
+
85
+ ) -> None
86
+ ```
87
+
88
+ A class that handles all functions available in this module. Each keyword argument corresponds to
89
+ its attribute. For example:
90
+ ```py
91
+ wrapper = TextWrapper(width=100)
92
+ ```
93
+ is equivalent to:
94
+ ```py
95
+ wrapper = TextWrapper()
96
+ wrapper.width = 100
97
+ ```
98
+ You can reuse [`TextWrapper`](#textwrapper) multiple times or modify its options by assigning new
99
+ values to its attributes. However, it is recommended not to reuse [`TextWrapper`](#textwrapper) too
100
+ frequently inside a specific loop, as each attribute has type checking, which may reduce
101
+ performance.
102
+
103
+ <h1></h1>
104
+
105
+ **Attributes of [`TextWrapper`](#textwrapper):**
106
+
107
+ <h1></h1>
108
+
109
+ #### **`width`**
110
+ (Default: `70`) The maximum line length for wrapped text.
111
+
112
+ <h1></h1>
113
+
114
+ #### **`line_padding`**
115
+ (Default: `0`) The spacing between wrapped lines.
116
+
117
+ <h1></h1>
118
+
119
+ #### **`method`**
120
+ (Default: `'word'`) The wrapping method. Available options: `'mono'` and `'word'`.
121
+ - `'mono'` method wraps text character by character.
122
+ - `'word'` method wraps text word by word.
123
+
124
+ <h1></h1>
125
+
126
+ #### **`alignment`**
127
+ (Default: `'left'`) The alignment of the wrapped text. Available options: `'left'`, `'center'`,
128
+ `'right'`, (`'fill'` or `'fill-left'`), `'fill-center'`, and `'fill-right'`.
129
+ - `'left'`: Aligns text to the start of the line.
130
+ - `'center'`: Centers text within the line.
131
+ - `'right'`: Aligns text to the end of the line.
132
+ - `'fill'` or `'fill-left'`: Justifies text across the width but aligns single-word lines or the
133
+ last line (if [`justify_last_line`](#justify_last_line) is `False`) to the left.
134
+ - `'fill-center'` and `'fill-right'` work the same way as `'fill-left'`, aligning text according to
135
+ their respective names.
136
+
137
+ <h1></h1>
138
+
139
+ #### **`fillchar`**
140
+ (Default: `' '`) The character used for padding.
141
+
142
+ <h1></h1>
143
+
144
+ #### **`placeholder`**
145
+ (Default: `'...'`) The ellipsis used for truncating long lines.
146
+
147
+ <h1></h1>
148
+
149
+ #### **`prefix`**
150
+ (Default: `None`) The prefix used for the [`indent`](#indenttext) or [`dedent`](#dedenttext)
151
+ methods.
152
+ - [`indent`](#indenttext) method adds a prefix to the beginning of each line
153
+ (must be of type `str`).
154
+ - [`dedent`](#dedenttext) method removes the prefix from each line:
155
+ - `None` removes leading whitespace.
156
+ - `str` removes the specified character.
157
+ - `Iterable` (e.g., `set`, `list`, `tuple`, etc.) removes multiple specified characters, where
158
+ each must be a single-character `str`.
159
+
160
+ <h1></h1>
161
+
162
+ #### **`separator`**
163
+ (Default: `None`) The character used to separate words.
164
+ - `None`: Uses whitespace as the separator.
165
+ - `str`: Uses the specified character.
166
+ - `Iterable`: Uses multiple specified characters, where each must be a single-character `str`.
167
+
168
+ <h1></h1>
169
+
170
+ #### **`max_lines`**
171
+ (Default: `None`) The maximum number of wrapped lines.
172
+ - `None`: No limit on the number of wrapped lines.
173
+ - `int`: Limits the number of wrapped lines to the specified value. (Ensure that [`width`](#width)
174
+ is not smaller than the length of [`placeholder`](#placeholder)).
175
+
176
+ <h1></h1>
177
+
178
+ #### **`preserve_empty`**
179
+ (Default: `True`) Retains empty lines in the wrapped text.
180
+
181
+ <h1></h1>
182
+
183
+ #### **`use_minimum_width`**
184
+ (Default: `True`) Uses the minimum required line width. Some wrapped lines may be shorter than the
185
+ specified width, so enabling this attribute removes unnecessary empty space.
186
+
187
+ <h1></h1>
188
+
189
+ #### **`justify_last_line`**
190
+ (Default: `False`) Determines whether the last line should also be justified
191
+ (applies only to `fill-...` alignments).
192
+
193
+ <h1></h1>
194
+
195
+ #### **`break_on_hyphens`**
196
+ (Default: `True`) Breaks words at hyphens (-).
197
+ Example: `'self-organization'` becomes `['self-', 'organization']`.
198
+
199
+ <h1></h1>
200
+
201
+ #### **`sizefunc`**
202
+ (Default: `None`) A function used to calculate the width and height of each string. The function
203
+ must return a tuple containing two values:
204
+ - The width and height of the string.
205
+ - Both values must be of type int or float.
206
+
207
+ <h1></h1>
208
+
209
+ #### **`predicate`**
210
+ (Default: `None`) A function used by the [`indent`](#indenttext) or [`dedent`](#dedenttext) methods
211
+ to filter which lines should have a prefix added or removed.
212
+
213
+ <h1></h1>
214
+
215
+ **Methods of [`TextWrapper`](#textwrapper):**
216
+
217
+ <h1></h1>
218
+
219
+ #### **`copy`**
220
+ Creates and returns a copy of the [`TextWrapper`](#textwrapper) object.
221
+
222
+ <h1></h1>
223
+
224
+ #### **`mono(text)`**
225
+ Returns a list of strings, where the text is wrapped per character.
226
+ > Note: Does not support newline characters.
227
+
228
+ For example:
229
+ ```py
230
+ >>> TextWrapper(width=5).mono("Don't Touch My\nPizza!")
231
+ ["Don't", ' Touc', 'h My ', 'Pizza', '!']
232
+ ```
233
+
234
+ <h1></h1>
235
+
236
+ #### **`word(text)`**
237
+ Returns a list of strings, where the text is wrapped per word.
238
+ > Note: Does not support newline characters.
239
+
240
+ For example:
241
+ ```py
242
+ >>> TextWrapper(width=5).word("Don't Touch My\nJelly!")
243
+ ["Don't", 'Touch', 'My', 'Jelly', '!']
244
+ ```
245
+
246
+ <h1></h1>
247
+
248
+ #### **`wrap(text, return_details=False)`**
249
+ Returns a list of wrapped text strings. If `return_details=True`, returns a dictionary containing:
250
+ - `'wrapped'`: A list of wrapped text fragments.
251
+ - `'indiced'`: A set of indices marking line breaks (starting from `0`, like programming indices).
252
+ > Note: Supports newline characters.
253
+
254
+ For example:
255
+ ```py
256
+ >>> TextWrapper(width=15).wrap("I don't like blue cheese\ncuz it is realy smelly!")
257
+ ["I don't like", 'blue cheese', 'cuz it is realy', 'smelly!']
258
+ >>> TextWrapper(width=15).wrap("You touch my pizza your gonna to die!", return_details=True)
259
+ {'wrapped': ['You touch my', 'pizza your', 'gonna to die!'], 'indiced': {2}}
260
+ ```
261
+
262
+ <h1></h1>
263
+
264
+ #### **`align(text, return_details=False)`**
265
+ Returns a list of tuples, where each tuple contains `(x, y, text)`, representing the wrapped text
266
+ along with its coordinates. If `return_details=True`, returns a dictionary containing:
267
+ - `'aligned'`: A list of wrapped text with coordinate data.
268
+ - `'wrapped'`: The result from wrap.
269
+ - `'indiced'`: The indices of line breaks.
270
+ - `'size'`: The calculated text size.
271
+
272
+ For example:
273
+ ```py
274
+ >>> TextWrapper(width=20).align("Bang beli bawang, beli bawang gk pakek kulit")
275
+ [(0, 0, 'Bang beli bawang,'), (0, 1, 'beli bawang gk pakek'), (0, 2, 'kulit')]
276
+ >>> TextWrapper(width=20).align("Bang bawang gk pakek kulit..", return_details=True)
277
+ {'aligned': [(0, 0, 'Bang bawang gk pakek'), (0, 1, 'kulit..')], 'wrapped': ['Bang bawang gk pakek',
278
+ 'kulit..'], 'indiced': {1}, 'size': (20, 2)}
279
+ ```
280
+
281
+ <h1></h1>
282
+
283
+ #### **`fillstr(text)`**
284
+ Returns a string with wrapped text formatted for monospace fonts.
285
+ > Note: [`width`](#width), [`line_padding`](#line_padding), and the output of
286
+ [`sizefunc`](#sizefunc) must return `int`, not `float`!
287
+
288
+ For example:
289
+ ```py
290
+ >>> s = TextWrapper(width=20).fillstr("Tung tung tung tung tung tung sahur")
291
+ >>> s
292
+ 'Tung tung tung tung\ntung tung sahur '
293
+ >>> print(s)
294
+ Tung tung tung tung
295
+ tung tung sahur
296
+ ```
297
+
298
+ <h1></h1>
299
+
300
+ #### **`indent(text)`**
301
+ Returns a string where each line is prefixed with [`prefix`](#prefix).
302
+
303
+ For example:
304
+ ```py
305
+ >>> s = TextWrapper(prefix='> ').indent("Hello\nWorld!")
306
+ '> Hello\n> World!'
307
+ >>> print(s)
308
+ > Hello
309
+ > World!
310
+ ```
311
+
312
+ <h1></h1>
313
+
314
+ #### **`dedent(text)`**
315
+ Returns a string where [`prefix`](#prefix) is removed from the start of each line.
316
+
317
+ For example:
318
+ ```py
319
+ >>> s = TextWrapper(prefix='>>> ').dedent(">>> Hello\n>>> World!")
320
+ >>> s
321
+ 'Hello\nWorld!'
322
+ >>> print(s)
323
+ Hello
324
+ World!
325
+ ```
326
+
327
+ <h1></h1>
328
+
329
+ #### **`shorten(text)`**
330
+ Returns a truncated string if its length exceeds [`width`](#width), appending
331
+ [`placeholder`](#placeholder) at the end if truncated.
332
+
333
+ For example:
334
+ ```py
335
+ >>> TextWrapper(width=20).shorten("This is a very long string that will be shortened")
336
+ 'This is a very lo...'
337
+ ```
338
+
339
+ <h1></h1>
340
+
341
+ **External Functions:**
342
+
343
+ <h1></h1>
344
+
345
+ ### `printwrap`
346
+ ```py
347
+ def printwrap(
348
+ *values: object,
349
+ sep: Optional[str] = ' ',
350
+ end: Optional[str] = '\n',
351
+ wrap: Optional[TextWrapper] = None,
352
+ file: Optional[IO] = None,
353
+ flush: bool = False,
354
+ size: Optional[Union[Tuple[int, Optional[int]], Literal['auto']]] = None,
355
+ default_size: Tuple[int, Optional[int]] = (70, None),
356
+ apply_height: bool = True,
357
+ use_minimum_width: bool = False,
358
+ **kwargs
359
+ ) -> None
360
+ ```
361
+
362
+ [`printwrap`](#printwrap) is a function similar to Python's built-in `print`, but it includes text
363
+ wrapping functionality. It utilizes the [`fillstr`](#fillstrtext) method.
364
+
365
+ #### `*values`
366
+ Arguments to be printed.
367
+
368
+ #### `sep`
369
+ Separator between values.
370
+ > Note: This does not refer to the [`separator`](#separator) attribute in
371
+ [`TextWrapper`](#textwrapper) or [`fillstr`](#fillstrtext).
372
+
373
+ #### `end`
374
+ End character(s) between printed values.
375
+
376
+ #### `wrap`
377
+ An instance of [`TextWrapper`](#textwrapper). If set to `None`, a new [`TextWrapper`](#textwrapper)
378
+ object is created on each call.
379
+
380
+ #### `file`
381
+ The standard `file` argument for Python's `print` function.
382
+
383
+ #### `flush`
384
+ The standard `flush` argument for Python's `print` function.
385
+
386
+ #### `size`
387
+ Defines the width and height of the [`TextWrapper`](#textwrapper).
388
+ - If set to a `tuple`, it specifies the width and height manually. The height can be None, meaning
389
+ no maximum height limit (taken from max_lines).
390
+ - If set to `'auto'` when using a [`TextWrapper`](#textwrapper) instance in wrap, it automatically
391
+ adjusts the following attributes:
392
+ - [`width`](#width)
393
+ - [`line_padding`](#line_padding) (set to `0`)
394
+ - [`max_lines`](#max_lines)
395
+
396
+ #### `default_size`
397
+ The default width and height for wrapping, used when:
398
+ - `os.get_terminal_size()` fails to retrieve terminal dimensions.
399
+ - The [`size`](#size) parameter is invalid.
400
+
401
+ #### `apply_height`
402
+ Determines whether the wrapper should enforce a height limit, even if a height is defined.
403
+
404
+ #### `use_minimum_width, **kwargs`
405
+ Additional arguments passed to external methods of [`fillstr`](#fillstrtext).
406
+
407
+ <h1></h1>
408
+
409
+ ## Another examples❓
410
+
411
+ ### Render a wrap text in PyGame🎮
412
+ ```py
413
+ from typing import Literal, Optional
414
+ from txtwrap import align, LOREM_IPSUM_PARAGRAPHS
415
+ import pygame
416
+
417
+ def render_wrap(
418
+
419
+ font: pygame.Font,
420
+ text: str,
421
+ width: int,
422
+ antialias: bool,
423
+ color: pygame.Color,
424
+ background: Optional[pygame.Color] = None,
425
+ line_padding: int = 0,
426
+ method: Literal['word', 'mono'] = 'word',
427
+ alignment: Literal['left', 'center', 'right', 'fill',
428
+ 'fill-left', 'fill-center', 'fill-right'] = 'left',
429
+ placeholder: str = '...',
430
+ max_lines: Optional[int] = None,
431
+ preserve_empty: bool = True,
432
+ use_minimum_width: bool = True,
433
+ justify_last_line: bool = False,
434
+ break_on_hyphens: bool = True
435
+
436
+ ) -> pygame.Surface:
437
+
438
+ align_info = align(
439
+ text=text,
440
+ width=width,
441
+ line_padding=line_padding,
442
+ method=method,
443
+ alignment=alignment,
444
+ placeholder=placeholder,
445
+ max_lines=max_lines,
446
+ preserve_empty=preserve_empty,
447
+ use_minimum_width=use_minimum_width,
448
+ justify_last_line=justify_last_line,
449
+ break_on_hyphens=break_on_hyphens,
450
+ return_details=True,
451
+ sizefunc=font.size
452
+ )
453
+
454
+ surface = pygame.Surface(align_info['size'], pygame.SRCALPHA)
455
+
456
+ if background is not None:
457
+ surface.fill(background)
458
+
459
+ for x, y, text in align_info['aligned']:
460
+ surface.blit(font.render(text, antialias, color), (x, y))
461
+
462
+ return surface
463
+
464
+ # Example usage:
465
+ pygame.init()
466
+ pygame.display.set_caption("Lorem Ipsum")
467
+
468
+ running = True
469
+ width, height = 800, 600
470
+ screen = pygame.display.set_mode((width, height))
471
+ clock = pygame.time.Clock()
472
+
473
+ surface = render_wrap(
474
+ font=pygame.font.SysFont('Arial', 18),
475
+ text=LOREM_IPSUM_PARAGRAPHS,
476
+ width=width,
477
+ antialias=True,
478
+ color='#ffffff',
479
+ background='#303030',
480
+ alignment='fill'
481
+ )
482
+
483
+ wsurf, hsurf = surface.get_size()
484
+ pos = ((width - wsurf) / 2, (height - hsurf) / 2)
485
+
486
+ while running:
487
+ for event in pygame.event.get():
488
+ if event.type == pygame.QUIT:
489
+ running = False
490
+ screen.fill('#000000')
491
+ screen.blit(surface, pos)
492
+ pygame.display.flip()
493
+ clock.tick(60)
494
+ ```
495
+
496
+ ### Print a wrap text to terminal🔡
497
+ ```py
498
+ from txtwrap import printwrap, LOREM_IPSUM_WORDS
499
+
500
+ width = 20
501
+
502
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='left')
503
+ print('=' * width)
504
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='center')
505
+ print('=' * width)
506
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='right')
507
+ print('=' * width)
508
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='fill') # or alignment='fill-left'
509
+ print('=' * width)
510
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='fill-center')
511
+ print('=' * width)
512
+ printwrap(LOREM_IPSUM_WORDS, size=(width, None), alignment='fill-right')
513
+ ```
514
+
515
+ ### Short a long text🔤
516
+ ```py
517
+ from txtwrap import shorten, LOREM_IPSUM_SENTENCES
518
+
519
+ print(shorten(LOREM_IPSUM_SENTENCES, width=20, placeholder='…'))
520
+ ```
521
+
522
+ ### Bonus🎁 - Print a colorfull text to terminal🔥
523
+ ```py
524
+ # Run this code in a terminal that supports ansi characters
525
+
526
+ from re import compile
527
+ from random import randint
528
+ from txtwrap import printwrap, LOREM_IPSUM_PARAGRAPHS
529
+
530
+ # Set the text to be printed here
531
+ text = LOREM_IPSUM_PARAGRAPHS
532
+
533
+ remove_ansi_re = compile(r'\x1b\[(K|.*?m)').sub
534
+
535
+ def len_no_ansi(s: str):
536
+ return len(remove_ansi_re('', s))
537
+
538
+ while True:
539
+ printwrap(
540
+ ''.join('\x1b[{}m{}'.format(randint(31, 36), char) for char in text),
541
+ end='\x1b[0m\x1b[H\x1b[J',
542
+ alignment='fill',
543
+ lenfunc=len_no_ansi
544
+ )
545
+ ```