Skip to content

Commit 03f985d

Browse files
committed
Implement EML file rendering/raw outputting
1 parent bca187e commit 03f985d

4 files changed

Lines changed: 146 additions & 27 deletions

File tree

README.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
reader
2-
------
1+
## reader
32

43
[![Static
54
Badge](https://img.shields.io/badge/Join_on_Matrix-green?style=for-the-badge&logo=element&logoColor=%23ffffff&label=Chat&labelColor=%23333&color=%230DBD8B&link=https%3A%2F%2Fmatrix.to%2F%23%2F%2521PHlbgZTdrhjkCJrfVY%253Amatrix.org)](https://matrix.to/#/%21PHlbgZTdrhjkCJrfVY%3Amatrix.org)
65

7-
*reader* is for your command line what the “readability” view is for modern
6+
_reader_ is for your command line what the “readability” view is for modern
87
browsers: A lightweight tool offering better readability of web pages on the
98
CLI.
109

1110
![reader](demo.gif)
1211

13-
`reader` parses a web page for its actual content and displays it in nicely
14-
highlighted text on the command line. In addition, `reader` renders embedded
15-
images from that page as colored block-renders on the terminal as well.
12+
`reader` parses a web page (or an EML file) for its actual content and displays
13+
it in nicely highlighted text on the command line. In addition, `reader` renders
14+
embedded images from that page as colored block-renders on the terminal as well.
1615

17-
18-
## Installation
16+
## Installation
1917

2018
```
2119
go install github.com/mrusme/reader@latest
@@ -26,10 +24,9 @@ If the above fails, then the following should work:
2624
```
2725
git clone https://github.com/mrusme/reader.git
2826
cd reader
29-
go install
27+
go install
3028
```
3129

32-
3330
## Usage
3431

3532
```sh
@@ -68,16 +65,26 @@ reader --image-mode sixel https://xn--gckvb8fzb.com/travel-aruba/
6865

6966
![sixel](sixel.png)
7067

68+
Render EML file:
69+
70+
```sh
71+
reader --eml -i none my-email-file.eml
72+
```
73+
74+
Output EML file raw:
75+
76+
```sh
77+
reader --eml --raw my-email-file.eml
78+
```
79+
7180
More options:
7281

7382
```sh
7483
reader -h
7584
```
7685

77-
7886
## Examples
7987

80-
8188
### Using `reader` from within `w3m`
8289

8390
While on a web page in w3m, press `!` and enter the following:
@@ -95,7 +102,6 @@ If you want to navigate through the page:
95102
reader $W3M_URL | less -R
96103
```
97104

98-
99105
### Using `reader` from within `vim`/`neovim`
100106

101107
Add the following function/mapping to your `init.vim`:
@@ -112,6 +118,3 @@ nmap gx <Plug>vertopen_url
112118

113119
Open a document and place the cursor on a link, then press `g` followed by `x`.
114120
Vim will open a new terminal and show you the output of `reader`.
115-
116-
117-

cmd/root.go

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
67
"image"
78
"image/color"
@@ -25,6 +26,7 @@ import (
2526

2627
md "github.com/JohannesKaufmann/html-to-markdown"
2728
mdplug "github.com/JohannesKaufmann/html-to-markdown/plugin"
29+
"github.com/emersion/go-message"
2830
"github.com/spf13/cobra"
2931
"golang.org/x/crypto/ssh/terminal"
3032

@@ -37,6 +39,8 @@ var (
3739
noPretty bool
3840
noReadability bool
3941
noCycleTLS bool
42+
isEML bool
43+
rawOut bool
4044
imageMode string
4145
terminalWidth int
4246
validImageModes = []string{"none", "ansi", "ansi-dither", "kitty", "sixel"}
@@ -47,8 +51,10 @@ type InlineImage struct {
4751
Title string
4852
}
4953

50-
var mdImgRegex = regexp.MustCompile(`(?m)\[{0,1}!\[(:?\]\(.*\)){0,1}(.*)\]\((.+)\)`)
51-
var mdImgPlaceholderRegex = regexp.MustCompile(`(?m)\$\$\$([0-9]*)\$`)
54+
var (
55+
mdImgRegex = regexp.MustCompile(`(?m)\[{0,1}!\[(:?\]\(.*\)){0,1}(.*)\]\((.+)\)`)
56+
mdImgPlaceholderRegex = regexp.MustCompile(`(?m)\$\$\$([0-9]*)\$`)
57+
)
5258

5359
func MakeReadable(rawUrl *string, logger *zap.Logger, cycleTLS bool) (string, string, error) {
5460
var crwlr *crawler.Crawler = crawler.New(logger)
@@ -73,7 +79,93 @@ func MakeReadable(rawUrl *string, logger *zap.Logger, cycleTLS bool) (string, st
7379
return article.Title, article.ContentHtml, nil
7480
}
7581

76-
func HTMLtoMarkdown(html *string) (string, error) {
82+
func ioReaderToString(r io.Reader) (string, error) {
83+
buf := make([]byte, 8)
84+
var text strings.Builder
85+
86+
for {
87+
n, err := r.Read(buf)
88+
if n > 0 {
89+
text.Write(buf[:n])
90+
}
91+
if err == io.EOF {
92+
break
93+
} else if err != nil {
94+
return "", err
95+
}
96+
}
97+
98+
return text.String(), nil
99+
}
100+
101+
func EMLToMarkdown(eml *string, rawOutput bool) (string, error) {
102+
m, err := message.Read(strings.NewReader(*eml))
103+
if message.IsUnknownCharset(err) {
104+
// TODO
105+
} else if err != nil {
106+
return "", err
107+
}
108+
109+
var txt string = ""
110+
if mr := m.MultipartReader(); mr != nil {
111+
var noCT bool = false
112+
113+
for {
114+
p, err := mr.NextPart()
115+
if err == io.EOF {
116+
break
117+
} else if err != nil {
118+
return "", err
119+
}
120+
121+
t, _, _ := p.Header.ContentType()
122+
if t != "text/html" {
123+
noCT = true
124+
continue
125+
}
126+
if txt, err = ioReaderToString(p.Body); err != nil {
127+
return "", err
128+
}
129+
noCT = false
130+
break
131+
}
132+
if noCT {
133+
return "", errors.New(
134+
fmt.Sprintf("Expected text/html content type, found others\n"),
135+
)
136+
}
137+
} else {
138+
t, _, _ := m.Header.ContentType()
139+
if t != "text/html" {
140+
return "", errors.New(
141+
fmt.Sprintf("Expected text/html content type, found: %s\n", t),
142+
)
143+
}
144+
if txt, err = ioReaderToString(m.Body); err != nil {
145+
return "", err
146+
}
147+
}
148+
149+
if rawOutput {
150+
return txt, nil
151+
}
152+
153+
converter := md.NewConverter("", true, nil)
154+
converter.Use(mdplug.GitHubFlavored())
155+
156+
markdown, err := converter.ConvertString(txt)
157+
if err != nil {
158+
return "", err
159+
}
160+
161+
return markdown, nil
162+
}
163+
164+
func HTMLtoMarkdown(html *string, rawOutput bool) (string, error) {
165+
if rawOutput {
166+
return *html, nil
167+
}
168+
77169
converter := md.NewConverter("", true, nil)
78170
converter.Use(mdplug.GitHubFlavored())
79171

@@ -111,7 +203,6 @@ func RenderImg(md string) (string, []InlineImage, error) {
111203
}
112204

113205
func renderImage(img image.Image, imgTitle string, mode string, width int) (string, error) {
114-
115206
switch mode {
116207
case "sixel":
117208
var b bytes.Buffer
@@ -150,16 +241,14 @@ func renderImage(img image.Image, imgTitle string, mode string, width int) (stri
150241
}
151242

152243
func RenderMarkdown(title, markdown string, images []InlineImage, width int) (string, error) {
153-
154244
renderer, _ := glamour.NewTermRenderer(
155245
glamour.WithEnvironmentConfig(),
156246
glamour.WithWordWrap(width),
157247
)
158248

159-
output, err :=
160-
renderer.Render(
161-
fmt.Sprintf("# %s\n\n%s", title, markdown),
162-
)
249+
output, err := renderer.Render(
250+
fmt.Sprintf("# %s\n\n%s", title, markdown),
251+
)
163252
if err != nil {
164253
output = fmt.Sprintf("%v", err)
165254
} else {
@@ -212,7 +301,7 @@ func RenderMarkdown(title, markdown string, images []InlineImage, width int) (st
212301
}
213302

214303
var rootCmd = &cobra.Command{
215-
Use: "reader <url/file/->",
304+
Use: "reader < url/file/- >",
216305
Short: "Reader is a command line web reader",
217306
Long: "A minimal command line reader offering better readability of web " +
218307
"pages on the CLI. [https://github.com/mrusme/reader]",
@@ -250,18 +339,30 @@ var rootCmd = &cobra.Command{
250339
}
251340
}
252341

342+
if isEML {
343+
noReadability = true
344+
}
253345
title, content, err := MakeReadable(&rawUrl, logger, !noCycleTLS)
254346
if err != nil {
255347
fmt.Fprintln(os.Stderr, err)
256348
os.Exit(1)
257349
}
258350

259-
markdown, err := HTMLtoMarkdown(&content)
351+
var markdown string = ""
352+
if isEML {
353+
markdown, err = EMLToMarkdown(&content, rawOut)
354+
} else {
355+
markdown, err = HTMLtoMarkdown(&content, rawOut)
356+
}
260357
if err != nil {
261358
fmt.Fprintln(os.Stderr, err)
262359
os.Exit(1)
263360
}
264361

362+
if rawOut == true {
363+
fmt.Print(markdown)
364+
os.Exit(0)
365+
}
265366
if noPretty == true {
266367
fmt.Printf("# %s\n\n", title)
267368
fmt.Print(markdown)
@@ -301,6 +402,18 @@ func Execute() {
301402
false,
302403
"disable use of CycleTLS",
303404
)
405+
rootCmd.Flags().BoolVar(
406+
&isEML,
407+
"eml",
408+
false,
409+
"input is EML (email) format",
410+
)
411+
rootCmd.Flags().BoolVar(
412+
&rawOut,
413+
"raw",
414+
false,
415+
"output raw text",
416+
)
304417
rootCmd.Flags().BoolVarP(
305418
&verbose,
306419
"verbose",

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require (
3636
github.com/cloudflare/circl v1.6.1 // indirect
3737
github.com/disintegration/imaging v1.6.2 // indirect
3838
github.com/dlclark/regexp2 v1.11.5 // indirect
39+
github.com/emersion/go-message v0.18.2 // indirect
3940
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
4041
github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612 // indirect
4142
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ github.com/dolmen-go/kittyimg v0.0.0-20250509141512-6d6dc00afde0/go.mod h1:2vk7A
9494
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
9595
github.com/eliukblau/pixterm v1.3.2 h1:kAF9qvbaDV3emb9LPHw1Bvd9D5o4y28U0e8Q9vfl24I=
9696
github.com/eliukblau/pixterm v1.3.2/go.mod h1:CgaInx2l92Xo3GTldly4UQeNghSFXmIQNk3zL77Xo/A=
97+
github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
98+
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
9799
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
98100
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
99101
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=

0 commit comments

Comments
 (0)