Ë Ä8‚^ÄT0ãój—dZddlZddlmZmZmZmZddlmZddl m Z m Z m Z dZ d d„ZGd„d e«Zy) zLConvert HTML with simple text formatting to text with ANSI escape sequences.éN)Ú HTMLParserÚStringIOÚname2codepointÚunichr)Úcompact_empty_lines)ÚANSI_COLOR_CODESÚ ANSI_RESETÚ ansi_style)Ú HTMLConverterÚ html_to_ansicó*—t|¬«}||«S)aÊ Convert HTML with simple text formatting to text with ANSI escape sequences. :param data: The HTML to convert (a string). :param callback: Optional callback to pass to :class:`HTMLConverter`. :returns: Text with ANSI escape sequences (a string). Please refer to the documentation of the :class:`HTMLConverter` class for details about the conversion process (like which tags are supported) and an example with a screenshot. )Úcallback)r )ÚdatarÚ converters ú@/usr/lib/python3.12/site-packages/humanfriendly/terminal/html.pyr r s€ô xÔ0€IÙ �T‹?Ðócó‚—eZdZdZdZ d„Zd„Zed„«Zd„Z dd„Z d „Z d „Z d „Z d „Zd „Zd„Zd„Zd„Zd„Zd„Zd„Zy)r aˆ Convert HTML with simple text formatting to text with ANSI escape sequences. The following text styles are supported: - Bold: ````, ```` and ```` - Italic: ````, ```` and ```` - Strike-through: ````, ```` and ```` - Underline: ````, ```` and ```` Colors can be specified as follows: - Foreground color: ```` - Background color: ```` Here's a small demonstration: .. code-block:: python from humanfriendly.text import dedent from humanfriendly.terminal import html_to_ansi print(html_to_ansi(dedent(''' Hello world! Is this thing on? I guess I can underline or strike-through text? And what about color? '''))) rainbow_colors = [ '#FF0000', '#E2571E', '#FF7F00', '#FFFF00', '#00FF00', '#96BF33', '#0000FF', '#4B0082', '#8B00FF', '#FFFFFF', ] html_rainbow = "".join('o' % c for c in rainbow_colors) print(html_to_ansi("Let's try a rainbow: %s" % html_rainbow)) Here's what the results look like: .. image:: images/html-to-ansi.png Some more details: - Nested tags are supported, within reasonable limits. - Text in ```` and ``
`` tags will be highlighted in a
      different color from the main text (currently this is yellow).

    - ``TEXT`` is converted to the format "TEXT (URL)" where
      the uppercase symbols are highlighted in light blue with an underline.

    - ``
``, ``

`` and ``

`` tags are considered block level tags
      and are wrapped in vertical whitespace to prevent their content from
      "running into" surrounding text. This may cause runs of multiple empty
      lines to be emitted. As a *workaround* the :func:`__call__()` method
      will automatically call :func:`.compact_empty_lines()` on the generated
      output before returning it to the caller. Of course this won't work
      when `output` is set to something like :data:`sys.stdout`.

    - ``
`` is converted to a single plain text line break. Implementation notes: - A list of dictionaries with style information is used as a stack where new styling can be pushed and a pop will restore the previous styling. When new styling is pushed, it is merged with (but overrides) the current styling. - If you're going to be converting a lot of HTML it might be useful from a performance standpoint to re-use an existing :class:`HTMLConverter` object for unrelated HTML fragments, in this case take a look at the :func:`__call__()` method (it makes this use case very easy). .. versionadded:: 4.15 :class:`humanfriendly.terminal.HTMLConverter` was added to the `humanfriendly` package during the initial development of my new `chat-archive `_ project, whose command line interface makes for a great demonstration of the flexibility that this feature provides (hint: check out how the search keyword highlighting combines with the regular highlighting). )ÚdivÚpÚprecó’—|jdd«|_|jdd«|_tj|g|¢­i|¤Žy)a� Initialize an :class:`HTMLConverter` object. :param callback: Optional keyword argument to specify a function that will be called to process text fragments before they are emitted on the output stream. Note that link text and preformatted text fragments are not processed by this callback. :param output: Optional keyword argument to redirect the output to the given file-like object. If this is not given a new :class:`~python3:io.StringIO` object is created. rNÚoutput)ÚpoprrrÚ__init__)ÚselfÚargsÚkws rrzHTMLConverter.__init__{s@€ðŸ™˜z¨4Ó0ˆŒ Ø—f‘f˜X tÓ,ˆŒ ä×јDÐ. 4Ò.¨2Ó.rcóà—|j«|j|«|j«t|jt «r#t |jj««Sy)a Reset the parser, convert some HTML and get the text with ANSI escape sequences. :param data: The HTML to convert to text (a string). :returns: The converted text (only in case `output` is a :class:`~python3:io.StringIO` object). N)ÚresetÚfeedÚcloseÚ isinstancerrrÚgetvalue©rrs rÚ__call__zHTMLConverter.__call__ŽsL€ð � ‰ Œ Ø � ‰ �$ŒØ � ‰ Œ Ü �d—k‘k¤8Ô ,Ü& t§{¡{×';Ñ';Ó'=Ó>Ð >ð -rcó<—|jr|jdSiS)z?Get the current style from the top of the stack (a dictionary).éÿÿÿÿ)Ústack©rs rÚ current_stylezHTMLConverter.current_styleœs€ð"&§¢ˆt�z‰z˜"‰~Ð3°Ð3rcó¤—t|j«r&|jjt«g|_t j |«y)ab Close previously opened ANSI escape sequences. This method overrides the same method in the superclass to ensure that an :data:`.ANSI_RESET` code is emitted when parsing reaches the end of the input but a style is still active. This is intended to prevent malformed HTML from messing up terminal output. N)Úanyr(rÚwriter rr!r)s rr!zHTMLConverter.close¡s6€ô ˆt�z‰zŒ?Ø �K‰K× Ñ œjÔ )؈DŒJÜ×јÕrNcó°—|jjt«|€ |jn|}|r%|jjt di|¤Ž«yy)a) Emit an ANSI escape sequence for the given or current style to the output stream. :param style: A dictionary with arguments for :func:`.ansi_style()` or :data:`None`, in which case the style at the top of the stack is emitted. N©)rr-r r*r )rÚstyles rÚ emit_stylezHTMLConverter.emit_style¯sI€ð � ‰ ×Ñœ*Ô%à&+ m�×"Ò"¸ˆÙ Ø �K‰K× Ñ œjÑ1¨5Ñ1Õ 2ð rc ó°—|jjt|jd«rt |ddd«««yt |«««y)z“ Process a decimal or hexadecimal numeric character reference. :param value: The decimal or hexadecimal value (a string). ÚxéNé)rr-rÚ startswithÚint)rÚvalues rÚhandle_charrefzHTMLConverter.handle_charref¾sD€ð � ‰ ×Ñœ&°u×7GÑ7GÈÔ7L¤ U¨1¨2 Y°Ó!3Ó]Õ^ÔRUÐV[ÓR\Ó]Õ^rcóº—|jr||_n,|jr |jdk(r|j|«}|jj |«y)zZ Process textual data. :param data: The decoded text (a string). rN)Úlink_urlÚ link_textrÚpreformatted_text_levelrr-r$s rÚ handle_datazHTMLConverter.handle_dataÆsJ€ð �=Š=ð"ˆD�NØ �]Š]˜t×;Ñ;¸qÒ@ð—=‘= Ó&ˆDà � ‰ ×ј$Õrcóê—|dv�rD|j}|jr|jjd«|j}|dk(rÖ|j|j|j «r|j |«n¯|j |«|jjd«|j |«|jj|j|j ««|j |«|jjd«n|j |«|dvr|xjdzc_ ||jvr|jjd«y y ) zf Process the end of an HTML tag. :param tag: The name of the tag (a string). ) ÚaÚbÚcodeÚdelÚemÚiÚinsrÚsÚstrongÚspanÚur'r@z (Ú))rBrr4ú N) r*r(rÚ urls_matchr<r;r1rr-Ú render_urlr=Ú BLOCK_TAGS)rÚtagÚ old_styleÚ new_styles rÚ handle_endtagzHTMLConverter.handle_endtagØs€ð Ð`Ò `Ø×*Ñ*ˆIð�zŠzØ— ‘ —‘˜rÔ"Ø×*Ñ*ˆIØ�cŠzØ—?‘? 4§>¡>°4·=±=ÔAà—O‘O IÕ.à—O‘O IÔ.Ø—K‘K×%Ñ% dÔ+Ø—O‘O IÔ.Ø—K‘K×%Ñ% d§o¡o°d·m±mÓ&DÔEØ—O‘O IÔ.Ø—K‘K×%Ñ% cÕ*à—‘  Ô*Ø�oÑ%Ø×,Ò,°Ñ1Õ,Ø �$—/‘/Ñ !à �K‰K× Ñ ˜fÕ %ð "rcóZ—|jjtt|««y)z| Process a named character reference. :param name: The name of the character reference (a string). N)rr-rr)rÚnames rÚhandle_entityrefzHTMLConverter.handle_entityreføs €ð � ‰ ×Ñœ&¤°Ñ!5Ó6Õ7rcó"—||jvr|jjd«|dk(r-|jddd¬«t d„|D«d«|_y*|dk(s|d k(r|jd¬ «y*|d k(r|jjd «y*|d k(s|dk(r(|jd¬«|xj dz c_y*|dk(s|dk(r|jd¬«y*|dk(s|dk(r|jd¬«y*|dk(s|dk(r|jd¬«y*|dk(rèi}t d„|D«d«}|jd«D]¬}|jd«\}}}|j«}|j«}|dk(r|j|«|d <ŒR|d!k(r|j|«|d!<Œl|d"k(r |d#k(rd|d#<Œ||d$k(r |d%k(rd|d%<ŒŒ|d&k(r |d'k(rd|d(<Œœ|d&k(sŒ¢|d)k(sŒ¨d|d)<Œ®|jd+i|¤Žy*y*),z¦ Process the start of an HTML tag. :param tag: The name of the tag (a string). :param attrs: A list of tuples with two strings each. rLr@ÚblueT)ÚcolorÚbrightÚ underlinec3ó2K—|]\}}|dk(sŒ |–—Œy­w)ÚhrefNr/©Ú.0ÚnÚvs rÚ z0HTMLConverter.handle_starttag..sèø€Ð!E±©¨¨1¸¸f»¤!±ùó‚ �ÚrArH)ÚboldÚbrÚ rBrÚyellow)rYr4rCrG)Ústrike_throughrDrE)ÚitalicrFrJ)r[rIc3ó2K—|]\}}|dk(sŒ |–—Œy­w)r0Nr/r^s rrbz0HTMLConverter.handle_starttag.. sèø€Ð<¡e™d˜a ¨q°G«|œ¡eùrcÚ;Ú:zbackground-colorÚ backgroundrYz font-stylerjz font-weightreztext-decorationz line-throughrir[Nr/) rOrr-Ú push_stylesÚnextr;r=ÚsplitÚ partitionÚstripÚ parse_color) rrPÚattrsÚstylesÚcssÚrulerUÚ_r8s rÚhandle_starttagzHTMLConverter.handle_starttags&€ð �$—/‘/Ñ !à �K‰K× Ñ ˜fÔ %Ø �#Š:Ø × Ñ  6°$À$Ð Ô Gô !Ñ!E±Ó!EÀrÓJˆD�MØ �CŠZ˜3 (š?Ø × Ñ  $Ð Õ 'Ø �DŠ[Ø �K‰K× Ñ ˜dÕ #Ø �FŠ]˜c UšlØ × Ñ  8Ð Ô ,Ø × (Ò (¨AÑ -Ö (Ø �EŠ\˜S CšZØ × Ñ ¨DÐ Õ 1Ø �DŠ[˜C 3šJØ × Ñ  DÐ Õ )Ø �EŠ\˜S CšZØ × Ñ  tÐ Õ ,Ø �FŠ]؈FÜÑ<¡eÓ<¸bÓAˆCØŸ ™  #ž�Ø!%§¡°Ó!4‘��a˜Ø—z‘z“|�ØŸ ™ › �ØÐ-Ò-Ø+/×+;Ñ+;¸EÓ+B�F˜<Ò(ؘW’_Ø&*×&6Ñ&6°uÓ&=�F˜7’Oؘ\Ò)¨e°xÒ.?Ø'+�F˜8Ò$ؘ]Ò*¨u¸ªØ%)�F˜6’NØÐ.Ò.°5¸NÒ3JØ/3�FÐ+Ò,ØÐ.Ó.°5¸KÓ3GØ*.�F˜;Ò'ð'ð ˆD× Ñ Ñ &˜vÓ &ð'rcó0—tjdd|«S)z® Normalize a URL to enable string equality comparison. :param url: The URL to normalize (a string). :returns: The normalized URL (a string). ú^mailto:rd©ÚreÚsub©rÚurls rÚ normalize_urlzHTMLConverter.normalize_url3s€ô�v‰v�j " cÓ*Ð*rcóÚ—|jd«r=tjd|«}t|«dk(rœt t t |««S|jd«rr|dd}t|«}|dk(r,t |ddd «t |dd d «t |d dd «fS|dk(r,t |d d «t |dd «t |dd «fS|j«}|tvr|Sy) zþ Convert a CSS color to something that :func:`.ansi_style()` understands. :param value: A string like ``rgb(1,2,3)``, ``#AABBCC`` or ``yellow``. :returns: A color value supported by :func:`.ansi_style()` or :data:`None`. Úrgbz\d+éÚ#r4Néér5ér) r6r~ÚfindallÚlenÚtupleÚmapr7Úlowerr)rr8ÚtokensÚlengths rrtzHTMLConverter.parse_color<s€ð × Ñ ˜EÔ "Ü—Z‘Z ¨Ó.ˆFÜ�6‹{˜aÒÜœS¤ fÓ-Ó.Ð.à × Ñ ˜cÔ "ؘ!˜"�IˆEܘ“ZˆFؘŠ{ô˜˜b˜q˜  2Ó&ܘ˜a ˜  BÓ'ܘ˜a ˜  BÓ'ððð ˜1’ô˜˜a™ "Ó%ܘ˜a™ "Ó%ܘ˜a™ "Ó%ððð — ‘ “ ˆØ Ô$Ñ $؈Lð %rc ó¶—|j}|rt|«}|j|«n|}|jj |«|j |«y)aý Push new style information onto the stack. :param changes: Any keyword arguments are passed on to :func:`.ansi_style()`. This method is a helper for :func:`handle_starttag()` that does the following: 1. Make a copy of the current styles (from the top of the stack), 2. Apply the given `changes` to the copy of the current styles, 3. Add the new styles to the stack, 4. Emit the appropriate ANSI escape sequence to the output stream. N)r*ÚdictÚupdater(Úappendr1)rÚchangesÚ prototyperRs rrozHTMLConverter.push_styles_sL€ð×&Ñ&ˆ ٠ܘY›ˆIØ × Ñ ˜WÕ %àˆIØ � ‰ ×ј)Ô$Ø �‰˜ Õ"rcó�—tjdd|«}tjdd|«}tjdd|«}|S)a± Prepare a URL for rendering on the terminal. :param url: The URL to simplify (a string). :returns: The simplified URL (a string). This method pre-processes a URL before rendering on the terminal. The following modifications are made: - The ``mailto:`` prefix is stripped. - Spaces are converted to ``%20``. - A trailing parenthesis is converted to ``%29``. r|rdÚ z%20z\)$z%29r}r€s rrNzHTMLConverter.render_urlvs?€ô�f‰f�Z  SÓ)ˆÜ�f‰f�S˜% Ó%ˆÜ�f‰f�V˜U CÓ(ˆØˆ rcóЗtj|«d|_d|_d|_|j �t |j t«rt«|_g|_y)zÕ Reset the state of the HTML parser and ANSI converter. When `output` is a :class:`~python3:io.StringIO` object a new instance will be created (and the old one garbage collected). Nr) rrr<r;r=rr"rr(r)s rrzHTMLConverter.reset‰sQ€ô ×јÔàˆŒØˆŒ Ø'(ˆÔ$Ø �;‰;Ð ¤*¨T¯[©[¼(Ô"Cô#›*ˆDŒK؈� rcóH—|j|«|j|«k(S)a” Compare two URLs for equality using :func:`normalize_url()`. :param a: A string containing a URL. :param b: A string containing a URL. :returns: :data:`True` if the URLs are the same, :data:`False` otherwise. This method is used by :func:`handle_endtag()` to omit the URL of a hyperlink (````) when the link text is that same URL. )r‚)rr@rAs rrMzHTMLConverter.urls_matchœs%€ð×!Ñ! !Ó$¨×(:Ñ(:¸1Ó(=Ñ=Ð=r©N)Ú__name__Ú __module__Ú __qualname__Ú__doc__rOrr%Úpropertyr*r!r1r9r>rSrVrzr‚rtrorNrrMr/rrr r %sv„ñOðb%€JØEò/ò& ?ðñ4óð4ò ó 3ò_ò ò$&ò@8ò1'òf+ò!òF#ò.ò&ó& >rr r›)rŸr~Úhumanfriendly.compatrrrrÚhumanfriendly.textrÚhumanfriendly.terminalrr r Ú__all__r r r/rrÚr¥s<ðñSó ÷NÓMÝ2ßKÑKð ,€ó ô B>�JõB>r