diff --git a/README.md b/README.md index 7651cfa..6d5ea16 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ go install github.com/knipferrc/fm@latest - Open selected file in editor set in EDITOR environment variable (currently only supports GUI editors) - Preview a directory in the secondary pane - Copy selected directory items path to the clipboard +- Read PDF files ## Themes diff --git a/go.mod b/go.mod index 7daea14..1026153 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/charmbracelet/glamour v0.3.0 github.com/charmbracelet/lipgloss v0.4.0 github.com/disintegration/imaging v1.6.2 + github.com/ledongthuc/pdf v0.0.0-20210621053716-e28cb8259002 github.com/lucasb-eyer/go-colorful v1.2.0 github.com/muesli/reflow v0.3.0 github.com/spf13/cobra v1.2.1 diff --git a/go.sum b/go.sum index c5a34de..89df004 100644 --- a/go.sum +++ b/go.sum @@ -320,6 +320,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/ledongthuc/pdf v0.0.0-20210621053716-e28cb8259002 h1:9KI9JpkbCm0b/xSjvNyg9O7G27t+cf+L3um6xv1IbNs= +github.com/ledongthuc/pdf v0.0.0-20210621053716-e28cb8259002/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= diff --git a/internal/pdfdoc/pdfdoc.go b/internal/pdfdoc/pdfdoc.go new file mode 100644 index 0000000..b127bd0 --- /dev/null +++ b/internal/pdfdoc/pdfdoc.go @@ -0,0 +1,62 @@ +package pdfdoc + +import ( + "bytes" + + "github.com/knipferrc/fm/strfmt" + + "github.com/charmbracelet/lipgloss" + "github.com/ledongthuc/pdf" +) + +// Model represents the properties of text. +type Model struct { + Content string + Width int +} + +// ReadPdf reads a PDF file given a name. +func ReadPdf(name string) (string, error) { + f, r, err := pdf.Open(name) + if err != nil { + return "", err + } + + defer f.Close() + + buf := new(bytes.Buffer) + b, err := r.GetPlainText() + + if err != nil { + return "", err + } + + _, err = buf.ReadFrom(b) + if err != nil { + return "", err + } + + return buf.String(), nil +} + +// SetSize sets the size of the pdfdoc. +func (m *Model) SetSize(width int) { + m.Width = width +} + +// SetContent sets the content of the pdfdoc. +func (m *Model) SetContent(content string) { + m.Content = content +} + +// GetContent returns the content of the pdfdoc. +func (m Model) GetContent() string { + return m.Content +} + +// View returns a string representation of pdfdoc. +func (m *Model) View() string { + return lipgloss.NewStyle(). + Width(m.Width). + Render(strfmt.ConvertTabsToSpaces(m.Content)) +} diff --git a/internal/ui/commands.go b/internal/ui/commands.go index 3cf80b0..c095b27 100644 --- a/internal/ui/commands.go +++ b/internal/ui/commands.go @@ -17,6 +17,7 @@ import ( "github.com/knipferrc/fm/dirfs" "github.com/knipferrc/fm/internal/colorimage" "github.com/knipferrc/fm/internal/markdown" + "github.com/knipferrc/fm/internal/pdfdoc" "github.com/knipferrc/fm/internal/text" "github.com/knipferrc/fm/strfmt" "golang.design/x/clipboard" @@ -36,6 +37,7 @@ type readFileContentMsg struct { markdown string code string imageString string + pdfContent string image image.Image } @@ -164,6 +166,7 @@ func (m Model) readFileContentCmd(file os.FileInfo, width int) tea.Cmd { markdown: markdownContent, code: "", imageString: "", + pdfContent: "", image: nil, } case filepath.Ext(file.Name()) == ".png" || filepath.Ext(file.Name()) == ".jpg" || filepath.Ext(file.Name()) == ".jpeg": @@ -184,8 +187,23 @@ func (m Model) readFileContentCmd(file os.FileInfo, width int) tea.Cmd { code: "", markdown: "", imageString: imageString, + pdfContent: "", image: img, } + case filepath.Ext(file.Name()) == ".pdf": + pdfContent, err := pdfdoc.ReadPdf(file.Name()) + if err != nil { + return errorMsg(err.Error()) + } + + return readFileContentMsg{ + rawContent: content, + code: "", + markdown: "", + imageString: "", + pdfContent: pdfContent, + image: nil, + } default: code, err := text.Highlight(content, filepath.Ext(file.Name()), m.appConfig.Settings.SyntaxTheme) if err != nil { @@ -197,6 +215,7 @@ func (m Model) readFileContentCmd(file os.FileInfo, width int) tea.Cmd { code: code, markdown: "", imageString: "", + pdfContent: "", image: nil, } } diff --git a/internal/ui/model.go b/internal/ui/model.go index 97bd5ef..02cefca 100644 --- a/internal/ui/model.go +++ b/internal/ui/model.go @@ -9,6 +9,7 @@ import ( "github.com/knipferrc/fm/internal/dirtree" "github.com/knipferrc/fm/internal/markdown" "github.com/knipferrc/fm/internal/pane" + "github.com/knipferrc/fm/internal/pdfdoc" "github.com/knipferrc/fm/internal/statusbar" "github.com/knipferrc/fm/internal/text" "github.com/knipferrc/fm/internal/theme" @@ -36,6 +37,7 @@ type Model struct { colorimage colorimage.Model markdown markdown.Model text text.Model + pdfdoc pdfdoc.Model itemToMove os.FileInfo appConfig config.Config directoryItemSizeCtx *directoryItemSizeCtx @@ -130,6 +132,7 @@ func NewModel() Model { colorimage: colorimage.Model{}, markdown: markdown.Model{}, text: text.Model{}, + pdfdoc: pdfdoc.Model{}, itemToMove: nil, appConfig: cfg, directoryItemSizeCtx: &directoryItemSizeCtx{ diff --git a/internal/ui/update.go b/internal/ui/update.go index 5518cc5..0984086 100644 --- a/internal/ui/update.go +++ b/internal/ui/update.go @@ -73,7 +73,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.statusBar.ResetCommandBar() m.updateStatusBarContent() - return m, m.getDirectoryItemSizeCmd(m.dirTree.GetSelectedFile().Name()) + if len(msg) > 0 { + return m, m.getDirectoryItemSizeCmd(m.dirTree.GetSelectedFile().Name()) + } + + return m, nil // previewDirectoryListingMsg shows a preview of a directory in the secondary pane. case previewDirectoryListingMsg: @@ -116,19 +120,30 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.markdown.SetContent("") m.dirTreePreview.SetContent(nil) m.text.SetContent(msg.code) + m.pdfdoc.SetContent("") m.secondaryPane.SetContent(m.text.View()) + case msg.pdfContent != "": + m.secondaryPane.GotoTop() + m.colorimage.SetImage(nil) + m.markdown.SetContent("") + m.dirTreePreview.SetContent(nil) + m.text.SetContent("") + m.pdfdoc.SetContent(msg.pdfContent) + m.secondaryPane.SetContent(m.pdfdoc.View()) case msg.markdown != "": m.secondaryPane.GotoTop() m.colorimage.SetImage(nil) m.text.SetContent("") m.dirTreePreview.SetContent(nil) m.markdown.SetContent(msg.markdown) + m.pdfdoc.SetContent("") m.secondaryPane.SetContent(m.markdown.View()) case msg.image != nil: m.secondaryPane.GotoTop() m.markdown.SetContent("") m.text.SetContent("") m.dirTreePreview.SetContent(nil) + m.pdfdoc.SetContent("") m.colorimage.SetImage(msg.image) m.colorimage.SetContent(msg.imageString) m.secondaryPane.SetContent(m.colorimage.View()) @@ -180,6 +195,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.text.SetSize(m.secondaryPane.GetWidth() - m.secondaryPane.GetHorizontalFrameSize()) m.markdown.SetSize(m.secondaryPane.GetWidth() - m.secondaryPane.GetHorizontalFrameSize()) m.colorimage.SetSize(m.secondaryPane.GetWidth() - m.secondaryPane.GetHorizontalFrameSize()) + m.pdfdoc.SetSize(m.secondaryPane.GetWidth() - m.secondaryPane.GetHorizontalFrameSize()) m.primaryPane.SetContent(m.dirTree.View()) m.help.Width = msg.Width @@ -192,7 +208,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.secondaryPane.SetContent(m.text.View()) case m.dirTreePreview.GetTotalFiles() != 0: m.secondaryPane.SetContent(m.dirTreePreview.View()) - case m.text.GetContent() == "" && m.markdown.GetContent() == "" && m.colorimage.GetImage() == nil && m.dirTreePreview.GetTotalFiles() == 0: + case m.pdfdoc.GetContent() != "": + m.secondaryPane.SetContent(m.pdfdoc.View()) + case m.text.GetContent() == "" && m.markdown.GetContent() == "" && m.colorimage.GetImage() == nil && m.dirTreePreview.GetTotalFiles() == 0 && m.pdfdoc.GetContent() == "": m.secondaryPane.SetContent(lipgloss.NewStyle(). Width(m.secondaryPane.GetWidth() - m.secondaryPane.GetHorizontalFrameSize()). Render(m.help.View(m.keys)), @@ -596,6 +614,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.colorimage.SetImage(nil) m.markdown.SetContent("") m.text.SetContent("") + m.pdfdoc.SetContent("") m.dirTreePreview.SetContent(nil) m.updateStatusBarContent() }