support for timg as an alternative to chafa

This commit is contained in:
Lionel Dricot 2022-03-17 10:18:00 +01:00
parent a651537052
commit 435ab36525
3 changed files with 72 additions and 57 deletions

View File

@ -1,14 +1,16 @@
# Offpunk History # Offpunk History
## 1.1 - Unreleased ## 1.1 - Unreleased
- Perfect rendering of pictures with chafa 1.8+ and compatible terminal - Perfect rendering of pictures with chafa 1.8+ and compatible terminal (Kitty)
- timg is supported as an alternative to chafa (with a little glitch)
- "cp cache" put the path of the cached content in clipboard - "cp cache" put the path of the cached content in clipboard
- "cp url X" will copy the URL of link X (suggested by Eoin Carney) - "cp url X" will copy the URL of link X (suggested by Eoin Carney)
- HTML renderering of <pre> has been improved - HTML renderering of <pre> has been improved
- "fold" has been removed as it doesnt work well anyway with our width. - "fold" has been removed as it doesnt work well and can be replaced with "!fold".
- Improved clipboard URL detection an fixed crash when binary in clipboard - Improved clipboard URL detection an fixed crash when binary in clipboard
- Fixed crash when chafa is not installed (Thanks Xavier Hinault for the report) - Fixed crash when chafa is not installed (Thanks Xavier Hinault for the report)
- Fixed crash when python-readability not installed (Thanks Nic for the report) - Fixed crash when python-readability not installed (Thanks Nic for the report)
- Fixed some URL being wronlgy interpreted as IPv6
## 1.0 - March 14th 2022 ## 1.0 - March 14th 2022
- Default width is now the standard 72 - Default width is now the standard 72

View File

@ -67,7 +67,7 @@ Announces about Offpunk will be made on Ploums Gemlog => gemini://rawtext.cl
Offpunk has no "strict dependencies", i.e. it should run and work without anything Offpunk has no "strict dependencies", i.e. it should run and work without anything
else beyond the Python standard library and the "less" pager. However, it will "opportunistically else beyond the Python standard library and the "less" pager. However, it will "opportunistically
import" a few other libraries if they are available to offer an improved import" a few other libraries if they are available to offer an improved
experience or some other features. Python libraries requests, bs4 and readability are required for http/html support. Images are displayed if python-ansiwrap and chafa are presents (python-pil is needed for chafa version before 1.10). experience or some other features. Python libraries requests, bs4 and readability are required for http/html support. Images are displayed if python-ansiwrap and chafa or timg are presents (python-pil is needed for chafa version before 1.10). When displaying only a picture (not inline), rendering will be pixel perfect in compatible terminals (such as Kitty) if chafa is at least version 1.8 or if timg is used.
To avoid using unstable or too recent libraries, the rule of thumb is that a library should be packaged in Debian/Ubuntu. Keep in mind that Offpunk is mainly tested will all libraries installed. If you encounter a crash without one optional dependencies, please report it. To avoid using unstable or too recent libraries, the rule of thumb is that a library should be packaged in Debian/Ubuntu. Keep in mind that Offpunk is mainly tested will all libraries installed. If you encounter a crash without one optional dependencies, please report it.
@ -86,6 +86,7 @@ Run command `version` in offpunk to see if you are missing some dependencies.
* [Python editor](https://github.com/fmoo/python-editor) is used to edit your lists with "list edit". (apt-get install python3-editor) * [Python editor](https://github.com/fmoo/python-editor) is used to edit your lists with "list edit". (apt-get install python3-editor)
* [Xsel](http://www.vergenet.net/~conrad/software/xsel/) allows to `go` to the URL copied in the clipboard without having to paste it (both X and traditional clipboards are supported). Also needed to use the `copy` command. (apt-get install xsel) * [Xsel](http://www.vergenet.net/~conrad/software/xsel/) allows to `go` to the URL copied in the clipboard without having to paste it (both X and traditional clipboards are supported). Also needed to use the `copy` command. (apt-get install xsel)
* [Chafa](https://hpjansson.org/chafa/) allows to display pictures in your console. Install it and browse to an HTML page with picture to see the magic. * [Chafa](https://hpjansson.org/chafa/) allows to display pictures in your console. Install it and browse to an HTML page with picture to see the magic.
* [Timg](https://github.com/hzeller/timg) is a slower alternative to chafa for inline images. But it has better rendering when displaying only the image. Install both to get the best of both world.
* [Python-pil](http://python-pillow.github.io/) is required to only display the first frame of animated gif with chafa if chafa version is lower than 1.10. * [Python-pil](http://python-pillow.github.io/) is required to only display the first frame of animated gif with chafa if chafa version is lower than 1.10.
* [Python-setproctitle](https://github.com/dvarrazzo/py-setproctitle) will change the process name from "python" to "offpunk". Useful to kill it without killing every python service. * [Python-setproctitle](https://github.com/dvarrazzo/py-setproctitle) will change the process name from "python" to "offpunk". Useful to kill it without killing every python service.

View File

@ -109,50 +109,73 @@ def wraplines(*args,**kwargs):
def wrapparagraph(*args,**kwargs): def wrapparagraph(*args,**kwargs):
return "\n".join(wraplines(*args,**kwargs)) return "\n".join(wraplines(*args,**kwargs))
_HAS_PIL = False try:
_RENDER_IMAGE = False from PIL import Image
_HAS_TIMG = False _HAS_PIL = True
#_HAS_TIMG = shutil.which('timg') except ModuleNotFoundError:
if _HAS_TIMG: _HAS_PIL = False
chafa_inline = "timg --frames=1 -p q -g %sx1000" _HAS_TIMG = shutil.which('timg')
chafa_cmd = "timg --frames=1 -C"
if _HAS_ANSIWRAP:
_RENDER_IMAGE = True
_HAS_CHAFA = shutil.which('chafa') _HAS_CHAFA = shutil.which('chafa')
if not _RENDER_IMAGE: _NEW_CHAFA = False
if _HAS_CHAFA:
# starting with 1.10, chafa can return only one frame
# which allows us to drop dependancy for PIL
return_code = subprocess.run("chafa --version",shell=True, capture_output=True)
output = return_code.stdout.decode()
# with chafa < 1.10, --version was returned to stderr instead of stdout.
if output == '':
_NEW_CHAFA = False
chafa_inline = "chafa --bg white -s %s -w 1 -f symbols"
chafa_cmd = "chafa --bg white -w 1"
else:
_NEW_CHAFA = True
chafa_inline += "--animate=off "
else:
_NEW_CHAFA = False
if _NEW_CHAFA and _HAS_ANSIWRAP: # All this code to know if we render image inline or not
_RENDER_IMAGE = True if _HAS_CHAFA:
else: # starting with 1.10, chafa can return only one frame
# which allows us to drop dependancy for PIL
return_code = subprocess.run("chafa --version",shell=True, capture_output=True)
output = return_code.stdout.decode()
# with chafa < 1.10, --version was returned to stderr instead of stdout.
if output != '':
_NEW_CHAFA = True
if _NEW_CHAFA and _HAS_ANSIWRAP:
_RENDER_IMAGE = True
elif _HAS_TIMG and _HAS_ANSIWRAP:
_RENDER_IMAGE = True
elif _HAS_CHAFA and _HAS_PIL and _HAS_ANSIWRAP:
_RENDER_IMAGE = True
else:
_RENDER_IMAGE = False
print("To render images inline, you need either chafa or timg and ansiwrap.")
if not _NEW_CHAFA and not _HAS_TIMG:
print("Before Chafa 1.10, you also need python-pil")
#return ANSItext that can be show by less
def inline_image(img_file,width):
#Chafa is faster than timg inline. Let use that one by default
inline = None
ansi_img = ""
if _HAS_CHAFA and _HAS_ANSIWRAP:
if _HAS_PIL and not _NEW_CHAFA:
# this code is a hack to remove frames from animated gif
img_obj = Image.open(img_file)
if hasattr(img_obj,"n_frames") and img_obj.n_frames > 1:
# we remove all frames but the first one
img_obj.save(img_file,save_all=False)
inline = "chafa --bg white -s %s -w 1 -f symbols"
elif _NEW_CHAFA:
inline = "chafa --bg white -s %s -w 1 -f symbols --animate=off"
if not inline and _HAS_TIMG and _HAS_ANSIWRAP:
inline = "timg --frames=1 -p q -g %sx1000"
if inline:
cmd = inline%width+ " \"%s\""%img_file
try: try:
from PIL import Image return_code = subprocess.run(cmd,shell=True, capture_output=True)
_HAS_PIL = True ansi_img = return_code.stdout.decode()
if _HAS_ANSIWRAP and _HAS_CHAFA: except Exception as err:
_RENDER_IMAGE = True ansi_img = "***image failed : %s***\n" %err
else: return ansi_img
print("chafa and ansiwrap are required to render images in terminal")
_RENDER_IMAGE = False def terminal_image(img_file):
except ModuleNotFoundError: #Render by timg is better than old chafa.
print("python-pil, chafa and ansiwrap are required to render images") # it is also centered
_RENDER_IMAGE = False cmd = None
_HAS_PIL = False if _HAS_TIMG:
cmd = "timg --frames=1 -C"
elif _HAS_CHAFA:
cmd = "chafa --bg white -w 1"
if cmd:
cmd = cmd + " \"%s\""%img_file
subprocess.run(cmd,shell=True)
_HAS_XSEL = shutil.which('xsel') _HAS_XSEL = shutil.which('xsel')
@ -741,17 +764,7 @@ class ImageRenderer(AbstractRenderer):
spaces = 0 spaces = 0
else: else:
spaces = int((term_width() - width)//2) spaces = int((term_width() - width)//2)
try: ansi_img = inline_image(img,width)
if _HAS_PIL:
img_obj = Image.open(img)
if hasattr(img_obj,"n_frames") and img_obj.n_frames > 1:
# we remove all frames but the first one
img_obj.save(img,save_all=False)
cmd = chafa_inline%width+ " \"%s\""%img
return_code = subprocess.run(cmd,shell=True, capture_output=True)
ansi_img = return_code.stdout.decode()
except Exception as err:
ansi_img = "***image failed : %s***\n" %err
#Now centering the image #Now centering the image
lines = ansi_img.splitlines() lines = ansi_img.splitlines()
new_img = "" new_img = ""
@ -761,8 +774,7 @@ class ImageRenderer(AbstractRenderer):
def display(self,mode=None,title=None): def display(self,mode=None,title=None):
if title: if title:
print(title) print(title)
cmd = chafa_cmd + " \"%s\""%self.body terminal_image(self.body)
subprocess.run(cmd,shell=True)
return True return True
class HtmlRenderer(AbstractRenderer): class HtmlRenderer(AbstractRenderer):