pdfcrowd-cli 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,173 @@
1
+ # Copyright (C) 2009-2026 pdfcrowd.com
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person
4
+ # obtaining a copy of this software and associated documentation
5
+ # files (the "Software"), to deal in the Software without
6
+ # restriction, including without limitation the rights to use,
7
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following
10
+ # conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ # OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ """Owner-only credential DACLs using native Windows security APIs.
25
+
26
+ See Microsoft's GetNamedSecurityInfoW and SetNamedSecurityInfoW contracts.
27
+ Native Windows validation remains part of platform release acceptance.
28
+ """
29
+
30
+ import ctypes
31
+ from ctypes import wintypes
32
+
33
+ from .catalog import CLIError
34
+
35
+
36
+ class ACLSize(ctypes.Structure):
37
+ _fields_ = [('count', wintypes.DWORD), ('used', wintypes.DWORD),
38
+ ('free', wintypes.DWORD)]
39
+
40
+
41
+ class ACEHeader(ctypes.Structure):
42
+ _fields_ = [('kind', wintypes.BYTE), ('flags', wintypes.BYTE),
43
+ ('size', wintypes.WORD)]
44
+
45
+
46
+ class Security:
47
+ def __init__(self):
48
+ self.advapi = ctypes.WinDLL('advapi32', use_last_error=True)
49
+ self.kernel = ctypes.WinDLL('kernel32', use_last_error=True)
50
+ pointer = ctypes.c_void_p
51
+ output = ctypes.POINTER(pointer)
52
+ integer = wintypes.DWORD
53
+ self.function(self.kernel, 'GetCurrentProcess', pointer, [])
54
+ self.function(self.kernel, 'CloseHandle', wintypes.BOOL, [pointer])
55
+ self.function(self.kernel, 'LocalFree', pointer, [pointer])
56
+ signatures = {
57
+ 'OpenProcessToken': (wintypes.BOOL, [pointer, integer, output]),
58
+ 'GetTokenInformation': (wintypes.BOOL, [
59
+ pointer, integer, pointer, integer, ctypes.POINTER(integer)]),
60
+ 'ConvertSidToStringSidW': (wintypes.BOOL, [pointer, output]),
61
+ 'ConvertStringSecurityDescriptorToSecurityDescriptorW': (
62
+ wintypes.BOOL, [wintypes.LPCWSTR, integer, output, pointer]),
63
+ 'GetSecurityDescriptorDacl': (wintypes.BOOL, [
64
+ pointer, ctypes.POINTER(wintypes.BOOL), output,
65
+ ctypes.POINTER(wintypes.BOOL)]),
66
+ 'GetNamedSecurityInfoW': (integer, [wintypes.LPCWSTR, integer,
67
+ integer, output, output, output, output, output]),
68
+ 'SetNamedSecurityInfoW': (integer, [wintypes.LPWSTR, integer,
69
+ integer, pointer, pointer, pointer, pointer]),
70
+ 'GetAclInformation': (wintypes.BOOL, [pointer, pointer,
71
+ integer, integer]),
72
+ 'GetAce': (wintypes.BOOL, [pointer, integer, output]),
73
+ }
74
+ for name, (result, arguments) in signatures.items():
75
+ self.function(self.advapi, name, result, arguments)
76
+
77
+ @staticmethod
78
+ def function(library, name, result, arguments):
79
+ function = getattr(library, name)
80
+ function.restype, function.argtypes = result, arguments
81
+
82
+ @staticmethod
83
+ def require(succeeded):
84
+ if not succeeded:
85
+ raise CLIError('Could not establish or verify private Windows '
86
+ 'credential access. Use environment credentials.', 3)
87
+
88
+ def sid_text(self, sid):
89
+ text = ctypes.c_void_p()
90
+ self.require(self.advapi.ConvertSidToStringSidW(
91
+ sid, ctypes.byref(text)))
92
+ try:
93
+ return ctypes.wstring_at(text)
94
+ finally:
95
+ self.kernel.LocalFree(text)
96
+
97
+ def current_sid(self):
98
+ token = ctypes.c_void_p()
99
+ self.require(self.advapi.OpenProcessToken(
100
+ self.kernel.GetCurrentProcess(), 8, ctypes.byref(token)))
101
+ try:
102
+ size = wintypes.DWORD()
103
+ self.advapi.GetTokenInformation(token, 1, None, 0,
104
+ ctypes.byref(size))
105
+ self.require(size.value)
106
+ buffer = ctypes.create_string_buffer(size.value)
107
+ self.require(self.advapi.GetTokenInformation(
108
+ token, 1, buffer, size, ctypes.byref(size)))
109
+ sid = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_void_p))[0]
110
+ return self.sid_text(sid)
111
+ finally:
112
+ self.kernel.CloseHandle(token)
113
+
114
+ def descriptor(self, path):
115
+ owner, dacl, descriptor = (ctypes.c_void_p() for _ in range(3))
116
+ result = self.advapi.GetNamedSecurityInfoW(
117
+ str(path), 1, 5, ctypes.byref(owner), None, ctypes.byref(dacl),
118
+ None, ctypes.byref(descriptor))
119
+ self.require(result == 0)
120
+ return owner, dacl, descriptor
121
+
122
+
123
+ def check_private(path):
124
+ security = Security()
125
+ current = security.current_sid()
126
+ owner, dacl, descriptor = security.descriptor(path)
127
+ try:
128
+ security.require(owner and security.sid_text(owner) == current and dacl)
129
+ size = ACLSize()
130
+ security.require(security.advapi.GetAclInformation(
131
+ dacl, ctypes.byref(size), ctypes.sizeof(size), 2))
132
+ allowed = False
133
+ for index in range(size.count):
134
+ ace = ctypes.c_void_p()
135
+ security.require(security.advapi.GetAce(dacl, index,
136
+ ctypes.byref(ace)))
137
+ header = ctypes.cast(ace, ctypes.POINTER(ACEHeader)).contents
138
+ if header.flags & 8 or header.kind == 1:
139
+ continue # Inherit-only or denying ACEs cannot grant access.
140
+ security.require(header.kind == 0)
141
+ security.require(security.sid_text(ace.value + 8) == current)
142
+ allowed = True
143
+ security.require(allowed)
144
+ finally:
145
+ security.kernel.LocalFree(descriptor)
146
+
147
+
148
+ def protect(path):
149
+ security = Security()
150
+ current = security.current_sid()
151
+ owner, _, existing = security.descriptor(path)
152
+ try:
153
+ security.require(owner and security.sid_text(owner) == current)
154
+ finally:
155
+ security.kernel.LocalFree(existing)
156
+ descriptor = ctypes.c_void_p()
157
+ inheritance = 'OICI' if path.is_dir() else ''
158
+ security.require(
159
+ security.advapi.ConvertStringSecurityDescriptorToSecurityDescriptorW(
160
+ f'D:P(A;{inheritance};FA;;;{current})', 1,
161
+ ctypes.byref(descriptor), None))
162
+ try:
163
+ present, defaulted = wintypes.BOOL(), wintypes.BOOL()
164
+ dacl = ctypes.c_void_p()
165
+ security.require(security.advapi.GetSecurityDescriptorDacl(
166
+ descriptor, ctypes.byref(present), ctypes.byref(dacl),
167
+ ctypes.byref(defaulted)))
168
+ security.require(present.value and dacl)
169
+ security.require(security.advapi.SetNamedSecurityInfoW(
170
+ str(path), 1, 0x80000004, None, None, dacl, None) == 0)
171
+ check_private(path)
172
+ finally:
173
+ security.kernel.LocalFree(descriptor)
@@ -0,0 +1,22 @@
1
+ Copyright (C) 2009-2026 pdfcrowd.com
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.1
2
+ Name: pdfcrowd-cli
3
+ Version: 1.0.0
4
+ Summary: Command-line converters for the PDFCrowd API.
5
+ Home-page: https://pdfcrowd.com/api/
6
+ Author: PDFCrowd Team
7
+ License: MIT
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pdfcrowd <7.1.0,>=7.0.0
12
+ Requires-Dist: platformdirs <5,>=4.3.6
13
+
14
+ # PDFCrowd CLI
15
+
16
+ Install with Python 3.8 or newer using pipx or uv:
17
+
18
+ ```sh
19
+ pipx install pdfcrowd-cli
20
+ # Alternatively: uv tool install pdfcrowd-cli
21
+ pdfcrowd html2pdf --output example.pdf https://example.com
22
+ pdfcrowd --help
23
+ pdfcrowd html2pdf help page-margins
24
+ pdfcrowd html2pdf help --json
25
+ ```
26
+
27
+ The CLI package also provides the converter commands directly:
28
+
29
+ ```sh
30
+ html2pdf --output example.pdf https://example.com
31
+ html2pdf --help
32
+ html2pdf help page-margins
33
+ html2pdf help --json
34
+ ```
35
+
36
+ The available commands are `html2pdf`, `html2image`, `image2pdf`,
37
+ `image2image`, `pdf2pdf`, `pdf2html`, `pdf2text`, and `pdf2image`.
38
+ Without personal credentials, conversions use the demo account and produce
39
+ marked output. Set `PDFCROWD_USERNAME` and `PDFCROWD_API_KEY`, or save your
40
+ account with `pdfcrowd auth login`. For unattended login, set both environment
41
+ variables and run `pdfcrowd auth login --non-interactive`. Conversions never
42
+ prompt for credentials.
43
+
44
+ Environment credentials take priority over the saved account. To use demo
45
+ even when personal credentials are saved:
46
+
47
+ ```sh
48
+ PDFCROWD_USERNAME=demo PDFCROWD_API_KEY=demo \
49
+ html2pdf --output example.pdf https://example.com
50
+ ```
51
+
52
+ Long options use `--name`. Account credentials are not accepted as arguments.
53
+ Boolean conversion options accept `true` or `false`. Compound option arguments
54
+ are separate values: `--page-margins 10mm 10mm 10mm 10mm`. Quote each value
55
+ individually when needed; semicolons inside values are preserved. Use
56
+ `--option=VALUE` for values that match an option name. Compound options can
57
+ also take a JSON array: `--http-auth='["username", "password"]'`.
58
+
59
+ `help`, version, and JSON help work offline. Conversion output uses
60
+ `--output FILE` or redirected stdout; only `pdf2text` writes results directly
61
+ to a terminal. Failed file conversions preserve the previous output.
62
+ Options can appear between input files:
63
+
64
+ ```sh
65
+ pdfcrowd pdf2pdf first.pdf -o merged.pdf second.pdf
66
+ ```
67
+
68
+ Diagnostics use stderr. Service errors preserve the SDK's complete message,
69
+ including URLs, option values, and documentation links.
70
+ Exit status 1 means an output
71
+ pipe closed; Ctrl-C exits with status 130.
72
+
73
+ Converter settings are read from a per-converter sectionless INI file, but
74
+ command invocations never automatically save settings. `--init-config` creates
75
+ a commented starter; `--show-config` inspects effective defaults; `--no-config`
76
+ bypasses saved conversion defaults while keeping credentials available.
77
+ `--show-config` lists conversion settings from the file and command line;
78
+ other options use their API defaults. Compound configuration values use JSON
79
+ arrays of strings. `auth login` explicitly saves shared credentials;
80
+ `auth logout` removes them. Use `help configuration` and `help authentication`
81
+ for paths, precedence, examples, and details.
82
+
83
+ For example:
84
+
85
+ ```sh
86
+ pdfcrowd html2pdf --init-config
87
+ # Open the created file and uncomment the settings you want to use.
88
+ pdfcrowd html2pdf --show-config
89
+ pdfcrowd html2pdf help --init-config
90
+ ```
91
+
92
+ For scripts, integrations, and AI agents, `pdfcrowd html2pdf help --json`
93
+ describes available options, argument types, defaults, and exit codes as JSON.
94
+ Use `pdfcrowd help --json` for all converters.
95
+
96
+ pipx and uv expose `pdfcrowd` and all eight converter commands. The CLI
97
+ installation includes the compatible SDK automatically.
98
+
99
+ The Python SDK contains only the API clients. All commands are provided by
100
+ `pdfcrowd-cli`, so the SDK and CLI can share a virtual environment:
101
+
102
+ ```sh
103
+ python -m pip install pdfcrowd-cli
104
+ html2pdf --help
105
+ ```
106
+
107
+ This public source builds independently of the private generator checkout:
108
+
109
+ ```sh
110
+ python -m pip install .
111
+ make build
112
+ ```
@@ -0,0 +1,16 @@
1
+ pdfcrowd_cli/__init__.py,sha256=HEsPJe7uOwJ9JkLw3flOPt_zYly_uemsmk00_Z_35bQ,1172
2
+ pdfcrowd_cli/__main__.py,sha256=VdO8UKRaPmhkzGaU82myBFhkdQTumCvDzljh8z8j2o4,1171
3
+ pdfcrowd_cli/adapter.py,sha256=YDfwR0DrqRsorue00CizCCg36Yc8cz8N4LXc4Kiq7MQ,5940
4
+ pdfcrowd_cli/catalog.py,sha256=AYobCGFuGN4RiHA3tPFA2dzTuIBC23hyLUHnTMe2ZUU,1797
5
+ pdfcrowd_cli/command-spec.json,sha256=gi4xG6v_cmiEwq9tK0Hr3YYS35tW9a0Ts1Gu8La5Rzw,585697
6
+ pdfcrowd_cli/entrypoints.py,sha256=u1iiQVLQkqDuUHMtl3vo7-897JYdQL-QL6Nvy_H2UKs,8850
7
+ pdfcrowd_cli/help.py,sha256=lmP0YbEyt-vPi6-J93sxA5Kz6SwhJBdilgZ-nmR6KuI,19679
8
+ pdfcrowd_cli/parser.py,sha256=Eq3DrVfjhxdsSZKhX4FJPXi3EgKvfSMQJmp8PqEzyTQ,8415
9
+ pdfcrowd_cli/state.py,sha256=rhh1CSQBwne_q9qDTwx8Tow-UNrLxg-MoSvHgQHd0LU,16308
10
+ pdfcrowd_cli/windows.py,sha256=_7BNRSY3Fqrlt-6ChWT76kvbCDHn1lg2nnG1IXBQFvU,7378
11
+ pdfcrowd_cli-1.0.0.dist-info/LICENSE,sha256=0LSvXvYHjr5lcJ--6Ls67xN9PHiIpmgFs8S1MIo4YKs,1061
12
+ pdfcrowd_cli-1.0.0.dist-info/METADATA,sha256=kzIE8UGmvzKX97vVl8Cd6uS_vcI3nNi0iR-3dKGTjWc,3994
13
+ pdfcrowd_cli-1.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
14
+ pdfcrowd_cli-1.0.0.dist-info/entry_points.txt,sha256=ihqC11MHeM0jQP37OYxpiOYqWO-FT8hMyC51hQqgzpY,435
15
+ pdfcrowd_cli-1.0.0.dist-info/top_level.txt,sha256=FnsOZBTnncYSPU9Zqgzbnp46GzHwrqBYSRdpDQLS1JA,13
16
+ pdfcrowd_cli-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.42.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,10 @@
1
+ [console_scripts]
2
+ html2image = pdfcrowd_cli.entrypoints:html2image
3
+ html2pdf = pdfcrowd_cli.entrypoints:html2pdf
4
+ image2image = pdfcrowd_cli.entrypoints:image2image
5
+ image2pdf = pdfcrowd_cli.entrypoints:image2pdf
6
+ pdf2html = pdfcrowd_cli.entrypoints:pdf2html
7
+ pdf2image = pdfcrowd_cli.entrypoints:pdf2image
8
+ pdf2pdf = pdfcrowd_cli.entrypoints:pdf2pdf
9
+ pdf2text = pdfcrowd_cli.entrypoints:pdf2text
10
+ pdfcrowd = pdfcrowd_cli.entrypoints:cli_main
@@ -0,0 +1 @@
1
+ pdfcrowd_cli