Skip to content

Commit b950976

Browse files
authored
Allow to download a single page from a PDF (#4434)
2 parents 8a145e0 + 48c1485 commit b950976

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

docs/files.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ Download the file content.
905905
By default the `content-disposition` will be `inline`, but it will be
906906
`attachment` if the query string contains the parameter `Dl=1`
907907

908+
For a PDF file, it's possible to get only a single page by using the `Page`
909+
parameter in the query-string (1 is the first page).
910+
908911
#### Request
909912

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

935+
For a PDF file, it's possible to get only a single page by using the `Page`
936+
parameter in the query-string (1 is the first page).
937+
932938
#### Request
933939

934940
```http

model/vfs/file.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package vfs
22

33
import (
4+
"bytes"
45
"encoding/base64"
56
"fmt"
67
"mime"
78
"net/http"
89
"os"
910
"path"
11+
"path/filepath"
1012
"strings"
1113
"time"
1214

15+
"github.com/cozy/cozy-stack/pkg/config/config"
1316
"github.com/cozy/cozy-stack/pkg/consts"
1417
"github.com/cozy/cozy-stack/pkg/couchdb"
1518
"github.com/labstack/echo/v4"
@@ -252,6 +255,28 @@ func ServeFileContent(fs VFS, doc *FileDoc, version *Version, filename, disposit
252255
return nil
253256
}
254257

258+
// ServePDFPage replies to an http request with a single page from a PDF file.
259+
func ServePDFPage(fs VFS, doc *FileDoc, disposition string, page int, req *http.Request, w http.ResponseWriter) error {
260+
ext := filepath.Ext(doc.DocName)
261+
basename := strings.TrimSuffix(doc.DocName, ext)
262+
filename := fmt.Sprintf("%s (%d)%s", basename, page, ext)
263+
264+
f, err := fs.OpenFile(doc)
265+
if err != nil {
266+
return err
267+
}
268+
defer f.Close()
269+
270+
extracted, err := config.PDF().ExtractPage(f, page)
271+
if err != nil {
272+
return err
273+
}
274+
content := bytes.NewReader(extracted.Bytes())
275+
276+
http.ServeContent(w, req, filename, doc.UpdatedAt, content)
277+
return nil
278+
}
279+
255280
// ModifyFileMetadata modify the metadata associated to a file. It can
256281
// be used to rename or move the file in the VFS.
257282
func ModifyFileMetadata(fs VFS, olddoc *FileDoc, patch *DocPatch) (*FileDoc, error) {

web/files/files.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,16 @@ func ReadFileContentFromIDHandler(c echo.Context) error {
905905
if c.QueryParam("Dl") == "1" {
906906
disposition = "attachment"
907907
}
908-
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
908+
909+
if page := c.QueryParam("Page"); page != "" {
910+
p, errp := strconv.Atoi(page)
911+
if errp != nil {
912+
return jsonapi.InvalidParameter("Page", errp)
913+
}
914+
err = vfs.ServePDFPage(instance.VFS(), doc, disposition, p, c.Request(), c.Response())
915+
} else {
916+
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
917+
}
909918
if err != nil {
910919
return WrapVfsError(err)
911920
}
@@ -1115,7 +1124,16 @@ func sendFileFromPath(c echo.Context, path string, checkPermission bool) error {
11151124
} else if !checkPermission {
11161125
addCSPRuleForDirectLink(c, doc.Class, doc.Mime)
11171126
}
1118-
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
1127+
1128+
if page := c.QueryParam("Page"); page != "" {
1129+
p, errp := strconv.Atoi(page)
1130+
if errp != nil {
1131+
return jsonapi.InvalidParameter("Page", errp)
1132+
}
1133+
err = vfs.ServePDFPage(instance.VFS(), doc, disposition, p, c.Request(), c.Response())
1134+
} else {
1135+
err = vfs.ServeFileContent(instance.VFS(), doc, nil, "", disposition, c.Request(), c.Response())
1136+
}
11191137
if err != nil {
11201138
return WrapVfsError(err)
11211139
}

0 commit comments

Comments
 (0)