Skip to content

Commit e649115

Browse files
authored
Merge pull request #4 from vinitkumar/feat/implement-linux-pdf-joiner
feat: implement cross-platform PDF joiner with Linux support
2 parents 00ced88 + 6349c3a commit e649115

File tree

9 files changed

+545
-26
lines changed

9 files changed

+545
-26
lines changed

.goreleaser.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
before:
2+
hooks:
3+
- go mod tidy
4+
5+
builds:
6+
- env:
7+
- CGO_ENABLED=0
8+
goos:
9+
- linux
10+
- darwin
11+
goarch:
12+
- amd64
13+
- arm64
14+
binary: pdf-joiner
15+
ldflags:
16+
- -s -w
17+
18+
archives:
19+
- format: tar.gz
20+
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
21+
files:
22+
- "README.md"
23+
- "LICENSE"
24+
25+
checksum:
26+
name_template: 'checksums.txt'
27+
28+
snapshot:
29+
name_template: "{{ incpatch .Version }}-next"
30+
31+
release:
32+
draft: false
33+
prerelease: false
34+
35+
dist:
36+
name: pdf-joiner

AGENT.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# PDF Joiner - Agent Guidelines
2+
3+
## Build/Test Commands
4+
- Build: `go build -o pdf-joiner` or `make build`
5+
- Test: `go test -v ./...` or `make test`
6+
- Run single test: `go test -v -run TestFileExists`
7+
- Clean: `make clean`
8+
- Dependencies: `go mod tidy` or `make deps`
9+
10+
## Architecture
11+
- Single-file Go CLI application
12+
- Uses macOS built-in PDF joining utility at `/System/Library/Automator/Combine PDF Pages.action/Contents/MacOS/join`
13+
- No external dependencies beyond Go standard library
14+
- Simple command-line interface with flag parsing
15+
16+
## Code Style
17+
- Standard Go formatting (use `gofmt`)
18+
- Use standard library packages: `flag`, `fmt`, `os`, `os/exec`, `path/filepath`, `time`
19+
- Constants in ALL_CAPS for paths
20+
- Descriptive variable names: `pdfFiles`, `outputPath`, `tempFile`
21+
- Error handling: check errors immediately and exit with os.Exit(1) on failure
22+
- Use defer for cleanup (file closing, temp file removal)
23+
- Table-driven tests with struct slices
24+
- Test function names: `TestFunctionName`
25+
- Comments for exported functions and complex logic only

LINUX_SUPPORT_ISSUE.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# 🐧 Feature Request: Add Linux Support for PDF Joining
2+
3+
## Overview
4+
5+
This project currently provides a simple and efficient PDF joining tool for **macOS only**, leveraging the native macOS PDF utility at `/System/Library/Automator/Combine PDF Pages.action/Contents/MacOS/join`. We're looking for community contributions to extend this functionality to **Linux systems** using stable, native Linux PDF processing tools.
6+
7+
## Current State
8+
9+
The tool currently:
10+
- ✅ Works seamlessly on macOS using built-in system utilities
11+
- ✅ Joins multiple PDF files with simple command-line interface
12+
- ✅ Supports custom output paths and automatic timestamped naming
13+
- ✅ Validates input files and creates output directories
14+
-**Linux support is missing**
15+
16+
## What We Need
17+
18+
We're seeking contributions to add Linux support while maintaining the same design principles:
19+
20+
### 🎯 Goals
21+
1. **Use native/stable Linux tools** - Just like we use macOS's built-in utility, we want to leverage well-established Linux PDF tools
22+
2. **Maintain the same CLI interface** - The user experience should be identical across platforms
23+
3. **Cross-platform detection** - Automatically detect the OS and use the appropriate backend
24+
4. **No external dependencies** - Avoid requiring users to install additional libraries
25+
26+
### 🛠️ Technical Implementation Ideas
27+
28+
We need platform detection and multiple backend support. Here are some proven Linux PDF joining tools to consider:
29+
30+
#### Option 1: `pdfunite` (from poppler-utils)
31+
```bash
32+
# Most Linux distributions include this by default
33+
pdfunite input1.pdf input2.pdf input3.pdf output.pdf
34+
```
35+
36+
#### Option 2: `ghostscript` (gs)
37+
```bash
38+
# Very stable, widely available
39+
gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=output.pdf input1.pdf input2.pdf
40+
```
41+
42+
#### Option 3: `pdftk`
43+
```bash
44+
# Popular choice, though may need installation
45+
pdftk input1.pdf input2.pdf cat output output.pdf
46+
```
47+
48+
#### Option 4: `qpdf`
49+
```bash
50+
# Another stable option
51+
qpdf --empty --pages input1.pdf input2.pdf -- output.pdf
52+
```
53+
54+
### 📋 Implementation Checklist
55+
56+
The ideal contribution should include:
57+
58+
- [ ] **OS Detection Logic** - Use `runtime.GOOS` to detect the operating system
59+
- [ ] **Backend Selection** - Implement a strategy pattern for different PDF joining backends
60+
- [ ] **Linux Tool Detection** - Check which PDF tools are available on the system (priority order)
61+
- [ ] **Error Handling** - Graceful fallbacks and clear error messages if no suitable tool is found
62+
- [ ] **Tests** - Unit tests for the new functionality (mocked where appropriate)
63+
- [ ] **Documentation** - Update README with Linux requirements and installation instructions
64+
- [ ] **CI/CD** - Ensure GitHub Actions test on Linux environments
65+
66+
### 🏗️ Suggested Code Structure
67+
68+
```go
69+
type PDFJoiner interface {
70+
Join(inputFiles []string, outputPath string) error
71+
IsAvailable() bool
72+
}
73+
74+
type MacOSJoiner struct{}
75+
type LinuxJoiner struct {
76+
backend string // "pdfunite", "gs", "pdftk", etc.
77+
}
78+
79+
func NewPDFJoiner() PDFJoiner {
80+
switch runtime.GOOS {
81+
case "darwin":
82+
return &MacOSJoiner{}
83+
case "linux":
84+
return NewLinuxJoiner()
85+
default:
86+
return nil // unsupported OS
87+
}
88+
}
89+
```
90+
91+
### 🐧 Linux Distribution Considerations
92+
93+
The solution should work across major Linux distributions:
94+
- **Ubuntu/Debian** - `poppler-utils` (pdfunite) is usually available
95+
- **RHEL/CentOS/Fedora** - Similar package availability
96+
- **Arch Linux** - Most PDF tools available via pacman
97+
- **Alpine Linux** - Lightweight options preferred
98+
99+
### 📚 Resources for Contributors
100+
101+
- **Current macOS implementation**: [`main.go:53-77`](main.go#L53-L77)
102+
- **Command execution pattern**: Uses `exec.Command()` - maintain this approach
103+
- **Testing examples**: See [`main_test.go`](main_test.go) for testing patterns
104+
- **Error handling style**: Follow existing patterns in the codebase
105+
106+
## How to Contribute
107+
108+
1. **Fork the repository**
109+
2. **Create a feature branch**: `git checkout -b feature/linux-support`
110+
3. **Implement the changes** following the checklist above
111+
4. **Test thoroughly** on different Linux distributions if possible
112+
5. **Update documentation** (README.md, comments)
113+
6. **Submit a pull request** with:
114+
- Clear description of your implementation choice
115+
- Testing details (which distros/tools you tested)
116+
- Any limitations or known issues
117+
118+
## Questions for Contributors
119+
120+
- Which Linux PDF tool would you recommend as the primary choice? Why?
121+
- Should we implement a fallback chain (try pdfunite, then gs, then pdftk)?
122+
- Any experience with PDF joining tools on specific Linux distributions?
123+
124+
## Recognition
125+
126+
Contributors who successfully implement Linux support will be:
127+
- Added to the project's contributors list
128+
- Mentioned in release notes
129+
- Given co-maintainer consideration for ongoing development
130+
131+
---
132+
133+
**Let's make this tool truly cross-platform! 🚀**
134+
135+
*This is a great opportunity for developers familiar with Linux systems and Go programming to contribute to an open-source project used by the community.*
136+
137+
---
138+
139+
### Labels
140+
`enhancement` `help wanted` `good first issue` `linux` `cross-platform`

Makefile

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test clean install all
1+
.PHONY: build test clean install all release release-snapshot build-all
22

33
# Binary name
44
BINARY_NAME=pdf-joiner
@@ -26,6 +26,8 @@ clean:
2626
$(GOCLEAN)
2727
rm -f $(BINARY_NAME)
2828
rm -f $(BINARY_NAME).exe
29+
rm -rf dist/
30+
rm -rf builds/
2931

3032
run:
3133
$(GOBUILD) -o $(BINARY_NAME) -v
@@ -38,12 +40,58 @@ install: build
3840
deps:
3941
$(GOMOD) tidy
4042

41-
# Cross compilation
43+
# Cross compilation with organized folder structure
4244
build-darwin:
43-
GOOS=darwin GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-amd64 -v
44-
GOOS=darwin GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-arm64 -v
45+
mkdir -p builds/darwin/amd64 builds/darwin/arm64
46+
GOOS=darwin GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o builds/darwin/amd64/$(BINARY_NAME) -v
47+
GOOS=darwin GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o builds/darwin/arm64/$(BINARY_NAME) -v
4548

4649
# Universal binary for macOS (both Intel and Apple Silicon)
4750
build-universal-darwin: build-darwin
48-
lipo -create -output $(BINARY_NAME) $(BINARY_NAME)-darwin-amd64 $(BINARY_NAME)-darwin-arm64
49-
rm $(BINARY_NAME)-darwin-amd64 $(BINARY_NAME)-darwin-arm64
51+
mkdir -p builds/darwin/universal
52+
lipo -create -output builds/darwin/universal/$(BINARY_NAME) builds/darwin/amd64/$(BINARY_NAME) builds/darwin/arm64/$(BINARY_NAME)
53+
54+
# Linux builds
55+
build-linux:
56+
mkdir -p builds/linux/amd64 builds/linux/arm64
57+
GOOS=linux GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o builds/linux/amd64/$(BINARY_NAME) -v
58+
GOOS=linux GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o builds/linux/arm64/$(BINARY_NAME) -v
59+
60+
# Build for all platforms (Linux and macOS only)
61+
build-all: build-darwin build-linux
62+
63+
# GoReleaser commands
64+
release:
65+
goreleaser release --clean
66+
67+
release-snapshot:
68+
goreleaser release --snapshot --clean --skip=publish
69+
70+
release-dry-run:
71+
goreleaser release --snapshot --clean --skip=publish --skip=validate
72+
73+
# Development builds with GoReleaser
74+
dev-build:
75+
goreleaser build --snapshot --clean --single-target
76+
77+
# Check GoReleaser configuration
78+
check-config:
79+
goreleaser check
80+
81+
# Help
82+
help:
83+
@echo "Available targets:"
84+
@echo " build - Build for current platform"
85+
@echo " build-all - Build for all platforms (Linux, macOS)"
86+
@echo " build-darwin - Build for macOS (Intel + Apple Silicon)"
87+
@echo " build-linux - Build for Linux (AMD64 + ARM64)"
88+
@echo " build-universal-darwin - Create universal macOS binary"
89+
@echo " test - Run tests"
90+
@echo " clean - Clean build artifacts"
91+
@echo " install - Install to /usr/local/bin"
92+
@echo " deps - Tidy dependencies"
93+
@echo " release - Create a release with GoReleaser"
94+
@echo " release-snapshot - Create a snapshot release"
95+
@echo " release-dry-run - Dry run of release process"
96+
@echo " dev-build - Build for current platform with GoReleaser"
97+
@echo " check-config - Validate GoReleaser configuration"

README.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,50 @@
22

33
[![Go CI](https://github.com/vinitkumar/pdf-joiner/actions/workflows/ci.yml/badge.svg)](https://github.com/vinitkumar/pdf-joiner/actions/workflows/ci.yml)
44

5-
A simple command-line tool to join multiple PDF files into a single PDF document on macOS.
5+
A simple command-line tool to join multiple PDF files into a single PDF document on **macOS and Linux**.
66

77
## Requirements
88

9+
### macOS
910
- macOS (the tool uses the built-in macOS PDF joining utility)
1011
- Go 1.24 or higher (for development)
1112

13+
### Linux
14+
- Linux distribution with one of the following PDF tools installed:
15+
- **pdfunite** (from poppler-utils) - *Recommended, available on most distributions*
16+
- **ghostscript** (gs) - *Widely available fallback*
17+
- **qpdf** - *Alternative option*
18+
- Go 1.24 or higher (for development)
19+
20+
### Installation of Linux PDF Tools
21+
22+
**Ubuntu/Debian:**
23+
```bash
24+
sudo apt-get install poppler-utils # for pdfunite
25+
# or
26+
sudo apt-get install ghostscript # for gs
27+
# or
28+
sudo apt-get install qpdf # for qpdf
29+
```
30+
31+
**RHEL/CentOS/Fedora:**
32+
```bash
33+
sudo dnf install poppler-utils # for pdfunite
34+
# or
35+
sudo dnf install ghostscript # for gs
36+
# or
37+
sudo dnf install qpdf # for qpdf
38+
```
39+
40+
**Arch Linux:**
41+
```bash
42+
sudo pacman -S poppler # for pdfunite
43+
# or
44+
sudo pacman -S ghostscript # for gs
45+
# or
46+
sudo pacman -S qpdf # for qpdf
47+
```
48+
1249
## Installation
1350

1451
### From Source
@@ -35,11 +72,26 @@ Download the latest binary from the [Releases page](https://github.com/vinitkuma
3572

3673
## Features
3774

75+
- **Cross-platform support**: Works on macOS and Linux
76+
- **Automatic tool detection**: Uses the best available PDF tool on each platform
77+
- **Multiple backends on Linux**: Supports pdfunite, ghostscript, and qpdf with intelligent fallback
3878
- Join multiple PDF files into a single document
3979
- Specify custom output path
4080
- Automatic output filename generation with timestamp
4181
- Verification of input files
4282

83+
## How It Works
84+
85+
The tool automatically detects your operating system and uses the appropriate PDF joining method:
86+
87+
- **macOS**: Uses the built-in `/System/Library/Automator/Combine PDF Pages.action/Contents/MacOS/join` utility
88+
- **Linux**: Automatically detects and uses the best available tool in this priority order:
89+
1. `pdfunite` (from poppler-utils) - Fastest and most reliable
90+
2. `ghostscript` (`gs`) - Widely available, stable fallback
91+
3. `qpdf` - Alternative option
92+
93+
If none of the Linux tools are available, the program will provide clear instructions on which packages to install.
94+
4395
## Development
4496

4597
### Testing

0 commit comments

Comments
 (0)