> For the complete documentation index, see [llms.txt](https://docs.pd4ml.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pd4ml.com/pdf-tools/pd4ml-pdf-merge/pd4mergecli-reference.md).

# pd4mergecli Reference

*Available starting from PD4ML v4.1.1*

Command-line front end for `com.pd4ml.pdf.merge`: combine page ranges from one or more PDFs (each with its own optional password) into one output file, optionally encrypting it - no code to write.

**Main-Class:** `com.pd4ml.pdf.merge.cli.Pd4MergeCli`

## 1. Invocation

```
java -cp pd4ml.jar com.pd4ml.pdf.merge.cli.Pd4MergeCli [flags...]
```

Throughout this document, `pd4ml.jar` stands for the pd4ml library jar (present in the current directory in every example below) - `com.pd4ml.pdf.merge.cli.Pd4MergeCli` isn't `pd4ml.jar`'s own `Main-Class` (that's `com.pd4ml.tools.Pd4Cmd`, a different tool), so invoke it with `-cp`, not `-jar`. Running with no arguments, or `help`/`--help`/`-h` as the first argument, prints the same usage summary this page expands on.

There is exactly one operation (no subcommands, unlike `pd4signcli`). All flags use `--key=value` syntax; there is no short-flag form.

### Exit codes

| Code | Meaning                                                                                                                                    |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `0`  | Success.                                                                                                                                   |
| `1`  | The operation failed for a business/runtime reason - bad password, out-of-range page, malformed PDF... The message on stderr explains why. |
| `2`  | Usage error - missing/invalid flags. Usage text is printed to stderr alongside the message.                                                |

## 2. Flags

### Sources - repeatable

Each `--in=` starts a new source; an optional `--password=` and/or `--pages=` immediately following it (before the *next* `--in=`, or a global-only flag) apply to that source specifically.

| Flag              | Notes                                                                                                    |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| `--in=<file.pdf>` | required at least once; repeat to add more sources                                                       |
| `--password=<pw>` | this source's password, if encrypted (tried as user *and* owner password; omit for an empty password)    |
| `--pages=<range>` | see §3 for the page-range grammar; **optional only when exactly one `--in` is given overall** - see §2.3 |

### Output

| Flag                 | Notes    |
| -------------------- | -------- |
| `--out=<output.pdf>` | required |

### Output encryption - optional

| Flag                                      | Notes                                                                         |
| ----------------------------------------- | ----------------------------------------------------------------------------- |
| `--encrypt-user=<pw>`                     | open (user) password for the merged output                                    |
| `--encrypt-owner=<pw>`                    | owner password for the merged output                                          |
| `--encrypt-alg=RC4_128\|AES_128\|AES_256` | default `AES_256` when any encrypt-\* flag is given                           |
| `--permissions=<int>`                     | raw `/P` permission bits (ISO 32000-1 Table 22); default "everything allowed" |

Giving *any* `--encrypt-user`/`--encrypt-owner`/`--encrypt-alg`/`--permissions` flag turns on output encryption; the output otherwise stays plain even if one or more sources were encrypted.

### 2.3 Omitting `--pages`

`--pages` may be omitted only when **exactly one** `--in` is given overall

* the result is then that document's own pages, in order, still decrypted (and re-encrypted only if an `--encrypt-*` flag is also given). With two or more `--in` occurrences, every one of them needs its own `--pages`; an unselected source in a multi-source run is a usage/runtime error, not a silent "everything."

## 3. Page-range grammar

`--pages` accepts a 1-based, comma-separated page-range spec:

| Token              | Meaning                                                                                                                              |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `"2-5,8,10-12"`    | explicit pages/ranges, in the order written                                                                                          |
| `"odd"` / `"even"` | **suppresses** the opposite parity from everything *else* in the same `--pages` value (not an additive range of its own) - see below |
| `"6+"`             | page 6 through the last page                                                                                                         |

`odd`/`even` filter the union of every other token in the same `--pages` value: `--pages=1-2,4+,odd` on a 10-page source unions `1-2` and `4+` into `1,2,4,5,6,7,8,9,10`, then `odd` suppresses the even ones, leaving `1,5,7,9`. Used alone (`--pages=odd`), the base defaults to every page in the document. `odd` and `even` together in one `--pages` value are rejected as contradictory. The combined result is always deduplicated, each page kept at the position it was first mentioned.

Shell quoting Most shells treat `+` and `,` as ordinary characters, but quote the whole `--pages=...` value anyway (e.g. `--pages="1,3,6+"`) if your source range contains spaces or shell metacharacters, or you're on a shell/OS with different quoting rules.

## 4. Examples

Two sources, mixed ranges and an `odd` filter:

```
$ java -cp pd4ml.jar com.pd4ml.pdf.merge.cli.Pd4MergeCli \
      --in=A.pdf --pages=2-5,odd \
      --in=B.pdf --password=secret --pages=1,3,6+ \
      --out=merged.pdf
```

Decrypt a single protected PDF (no merging, no re-encryption):

```
$ java -cp pd4ml.jar com.pd4ml.pdf.merge.cli.Pd4MergeCli --in=protected.pdf --password=secret --out=plain.pdf
```

Merge and encrypt the output:

```
$ java -cp pd4ml.jar com.pd4ml.pdf.merge.cli.Pd4MergeCli \
      --in=A.pdf --out=locked.pdf \
      --encrypt-user=open123 --encrypt-owner=owner456 --encrypt-alg=AES_256
```

A successful run reports what it did:

```
Merged 2 sources into merged.pdf (10201 bytes)
```

## 5. Troubleshooting

"selectPages(...) was never called for one of the merge sources" Two or more `--in` flags were given, and at least one has no `--pages=`. Add one - omitting `--pages` only works when there's exactly one source overall (§2.3)."specifies both 'odd' and 'even', which is contradictory" A single `--pages` value named both filters, e.g. `--pages=1-10,odd,even`. Use only one of them per source."Page N is out of range" A page number in `--pages` exceeds that specific source's own page count - ranges are validated per source, not against some combined total."is encrypted and the given password (or no password) did not open it" Add `--password=<pw>` for that source (either its user or owner password authenticates), or double-check the one given.Structural editing, tagging inspection, or general COS work `pd4mergecli` only merges; for arbitrary dictionary/array edits, structural inspection, or COS-path queries, see the [**pd4coscli Reference**](/pdf-tools/pd4ml-pdf-cos/pd4coscli-reference.md) (`com.pd4ml.pdf.cos.cli.CosCli`). For importing or exporting annotations and form field values via XFDF, see the [pd4xfdfcli Reference](/pdf-tools/pd4ml-pdf-xfdf/pd4xfdfcli-reference.md) (`com.pd4ml.pdf.xfdf.cli.Pd4XfdfCli`).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.pd4ml.com/pdf-tools/pd4ml-pdf-merge/pd4mergecli-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
