Skip to content

Commit

Permalink
Allow to download a single page from a PDF (#4434)
Browse files Browse the repository at this point in the history
  • Loading branch information
nono committed Jul 2, 2024
2 parents 8a145e0 + 48c1485 commit b950976
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
6 changes: 6 additions & 0 deletions docs/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,9 @@ Download the file content.
By default the `content-disposition` will be `inline`, but it will be
`attachment` if the query string contains the parameter `Dl=1`

For a PDF file, it's possible to get only a single page by using the `Page`
parameter in the query-string (1 is the first page).

#### Request

```http
Expand All @@ -929,6 +932,9 @@ Download the file content from its path.
By default the `content-disposition` will be `inline`, but it will be
`attachment` if the query string contains the parameter `Dl=1`

For a PDF file, it's possible to get only a single page by using the `Page`
parameter in the query-string (1 is the first page).

#### Request

```http
Expand Down
25 changes: 25 additions & 0 deletions model/vfs/file.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package vfs

import (
"bytes"
"encoding/base64"
"fmt"
"mime"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"

"github.com/cozy/cozy-stack/pkg/config/config"
"github.com/cozy/cozy-stack/pkg/consts"
"github.com/cozy/cozy-stack/pkg/couchdb"
"github.com/labstack/echo/v4"
Expand Down Expand Up @@ -252,6 +255,28 @@ func ServeFileContent(fs VFS, doc *FileDoc, version *Version, filename, disposit
return nil
}

// ServePDFPage replies to an http request with a single page from a PDF file.
func ServePDFPage(fs VFS, doc *FileDoc, disposition string, page int, req *http.Request, w http.ResponseWriter) error {
ext := filepath.Ext(doc.DocName)
basename := strings.TrimSuffix(doc.DocName, ext)
filename := fmt.Sprintf("%s (%d)%s", basename, page, ext)

f, err := fs.OpenFile(doc)
if err != nil {
return err
}
defer f.Close()

extracted, err := config.PDF().ExtractPage(f, page)
if err != nil {
return err
}
content := bytes.NewReader(extracted.Bytes())

http.ServeContent(w, req, filename, doc.UpdatedAt, content)
return nil
}

// ModifyFileMetadata modify the metadata associated to a file. It can
// be used to rename or move the file in the VFS.
func ModifyFileMetadata(fs VFS, olddoc *FileDoc, patch *DocPatch) (*FileDoc, error) {
Expand Down
22 changes: 20 additions & 2 deletions web/files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,16 @@ func ReadFileContentFromIDHandler(c echo.Context) error {
if c.QueryParam("Dl") == "1" {
disposition = "attachment"
}
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())

if page := c.QueryParam("Page"); page != "" {
p, errp := strconv.Atoi(page)
if errp != nil {
return jsonapi.InvalidParameter("Page", errp)
}
err = vfs.ServePDFPage(instance.VFS(), doc, disposition, p, c.Request(), c.Response())
} else {
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
}
if err != nil {
return WrapVfsError(err)
}
Expand Down Expand Up @@ -1115,7 +1124,16 @@ func sendFileFromPath(c echo.Context, path string, checkPermission bool) error {
} else if !checkPermission {
addCSPRuleForDirectLink(c, doc.Class, doc.Mime)
}
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())

if page := c.QueryParam("Page"); page != "" {
p, errp := strconv.Atoi(page)
if errp != nil {
return jsonapi.InvalidParameter("Page", errp)
}
err = vfs.ServePDFPage(instance.VFS(), doc, disposition, p, c.Request(), c.Response())
} else {
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
}
if err != nil {
return WrapVfsError(err)
}
Expand Down

0 comments on commit b950976

Please sign in to comment.