diff --git a/build/docs/content/04-html.md b/build/docs/content/04-html.md index 513fc6f8..85c426d5 100644 --- a/build/docs/content/04-html.md +++ b/build/docs/content/04-html.md @@ -301,6 +301,53 @@ $dest = 'result.pdf'; $client->store($request, $dest); ``` +## Page ranges + +You may specify the page ranges to convert. + +The format is the same as the one from the print options +of Google Chrome, e.g. `1-5,8,11-13`. + +### cURL + +```bash +$ curl --request POST \ + --url http://localhost:3000/convert/html \ + --header 'Content-Type: multipart/form-data' \ + --form files=@index.html \ + --form pageRanges='1-3,5' \ + -o result.pdf +``` + +### Go + +```golang +import "github.com/thecodingmachine/gotenberg-go-client/v7" + +c := &gotenberg.Client{Hostname: "http://localhost:3000"} +index, _ := gotenberg.NewDocumentFromPath("index.html", "/path/to/file") +req := gotenberg.NewHTMLRequest(index) +req.PageRanges("1-3,5") +dest := "result.pdf" +c.Store(req, dest) +``` + +### PHP + +```php +use TheCodingMachine\Gotenberg\Client; +use TheCodingMachine\Gotenberg\DocumentFactory; +use TheCodingMachine\Gotenberg\HTMLRequest; +use TheCodingMachine\Gotenberg\Request; + +$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); +$index = DocumentFactory::makeFromPath('index.html', '/path/to/file'); +$request = new HTMLRequest($index); +$request->setPageRanges('1-3,5'); +$dest = 'result.pdf'; +$client->store($request, $dest); +``` + ## Wait delay In some cases, you may want to wait a certain amount of time to make sure the diff --git a/build/docs/content/07-office.md b/build/docs/content/07-office.md index d9545027..a024b236 100644 --- a/build/docs/content/07-office.md +++ b/build/docs/content/07-office.md @@ -114,3 +114,54 @@ $request->setLandscape(true); $dest = 'result.pdf'; $client->store($request, $dest); ``` + +## Page ranges + +You may specify the page ranges to convert. + +The format is the same as the one from the print options +of LibreOffice, e.g. `1-1` or `1-4`. + +> **Attention:** if more than one document, the page ranges will be +> applied for each document. + +### cURL + +```bash +$ curl --request POST \ + --url http://localhost:3000/convert/office \ + --header 'Content-Type: multipart/form-data' \ + --form files=@document.docx \ + --form pageRanges='1-3' \ + -o result.pdf +``` + +### Go + +```golang +import "github.com/thecodingmachine/gotenberg-go-client/v6" + +c := &gotenberg.Client{Hostname: "http://localhost:3000"} +doc, _ := gotenberg.NewDocumentFromPath("document.docx", "/path/to/file") +req := gotenberg.NewOfficeRequest(doc) +req.PageRanges("1-3") +dest := "result.pdf" +c.Store(req, dest) +``` + +### PHP + +```php +use TheCodingMachine\Gotenberg\Client; +use TheCodingMachine\Gotenberg\DocumentFactory; +use TheCodingMachine\Gotenberg\OfficeRequest; + +$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); +$files = [ + DocumentFactory::makeFromPath('document.docx', '/path/to/file'), +]; +$request = new OfficeRequest($files); +$request->setPageRanges('1-3'); +$dest = 'result.pdf'; +$client->store($request, $dest); +``` diff --git a/docs/index.html b/docs/index.html index af20018c..6769a751 100755 --- a/docs/index.html +++ b/docs/index.html @@ -740,6 +740,58 @@ $dest = 'result.pdf'; $client->store($request, $dest); +
You may specify the page ranges to convert.
+ +The format is the same as the one from the print options
+of Google Chrome, e.g. 1-5,8,11-13.
$ curl --request POST \ + --url http://localhost:3000/convert/html \ + --header 'Content-Type: multipart/form-data' \ + --form files=@index.html \ + --form pageRanges='1-3,5' \ + -o result.pdf ++ +
import "github.com/thecodingmachine/gotenberg-go-client/v7" + +c := &gotenberg.Client{Hostname: "http://localhost:3000"} +index, _ := gotenberg.NewDocumentFromPath("index.html", "/path/to/file") +req := gotenberg.NewHTMLRequest(index) +req.PageRanges("1-3,5") +dest := "result.pdf" +c.Store(req, dest) ++ +
use TheCodingMachine\Gotenberg\Client;
+use TheCodingMachine\Gotenberg\DocumentFactory;
+use TheCodingMachine\Gotenberg\HTMLRequest;
+use TheCodingMachine\Gotenberg\Request;
+
+$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
+$index = DocumentFactory::makeFromPath('index.html', '/path/to/file');
+$request = new HTMLRequest($index);
+$request->setPageRanges('1-3,5');
+$dest = 'result.pdf';
+$client->store($request, $dest);
+
+
You may specify the page ranges to convert.
+ +The format is the same as the one from the print options
+of LibreOffice, e.g. 1-1 or 1-4.
++ +Attention: if more than one document, the page ranges will be +applied for each document.
+
$ curl --request POST \ + --url http://localhost:3000/convert/office \ + --header 'Content-Type: multipart/form-data' \ + --form files=@document.docx \ + --form pageRanges='1-3' \ + -o result.pdf ++ +
import "github.com/thecodingmachine/gotenberg-go-client/v6" + +c := &gotenberg.Client{Hostname: "http://localhost:3000"} +doc, _ := gotenberg.NewDocumentFromPath("document.docx", "/path/to/file") +req := gotenberg.NewOfficeRequest(doc) +req.PageRanges("1-3") +dest := "result.pdf" +c.Store(req, dest) ++ +
use TheCodingMachine\Gotenberg\Client;
+use TheCodingMachine\Gotenberg\DocumentFactory;
+use TheCodingMachine\Gotenberg\OfficeRequest;
+
+$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
+$files = [
+ DocumentFactory::makeFromPath('document.docx', '/path/to/file'),
+];
+$request = new OfficeRequest($files);
+$request->setPageRanges('1-3');
+$dest = 'result.pdf';
+$client->store($request, $dest);
diff --git a/internal/app/xhttp/option.go b/internal/app/xhttp/option.go
index 2afb6984..5a9a3759 100644
--- a/internal/app/xhttp/option.go
+++ b/internal/app/xhttp/option.go
@@ -48,23 +48,29 @@ func chromePrinterOptions(r resource.Resource, config conf.Config) (printer.Chro
if err != nil {
return printer.ChromePrinterOptions{}, err
}
+ pageRanges, err := r.StringArg(resource.PageRangesArgKey, "")
+ if err != nil {
+ return printer.ChromePrinterOptions{}, err
+ }
googleChromeRpccBufferSize, err := resource.GoogleChromeRpccBufferSizeArg(r, config)
if err != nil {
return printer.ChromePrinterOptions{}, err
}
return printer.ChromePrinterOptions{
- WaitTimeout: waitTimeout,
- WaitDelay: waitDelay,
- HeaderHTML: headerHTML,
- FooterHTML: footerHTML,
- PaperWidth: paperWidth,
- PaperHeight: paperHeight,
- MarginTop: marginTop,
- MarginBottom: marginBottom,
- MarginLeft: marginLeft,
- MarginRight: marginRight,
- Landscape: landscape,
- RpccBufferSize: googleChromeRpccBufferSize,
+ WaitTimeout: waitTimeout,
+ WaitDelay: waitDelay,
+ HeaderHTML: headerHTML,
+ FooterHTML: footerHTML,
+ PaperWidth: paperWidth,
+ PaperHeight: paperHeight,
+ MarginTop: marginTop,
+ MarginBottom: marginBottom,
+ MarginLeft: marginLeft,
+ MarginRight: marginRight,
+ Landscape: landscape,
+ PageRanges: pageRanges,
+ RpccBufferSize: googleChromeRpccBufferSize,
+ CustomHTTPHeaders: make(map[string]string),
}, nil
}
opts, err := resolver()
@@ -85,9 +91,14 @@ func officePrinterOptions(r resource.Resource, config conf.Config) (printer.Offi
if err != nil {
return printer.OfficePrinterOptions{}, err
}
+ pageRanges, err := r.StringArg(resource.PageRangesArgKey, "")
+ if err != nil {
+ return printer.OfficePrinterOptions{}, err
+ }
return printer.OfficePrinterOptions{
WaitTimeout: waitTimeout,
Landscape: landscape,
+ PageRanges: pageRanges,
}, nil
}
opts, err := resolver()
diff --git a/internal/app/xhttp/pkg/resource/arg.go b/internal/app/xhttp/pkg/resource/arg.go
index 706e8952..80063925 100644
--- a/internal/app/xhttp/pkg/resource/arg.go
+++ b/internal/app/xhttp/pkg/resource/arg.go
@@ -51,6 +51,9 @@ const (
// LandscapeArgKey is the key
// of the argument "landscape".
LandscapeArgKey ArgKey = "landscape"
+ // PageRangesArgKey is the key
+ // of the argument "pageRanges".
+ PageRangesArgKey ArgKey = "pageRanges"
// GoogleChromeRpccBufferSizeArgKey is the key
// of the argument "googleChromeRpccBufferSize".
GoogleChromeRpccBufferSizeArgKey ArgKey = "googleChromeRpccBufferSize"
@@ -76,6 +79,7 @@ func ArgKeys() []ArgKey {
MarginLeftArgKey,
MarginRightArgKey,
LandscapeArgKey,
+ PageRangesArgKey,
GoogleChromeRpccBufferSizeArgKey,
}
}
diff --git a/internal/app/xhttp/pkg/resource/arg_test.go b/internal/app/xhttp/pkg/resource/arg_test.go
index 1485c5d5..230add46 100644
--- a/internal/app/xhttp/pkg/resource/arg_test.go
+++ b/internal/app/xhttp/pkg/resource/arg_test.go
@@ -24,6 +24,7 @@ func TestArgKeys(t *testing.T) {
MarginLeftArgKey,
MarginRightArgKey,
LandscapeArgKey,
+ PageRangesArgKey,
GoogleChromeRpccBufferSizeArgKey,
}
assert.Equal(t, expected, ArgKeys())
diff --git a/internal/pkg/printer/chrome.go b/internal/pkg/printer/chrome.go
index 10337fab..4fe79d33 100644
--- a/internal/pkg/printer/chrome.go
+++ b/internal/pkg/printer/chrome.go
@@ -42,6 +42,7 @@ type ChromePrinterOptions struct {
MarginLeft float64
MarginRight float64
Landscape bool
+ PageRanges string
RpccBufferSize int64
CustomHTTPHeaders map[string]string
}
@@ -62,6 +63,7 @@ func DefaultChromePrinterOptions(config conf.Config) ChromePrinterOptions {
MarginLeft: 1.0,
MarginRight: 1.0,
Landscape: false,
+ PageRanges: "",
RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(),
CustomHTTPHeaders: make(map[string]string),
}
@@ -163,23 +165,35 @@ func (p chromePrinter) Print(destination string) error {
} else {
p.logger.DebugOp(op, "no wait delay to apply, moving on...")
}
+ printToPdfArgs := page.NewPrintToPDFArgs().
+ SetPaperWidth(p.opts.PaperWidth).
+ SetPaperHeight(p.opts.PaperHeight).
+ SetMarginTop(p.opts.MarginTop).
+ SetMarginBottom(p.opts.MarginBottom).
+ SetMarginLeft(p.opts.MarginLeft).
+ SetMarginRight(p.opts.MarginRight).
+ SetLandscape(p.opts.Landscape).
+ SetDisplayHeaderFooter(true).
+ SetHeaderTemplate(p.opts.HeaderHTML).
+ SetFooterTemplate(p.opts.FooterHTML).
+ SetPrintBackground(true)
+ if p.opts.PageRanges != "" {
+ printToPdfArgs.SetPageRanges(p.opts.PageRanges)
+ }
// print the page to PDF.
print, err := targetClient.Page.PrintToPDF(
ctx,
- page.NewPrintToPDFArgs().
- SetPaperWidth(p.opts.PaperWidth).
- SetPaperHeight(p.opts.PaperHeight).
- SetMarginTop(p.opts.MarginTop).
- SetMarginBottom(p.opts.MarginBottom).
- SetMarginLeft(p.opts.MarginLeft).
- SetMarginRight(p.opts.MarginRight).
- SetLandscape(p.opts.Landscape).
- SetDisplayHeaderFooter(true).
- SetHeaderTemplate(p.opts.HeaderHTML).
- SetFooterTemplate(p.opts.FooterHTML).
- SetPrintBackground(true),
+ printToPdfArgs,
)
if err != nil {
+ // find a way to check it in the handlers?
+ if strings.Contains(err.Error(), "Page range syntax error") {
+ return xerror.Invalid(
+ op,
+ fmt.Sprintf("'%s' is not a valid Google Chrome page ranges", p.opts.PageRanges),
+ err,
+ )
+ }
if strings.Contains(err.Error(), "rpcc: message too large") {
return xerror.Invalid(
op,
diff --git a/internal/pkg/printer/html_test.go b/internal/pkg/printer/html_test.go
index 4a233163..e69539fa 100644
--- a/internal/pkg/printer/html_test.go
+++ b/internal/pkg/printer/html_test.go
@@ -38,6 +38,26 @@ func TestHTMLPrinter(t *testing.T) {
assert.Nil(t, err)
err = os.RemoveAll(dest)
assert.Nil(t, err)
+ // options with a page ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "1"
+ p = NewHTMLPrinter(logger, fpath, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ assert.Nil(t, err)
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
+ // should not be OK as options have
+ // a wrong page ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "foo"
+ p = NewHTMLPrinter(logger, fpath, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ test.AssertError(t, err)
+ assert.Equal(t, xerror.InvalidCode, xerror.Code(err))
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
// should not be OK as context.Context
// should timeout.
opts = DefaultChromePrinterOptions(config)
diff --git a/internal/pkg/printer/markdown_test.go b/internal/pkg/printer/markdown_test.go
index c5115a3a..9627cdea 100644
--- a/internal/pkg/printer/markdown_test.go
+++ b/internal/pkg/printer/markdown_test.go
@@ -40,6 +40,28 @@ func TestMarkdownPrinter(t *testing.T) {
assert.Nil(t, err)
err = os.RemoveAll(dest)
assert.Nil(t, err)
+ // options with a page ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "1"
+ p, err = NewMarkdownPrinter(logger, fpath, opts)
+ assert.Nil(t, err)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ assert.Nil(t, err)
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
+ // should not be OK as options have
+ // a wrong page ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "foo"
+ p, err = NewMarkdownPrinter(logger, fpath, opts)
+ assert.Nil(t, err)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ test.AssertError(t, err)
+ assert.Equal(t, xerror.InvalidCode, xerror.Code(err))
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
// should not be OK as context.Context
// should timeout.
opts = DefaultChromePrinterOptions(config)
diff --git a/internal/pkg/printer/office.go b/internal/pkg/printer/office.go
index 1b31d461..020e5688 100644
--- a/internal/pkg/printer/office.go
+++ b/internal/pkg/printer/office.go
@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"sort"
+ "strings"
"github.com/phayes/freeport"
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
@@ -27,6 +28,7 @@ type officePrinter struct {
type OfficePrinterOptions struct {
WaitTimeout float64
Landscape bool
+ PageRanges string
}
// DefaultOfficePrinterOptions returns the default
@@ -35,6 +37,7 @@ func DefaultOfficePrinterOptions(config conf.Config) OfficePrinterOptions {
return OfficePrinterOptions{
WaitTimeout: config.DefaultWaitTimeout(),
Landscape: false,
+ PageRanges: "",
}
}
@@ -62,7 +65,7 @@ func (p officePrinter) Print(destination string) error {
baseFilename := xrand.Get()
tmpDest := fmt.Sprintf("%s/%d%s.pdf", dirPath, i, baseFilename)
p.logger.DebugfOp(op, "converting '%s' to PDF...", fpath)
- if err := unoconv(ctx, p.logger, fpath, tmpDest, p.opts); err != nil {
+ if err := p.unoconv(ctx, fpath, tmpDest); err != nil {
return err
}
p.logger.DebugfOp(op, "'%s.pdf' created", baseFilename)
@@ -88,7 +91,7 @@ func (p officePrinter) Print(destination string) error {
return nil
}
-func unoconv(ctx context.Context, logger xlog.Logger, fpath, destination string, opts OfficePrinterOptions) error {
+func (p officePrinter) unoconv(ctx context.Context, fpath, destination string) error {
const op string = "printer.unoconv"
resolver := func() error {
port, err := freeport.GetFreePort()
@@ -103,11 +106,25 @@ func unoconv(ctx context.Context, logger xlog.Logger, fpath, destination string,
"--format",
"pdf",
}
- if opts.Landscape {
+ if p.opts.Landscape {
args = append(args, "--printer", "PaperOrientation=landscape")
}
+ if p.opts.PageRanges != "" {
+ args = append(args, "--export", fmt.Sprintf("PageRange=%s", p.opts.PageRanges))
+ }
args = append(args, "--output", destination, fpath)
- return xexec.Run(ctx, logger, "unoconv", args...)
+ if err := xexec.Run(ctx, p.logger, "unoconv", args...); err != nil {
+ // find a way to check it in the handlers?
+ if p.opts.PageRanges != "" && strings.Contains(err.Error(), "exit status 5") {
+ return xerror.Invalid(
+ op,
+ fmt.Sprintf("'%s' is not a valid LibreOffice page ranges", p.opts.PageRanges),
+ err,
+ )
+ }
+ return err
+ }
+ return nil
}
if err := resolver(); err != nil {
return xerror.New(op, err)
diff --git a/internal/pkg/printer/office_test.go b/internal/pkg/printer/office_test.go
index b6a0235b..5eb8992e 100644
--- a/internal/pkg/printer/office_test.go
+++ b/internal/pkg/printer/office_test.go
@@ -46,6 +46,26 @@ func TestOfficePrinter(t *testing.T) {
assert.Nil(t, err)
err = os.RemoveAll(dest)
assert.Nil(t, err)
+ // options with page ranges.
+ opts = DefaultOfficePrinterOptions(config)
+ opts.PageRanges = "1-1"
+ p = NewOfficePrinter(logger, []string{fpaths[0]}, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ assert.Nil(t, err)
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
+ // should not be OK as options have
+ // a wrong page ranges.
+ opts = DefaultOfficePrinterOptions(config)
+ opts.PageRanges = "foo"
+ p = NewOfficePrinter(logger, []string{fpaths[0]}, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ test.AssertError(t, err)
+ assert.Equal(t, xerror.InvalidCode, xerror.Code(err))
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
// should not be OK as context.Context
// should timeout.
opts = DefaultOfficePrinterOptions(config)
diff --git a/internal/pkg/printer/url_test.go b/internal/pkg/printer/url_test.go
index 2c0b99f7..ef477e75 100644
--- a/internal/pkg/printer/url_test.go
+++ b/internal/pkg/printer/url_test.go
@@ -38,6 +38,26 @@ func TestURLPrinter(t *testing.T) {
assert.Nil(t, err)
err = os.RemoveAll(dest)
assert.Nil(t, err)
+ // options with a pages ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "1"
+ p = NewURLPrinter(logger, URL, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ assert.Nil(t, err)
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
+ // should not be OK as options have
+ // a wrong page ranges.
+ opts = DefaultChromePrinterOptions(config)
+ opts.PageRanges = "foo"
+ p = NewURLPrinter(logger, URL, opts)
+ dest = test.GenerateDestination()
+ err = p.Print(dest)
+ test.AssertError(t, err)
+ assert.Equal(t, xerror.InvalidCode, xerror.Code(err))
+ err = os.RemoveAll(dest)
+ assert.Nil(t, err)
// should not be OK as context.Context
// should timeout.
opts = DefaultChromePrinterOptions(config)