Skip to content

Commit 99f2374

Browse files
committed
feat: add ghoulscript
1 parent e34ab7e commit 99f2374

File tree

12 files changed

+4362
-14
lines changed

12 files changed

+4362
-14
lines changed

.gitignore

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,131 @@
1-
.yarn/*
2-
!.yarn/patches
3-
!.yarn/plugins
4-
!.yarn/releases
5-
!.yarn/sdks
6-
!.yarn/versions
7-
8-
# Swap the comments on the following lines if you wish to use zero-installs
9-
# In that case, don't forget to run `yarn config set enableGlobalCache false`!
10-
# Documentation here: https://yarnpkg.com/features/caching#zero-installs
11-
12-
#!.yarn/cache
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
.pnpm-debug.log*
9+
10+
# Diagnostic reports (https://nodejs.org/api/report.html)
11+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12+
13+
# Runtime data
14+
pids
15+
*.pid
16+
*.seed
17+
*.pid.lock
18+
19+
# Directory for instrumented libs generated by jscoverage/JSCover
20+
lib-cov
21+
22+
# Coverage directory used by tools like istanbul
23+
coverage
24+
*.lcov
25+
26+
# nyc test coverage
27+
.nyc_output
28+
29+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30+
.grunt
31+
32+
# Bower dependency directory (https://bower.io/)
33+
bower_components
34+
35+
# node-waf configuration
36+
.lock-wscript
37+
38+
# Compiled binary addons (https://nodejs.org/api/addons.html)
39+
build/Release
40+
41+
# Dependency directories
42+
node_modules/
43+
jspm_packages/
44+
45+
# Snowpack dependency directory (https://snowpack.dev/)
46+
web_modules/
47+
48+
# TypeScript cache
49+
*.tsbuildinfo
50+
51+
# Optional npm cache directory
52+
.npm
53+
54+
# Optional eslint cache
55+
.eslintcache
56+
57+
# Optional stylelint cache
58+
.stylelintcache
59+
60+
# Microbundle cache
61+
.rpt2_cache/
62+
.rts2_cache_cjs/
63+
.rts2_cache_es/
64+
.rts2_cache_umd/
65+
66+
# Optional REPL history
67+
.node_repl_history
68+
69+
# Output of 'npm pack'
70+
*.tgz
71+
72+
# Yarn Integrity file
73+
.yarn-integrity
74+
75+
# dotenv environment variable files
76+
.env
77+
.env.development.local
78+
.env.test.local
79+
.env.production.local
80+
.env.local
81+
82+
# parcel-bundler cache (https://parceljs.org/)
83+
.cache
84+
.parcel-cache
85+
86+
# Next.js build output
87+
.next
88+
out
89+
90+
# Nuxt.js build / generate output
91+
.nuxt
92+
dist
93+
94+
# Gatsby files
95+
.cache/
96+
# Comment in the public line in if your project uses Gatsby and not Next.js
97+
# https://nextjs.org/blog/next-9-1#public-directory-support
98+
# public
99+
100+
# vuepress build output
101+
.vuepress/dist
102+
103+
# vuepress v2.x temp and cache directory
104+
.temp
105+
.cache
106+
107+
# Docusaurus cache and generated files
108+
.docusaurus
109+
110+
# Serverless directories
111+
.serverless/
112+
113+
# FuseBox cache
114+
.fusebox/
115+
116+
# DynamoDB Local files
117+
.dynamodb/
118+
119+
# TernJS port file
120+
.tern-port
121+
122+
# Stores VSCode versions used for testing VSCode extensions
123+
.vscode-test
124+
125+
# yarn v2
126+
.yarn/cache
127+
.yarn/unplugged
128+
.yarn/build-state.yml
129+
.yarn/install-state.gz
13130
.pnp.*
131+
.DS_Store

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "packages/gs-wasm/ghostpdl"]
2+
path = packages/ghostscript/ghostpdl
3+
url = git://git.ghostscript.com/ghostpdl.git

.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2-
"name": "ghoulscript",
3-
"packageManager": "yarn@4.2.2"
2+
"name": "@privyid/ghoulscript-monorepo",
3+
"packageManager": "yarn@4.2.2",
4+
"private": true,
5+
"workspaces": [
6+
"packages/*"
7+
]
48
}

packages/ghoulscript/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# ghoulscript

packages/ghoulscript/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@privyid/ghoulscript",
3+
"packageManager": "yarn@4.2.2",
4+
"scripts": {
5+
"build": "unbuild",
6+
"test": "deno run ./tests/sample.ts"
7+
},
8+
"devDependencies": {
9+
"@types/node": "^20.14.0",
10+
"jiti": "^1.21.0",
11+
"typescript": "^5.4.5",
12+
"unbuild": "^2.0.0"
13+
},
14+
"dependencies": {
15+
"@privyid/ghostscript": "workspace:^",
16+
"defu": "^6.1.4"
17+
}
18+
}

packages/ghoulscript/src/index.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import useGS from '@privyid/ghostscript'
2+
import { defu } from 'defu'
3+
4+
interface CompressOptions {
5+
password?: string,
6+
pdfSettings: 'screen' | 'ebook' | 'printer' | 'prepress' | 'default',
7+
fastWebView: boolean,
8+
compatibilityLevel: string,
9+
noTransparency: boolean,
10+
ownerPassword?: string,
11+
userPassword?: string,
12+
keepPassword: boolean,
13+
}
14+
15+
async function createPDF (inputs: Array<string | ArrayBufferView>, option: Partial<CompressOptions> = {}): Promise<Uint8Array> {
16+
const gs = await useGS()
17+
const opts = defu<CompressOptions, [CompressOptions]>(option, {
18+
pdfSettings : 'screen',
19+
compatibilityLevel: '1.4',
20+
fastWebView : true,
21+
noTransparency : true,
22+
keepPassword : true,
23+
})
24+
25+
const args = [
26+
'-dQUIET',
27+
'-dNOPAUSE',
28+
'-dBATCH',
29+
'-dSAFER',
30+
'-sDEVICE=pdfwrite',
31+
]
32+
33+
let userPassword = opts.userPassword
34+
let ownerPassword = opts.ownerPassword ?? opts.userPassword
35+
36+
if (opts.password) {
37+
args.push(`-sPDFPassword=${opts.password}`)
38+
39+
if (opts.keepPassword) {
40+
userPassword ??= opts.password
41+
ownerPassword ??= opts.password
42+
}
43+
}
44+
45+
if (userPassword) {
46+
args.push(`-sUserPassword=${userPassword}`)
47+
args.push(`-sOwnerPassword=${ownerPassword}`)
48+
}
49+
50+
if (opts.noTransparency)
51+
args.push(`-dNOTRANSPARENCY`)
52+
53+
args.push(`-dCompatibilityLevel=${opts.compatibilityLevel}`)
54+
args.push(`-sColorConversionStrategy=RGB`)
55+
args.push(`-dPDFSETTINGS=/${opts.pdfSettings}`)
56+
args.push(`-dFastWebView=${opts.fastWebView}`)
57+
args.push(`-sOutputFile=./output.pdf`)
58+
59+
inputs.forEach((input, i) => {
60+
const inputFilename = `./input-${i}`
61+
62+
gs.FS.writeFile(inputFilename, input)
63+
args.push(inputFilename)
64+
})
65+
66+
await gs.callMain(args)
67+
68+
return gs.FS.readFile('./output.pdf', { encoding: 'binary' })
69+
}
70+
71+
export async function optimizePDF (input: string | ArrayBufferView, option: Partial<CompressOptions> = {}) {
72+
return createPDF([input], option)
73+
}
74+
75+
export async function combinePDF (inputs: Array<string | ArrayBufferView>, option: Partial<CompressOptions> = {}) {
76+
return createPDF(inputs, option)
77+
}
78+
79+
export async function addPassword (input: string | ArrayBufferView, ownerPassword: string, userPassword: string = ownerPassword) {
80+
return createPDF([input], {
81+
ownerPassword,
82+
userPassword,
83+
})
84+
}
85+
86+
export async function removePassword (input: string | ArrayBufferView, password: string) {
87+
return createPDF([input], { keepPassword: false, password: password })
88+
}
89+
90+
91+
export async function renderPageAsImage (input: string | ArrayBufferView, pageNumber: number = 1) {
92+
const gs = await useGS()
93+
const args = [
94+
'-dQUIET',
95+
'-dNOPAUSE',
96+
'-dBATCH',
97+
'-dSAFER',
98+
'-sDEVICE=jpeg',
99+
`-sPageList=${pageNumber}`,
100+
`-r96`,
101+
'-dTextAlphaBits=4',
102+
'-dGraphicsAlphaBits=4',
103+
`-sOutputFile=./output.jpg`,
104+
'input',
105+
]
106+
107+
gs.FS.writeFile('./input', input)
108+
109+
await gs.callMain(args)
110+
111+
return gs.FS.readFile('./output.jpg', { encoding: 'binary' })
112+
}
6.71 MB
Loading
127 KB
Binary file not shown.
4.03 MB
Binary file not shown.

0 commit comments

Comments
 (0)