nodebb-plugin-pdf-secure 1.1.4 → 1.1.6
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.
- package/lib/controllers.js +26 -2
- package/package.json +1 -1
- package/static/lib/main.js +45 -70
- package/static/lib/viewer.js +63 -3
- package/static/style.less +136 -1
package/lib/controllers.js
CHANGED
|
@@ -34,14 +34,38 @@ Controllers.servePdfBinary = async function (req, res) {
|
|
|
34
34
|
pdfBuffer = await pdfHandler.getSinglePagePdf(data.file);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// PARTIAL XOR Encryption: Sadece ilk ve son 8KB'ı şifrele
|
|
38
|
+
// Bu, PDF'yi açılamaz hale getirir ama performans etkisi minimal
|
|
39
|
+
const encryptionKey = Buffer.from(nonce.slice(0, 8));
|
|
40
|
+
const encryptedBuffer = Buffer.from(pdfBuffer); // Copy
|
|
41
|
+
const chunkSize = 8192; // 8KB
|
|
42
|
+
|
|
43
|
+
// İlk 8KB'ı şifrele (PDF header - CRITICAL)
|
|
44
|
+
const headerEnd = Math.min(chunkSize, pdfBuffer.length);
|
|
45
|
+
for (let i = 0; i < headerEnd; i++) {
|
|
46
|
+
encryptedBuffer[i] = pdfBuffer[i] ^ encryptionKey[i % encryptionKey.length];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Son 8KB'ı şifrele (PDF xref table - CRITICAL)
|
|
50
|
+
const footerStart = Math.max(0, pdfBuffer.length - chunkSize);
|
|
51
|
+
for (let i = footerStart; i < pdfBuffer.length; i++) {
|
|
52
|
+
encryptedBuffer[i] = pdfBuffer[i] ^ encryptionKey[i % encryptionKey.length];
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
res.set({
|
|
38
56
|
'Content-Type': 'application/octet-stream',
|
|
39
|
-
'Cache-Control': 'no-store, no-cache, must-revalidate, private',
|
|
57
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, private, max-age=0',
|
|
58
|
+
'Pragma': 'no-cache',
|
|
59
|
+
'Expires': '0',
|
|
40
60
|
'X-Content-Type-Options': 'nosniff',
|
|
61
|
+
'X-Download-Options': 'noopen',
|
|
62
|
+
'X-Frame-Options': 'SAMEORIGIN',
|
|
63
|
+
'Content-Security-Policy': "default-src 'none'",
|
|
41
64
|
'Content-Disposition': 'inline',
|
|
65
|
+
'X-PDF-Encrypted': '1', // İşaretleyici
|
|
42
66
|
});
|
|
43
67
|
|
|
44
|
-
return res.send(
|
|
68
|
+
return res.send(encryptedBuffer);
|
|
45
69
|
} catch (err) {
|
|
46
70
|
if (err.message === 'File not found') {
|
|
47
71
|
return res.status(404).json({ error: 'PDF not found' });
|
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -31,31 +31,61 @@ console.log('[PDF-Secure] main.js loaded');
|
|
|
31
31
|
var filename = parts[parts.length - 1];
|
|
32
32
|
console.log('[PDF-Secure] Embedding:', filename);
|
|
33
33
|
|
|
34
|
-
// Create inline viewer container
|
|
34
|
+
// Create inline viewer container with INLINE STYLES to ensure they work
|
|
35
35
|
var container = document.createElement('div');
|
|
36
36
|
container.className = 'pdf-secure-embed';
|
|
37
|
+
container.style.cssText = 'margin:16px 0;border-radius:12px;overflow:hidden;background:#1f1f1f;border:1px solid rgba(255,255,255,0.1);box-shadow:0 4px 20px rgba(0,0,0,0.25);';
|
|
37
38
|
|
|
38
|
-
// Header with filename
|
|
39
|
+
// Header with filename - ALL INLINE STYLES
|
|
39
40
|
var header = document.createElement('div');
|
|
40
41
|
header.className = 'pdf-secure-embed-header';
|
|
41
|
-
header.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:linear-gradient(135deg,#2d2d2d 0%,#252525 100%);border-bottom:1px solid rgba(255,255,255,0.08);';
|
|
43
|
+
|
|
44
|
+
var title = document.createElement('div');
|
|
45
|
+
title.className = 'pdf-secure-embed-title';
|
|
46
|
+
title.style.cssText = 'display:flex;align-items:center;gap:10px;color:#fff;font-size:14px;font-weight:500;';
|
|
47
|
+
|
|
48
|
+
// PDF icon with INLINE SIZE
|
|
49
|
+
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
50
|
+
icon.setAttribute('viewBox', '0 0 24 24');
|
|
51
|
+
icon.style.cssText = 'width:20px;height:20px;min-width:20px;max-width:20px;fill:#e81224;flex-shrink:0;';
|
|
52
|
+
icon.innerHTML = '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>';
|
|
53
|
+
|
|
54
|
+
var nameSpan = document.createElement('span');
|
|
55
|
+
nameSpan.style.cssText = 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:400px;';
|
|
56
|
+
nameSpan.textContent = decodeURIComponent(link.textContent || filename);
|
|
57
|
+
|
|
58
|
+
title.appendChild(icon);
|
|
59
|
+
title.appendChild(nameSpan);
|
|
60
|
+
|
|
61
|
+
// Actions (fullscreen button)
|
|
62
|
+
var actions = document.createElement('div');
|
|
63
|
+
actions.style.cssText = 'display:flex;gap:8px;';
|
|
64
|
+
|
|
65
|
+
var fullscreenBtn = document.createElement('button');
|
|
66
|
+
fullscreenBtn.className = 'pdf-secure-fullscreen-btn';
|
|
67
|
+
fullscreenBtn.title = 'Tam Ekran';
|
|
68
|
+
fullscreenBtn.style.cssText = 'display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:rgba(255,255,255,0.08);border:none;border-radius:6px;cursor:pointer;';
|
|
69
|
+
|
|
70
|
+
var fullscreenIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
71
|
+
fullscreenIcon.setAttribute('viewBox', '0 0 24 24');
|
|
72
|
+
fullscreenIcon.style.cssText = 'width:18px;height:18px;min-width:18px;max-width:18px;fill:#fff;';
|
|
73
|
+
fullscreenIcon.innerHTML = '<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>';
|
|
74
|
+
fullscreenBtn.appendChild(fullscreenIcon);
|
|
75
|
+
actions.appendChild(fullscreenBtn);
|
|
76
|
+
|
|
77
|
+
header.appendChild(title);
|
|
78
|
+
header.appendChild(actions);
|
|
51
79
|
container.appendChild(header);
|
|
52
80
|
|
|
53
|
-
// Iframe
|
|
81
|
+
// Iframe wrapper
|
|
54
82
|
var iframeWrapper = document.createElement('div');
|
|
55
83
|
iframeWrapper.className = 'pdf-secure-embed-body';
|
|
84
|
+
iframeWrapper.style.cssText = 'position:relative;width:100%;height:600px;background:#525659;';
|
|
56
85
|
|
|
57
86
|
var iframe = document.createElement('iframe');
|
|
58
87
|
iframe.className = 'pdf-secure-iframe';
|
|
88
|
+
iframe.style.cssText = 'width:100%;height:100%;border:none;display:block;';
|
|
59
89
|
iframe.src = config.relative_path + '/plugins/pdf-secure/viewer?file=' + encodeURIComponent(filename);
|
|
60
90
|
iframe.setAttribute('frameborder', '0');
|
|
61
91
|
iframe.setAttribute('allowfullscreen', 'true');
|
|
@@ -65,7 +95,7 @@ console.log('[PDF-Secure] main.js loaded');
|
|
|
65
95
|
container.appendChild(iframeWrapper);
|
|
66
96
|
|
|
67
97
|
// Fullscreen button handler
|
|
68
|
-
|
|
98
|
+
fullscreenBtn.addEventListener('click', function () {
|
|
69
99
|
if (iframe.requestFullscreen) {
|
|
70
100
|
iframe.requestFullscreen();
|
|
71
101
|
} else if (iframe.webkitRequestFullscreen) {
|
|
@@ -75,63 +105,8 @@ console.log('[PDF-Secure] main.js loaded');
|
|
|
75
105
|
}
|
|
76
106
|
});
|
|
77
107
|
|
|
78
|
-
|
|
79
|
-
var uploadContainer = link.closest('.upload-container, .uploaded-file, .attachment, [data-upload], .img-responsive');
|
|
80
|
-
var nodebbPreview = link.closest('p, div');
|
|
81
|
-
|
|
82
|
-
// Also look for sibling image/icon that NodeBB might show
|
|
83
|
-
var prevSibling = link.previousElementSibling;
|
|
84
|
-
var parentParagraph = link.parentElement;
|
|
85
|
-
|
|
86
|
-
// If the link is inside a paragraph with just the link and maybe an icon, replace the whole paragraph
|
|
87
|
-
if (parentParagraph && parentParagraph.tagName === 'P') {
|
|
88
|
-
var textContent = parentParagraph.textContent.trim();
|
|
89
|
-
var linkText = link.textContent.trim();
|
|
90
|
-
// If paragraph only contains the link text (maybe with some whitespace), replace the whole paragraph
|
|
91
|
-
if (textContent === linkText || textContent === filename || textContent === decodeURIComponent(filename)) {
|
|
92
|
-
parentParagraph.replaceWith(container);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Hide any preceding SVG or image that looks like a file icon
|
|
98
|
-
if (prevSibling && (prevSibling.tagName === 'SVG' || prevSibling.tagName === 'IMG' || prevSibling.classList.contains('file-icon'))) {
|
|
99
|
-
prevSibling.style.display = 'none';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// If there's an upload container, replace it entirely
|
|
103
|
-
if (uploadContainer) {
|
|
104
|
-
uploadContainer.replaceWith(container);
|
|
105
|
-
} else {
|
|
106
|
-
link.replaceWith(container);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Also hide any remaining large file preview icons that NodeBB might render
|
|
112
|
-
postContents.forEach(function (content) {
|
|
113
|
-
// Hide default NodeBB file preview containers for PDFs
|
|
114
|
-
var fileIcons = content.querySelectorAll('img[src*=".pdf"], svg.file-icon, .file-preview');
|
|
115
|
-
fileIcons.forEach(function (icon) {
|
|
116
|
-
// Check if this is near a pdf-secure-embed
|
|
117
|
-
var nearEmbed = icon.closest('.pdf-secure-embed');
|
|
118
|
-
if (!nearEmbed) {
|
|
119
|
-
// Check if there's a pdf-secure-embed nearby
|
|
120
|
-
var parent = icon.parentElement;
|
|
121
|
-
if (parent) {
|
|
122
|
-
var siblingEmbed = parent.querySelector('.pdf-secure-embed');
|
|
123
|
-
if (siblingEmbed) {
|
|
124
|
-
icon.style.display = 'none';
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
108
|
+
link.replaceWith(container);
|
|
128
109
|
});
|
|
129
110
|
});
|
|
130
111
|
}
|
|
131
|
-
|
|
132
|
-
function escapeHtml(str) {
|
|
133
|
-
var d = document.createElement('div');
|
|
134
|
-
d.textContent = str;
|
|
135
|
-
return d.innerHTML;
|
|
136
|
-
}
|
|
137
112
|
})();
|
package/static/lib/viewer.js
CHANGED
|
@@ -32,6 +32,7 @@ function interceptPdfLinks() {
|
|
|
32
32
|
|
|
33
33
|
var container = document.createElement('div');
|
|
34
34
|
container.className = 'pdf-secure-inline';
|
|
35
|
+
container.setAttribute('tabindex', '0'); // Focus için gerekli
|
|
35
36
|
container.innerHTML =
|
|
36
37
|
'<div class="pdf-secure-inline-header">' +
|
|
37
38
|
'<i class="fa fa-file-pdf-o"></i> ' +
|
|
@@ -40,7 +41,10 @@ function interceptPdfLinks() {
|
|
|
40
41
|
'<div class="pdf-secure-inline-body">' +
|
|
41
42
|
'<div class="pdf-secure-loading">Loading PDF...</div>' +
|
|
42
43
|
'<div class="pdf-secure-error"></div>' +
|
|
43
|
-
'<
|
|
44
|
+
'<div class="pdf-secure-canvas-wrapper">' +
|
|
45
|
+
'<canvas class="pdf-secure-canvas"></canvas>' +
|
|
46
|
+
'<div class="pdf-secure-overlay"></div>' +
|
|
47
|
+
'</div>' +
|
|
44
48
|
'</div>' +
|
|
45
49
|
'<div class="pdf-secure-inline-footer">' +
|
|
46
50
|
'<button class="pdf-secure-prev" disabled>‹ Prev</button>' +
|
|
@@ -106,8 +110,29 @@ async function loadPdf(container, filename) {
|
|
|
106
110
|
return;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
var
|
|
110
|
-
console.log('[PDF-Secure][Viewer] Step 2 -
|
|
113
|
+
var encryptedArrayBuffer = await pdfRes.arrayBuffer();
|
|
114
|
+
console.log('[PDF-Secure][Viewer] Step 2 - Encrypted data loaded:', encryptedArrayBuffer.byteLength, 'bytes');
|
|
115
|
+
|
|
116
|
+
// PARTIAL XOR Decryption: Sadece ilk ve son 8KB'ı decrypt et
|
|
117
|
+
var encryptedBytes = new Uint8Array(encryptedArrayBuffer);
|
|
118
|
+
var encryptionKey = new TextEncoder().encode(nonce.slice(0, 8));
|
|
119
|
+
var decryptedBytes = new Uint8Array(encryptedBytes); // Copy
|
|
120
|
+
var chunkSize = 8192; // 8KB
|
|
121
|
+
|
|
122
|
+
// İlk 8KB'ı decrypt et (PDF header)
|
|
123
|
+
var headerEnd = Math.min(chunkSize, encryptedBytes.length);
|
|
124
|
+
for (var i = 0; i < headerEnd; i++) {
|
|
125
|
+
decryptedBytes[i] = encryptedBytes[i] ^ encryptionKey[i % encryptionKey.length];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Son 8KB'ı decrypt et (PDF xref table)
|
|
129
|
+
var footerStart = Math.max(0, encryptedBytes.length - chunkSize);
|
|
130
|
+
for (var i = footerStart; i < encryptedBytes.length; i++) {
|
|
131
|
+
decryptedBytes[i] = encryptedBytes[i] ^ encryptionKey[i % encryptionKey.length];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var pdfArrayBuffer = decryptedBytes.buffer;
|
|
135
|
+
console.log('[PDF-Secure][Viewer] Step 2 - Decrypted PDF:', pdfArrayBuffer.byteLength, 'bytes');
|
|
111
136
|
|
|
112
137
|
// Step 3: Render PDF
|
|
113
138
|
console.log('[PDF-Secure][Viewer] Step 3 - Rendering...');
|
|
@@ -122,6 +147,41 @@ async function loadPdf(container, filename) {
|
|
|
122
147
|
container.addEventListener('contextmenu', function (e) { e.preventDefault(); });
|
|
123
148
|
container.addEventListener('dragstart', function (e) { e.preventDefault(); });
|
|
124
149
|
container.addEventListener('selectstart', function (e) { e.preventDefault(); });
|
|
150
|
+
container.addEventListener('copy', function (e) { e.preventDefault(); });
|
|
151
|
+
|
|
152
|
+
// Keyboard shortcuts engelleme (Ctrl+S, Ctrl+P, Ctrl+C, Print Screen)
|
|
153
|
+
container.addEventListener('keydown', function (e) {
|
|
154
|
+
// Ctrl/Cmd + S (Save)
|
|
155
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
console.log('[PDF-Secure] Save blocked');
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
// Ctrl/Cmd + P (Print)
|
|
161
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
console.log('[PDF-Secure] Print blocked');
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
// Ctrl/Cmd + C (Copy)
|
|
167
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
console.log('[PDF-Secure] Copy blocked');
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
// Print Screen
|
|
173
|
+
if (e.key === 'PrintScreen') {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
console.log('[PDF-Secure] PrintScreen blocked');
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
// F12 (DevTools)
|
|
179
|
+
if (e.key === 'F12') {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
console.log('[PDF-Secure] F12 blocked');
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
125
185
|
|
|
126
186
|
var ctx = canvas.getContext('2d');
|
|
127
187
|
var currentPage = 1;
|
package/static/style.less
CHANGED
|
@@ -136,7 +136,142 @@
|
|
|
136
136
|
|
|
137
137
|
/* Anti-print */
|
|
138
138
|
@media print {
|
|
139
|
-
.pdf-secure-embed
|
|
139
|
+
.pdf-secure-embed,
|
|
140
|
+
.pdf-secure-inline {
|
|
140
141
|
display: none !important;
|
|
141
142
|
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Inline PDF Viewer Styles */
|
|
146
|
+
.pdf-secure-inline {
|
|
147
|
+
margin: 20px 0;
|
|
148
|
+
border: 1px solid #ddd;
|
|
149
|
+
border-radius: 8px;
|
|
150
|
+
overflow: hidden;
|
|
151
|
+
background: #f5f5f5;
|
|
152
|
+
user-select: none;
|
|
153
|
+
-webkit-user-select: none;
|
|
154
|
+
-moz-user-select: none;
|
|
155
|
+
-ms-user-select: none;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.pdf-secure-inline-header {
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 8px;
|
|
162
|
+
padding: 12px 16px;
|
|
163
|
+
background: linear-gradient(to bottom, #fff, #f8f8f8);
|
|
164
|
+
border-bottom: 1px solid #ddd;
|
|
165
|
+
font-weight: 500;
|
|
166
|
+
color: #333;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.pdf-secure-inline-header i {
|
|
170
|
+
color: #e81224;
|
|
171
|
+
font-size: 18px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.pdf-secure-filename {
|
|
175
|
+
flex: 1;
|
|
176
|
+
overflow: hidden;
|
|
177
|
+
text-overflow: ellipsis;
|
|
178
|
+
white-space: nowrap;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.pdf-secure-inline-body {
|
|
182
|
+
position: relative;
|
|
183
|
+
min-height: 400px;
|
|
184
|
+
background: #fff;
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
justify-content: center;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.pdf-secure-loading {
|
|
191
|
+
position: absolute;
|
|
192
|
+
top: 50%;
|
|
193
|
+
left: 50%;
|
|
194
|
+
transform: translate(-50%, -50%);
|
|
195
|
+
color: #666;
|
|
196
|
+
font-size: 14px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.pdf-secure-error {
|
|
200
|
+
display: none;
|
|
201
|
+
position: absolute;
|
|
202
|
+
top: 50%;
|
|
203
|
+
left: 50%;
|
|
204
|
+
transform: translate(-50%, -50%);
|
|
205
|
+
color: #e81224;
|
|
206
|
+
font-size: 14px;
|
|
207
|
+
text-align: center;
|
|
208
|
+
padding: 20px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.pdf-secure-canvas-wrapper {
|
|
212
|
+
position: relative;
|
|
213
|
+
display: inline-block;
|
|
214
|
+
/* Canvas wrapper'ı güvenlik için kapsayıcı */
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.pdf-secure-canvas {
|
|
218
|
+
display: none;
|
|
219
|
+
max-width: 100%;
|
|
220
|
+
height: auto;
|
|
221
|
+
/* Canvas üzerinde sağ tık ve sürüklemeyi engelle */
|
|
222
|
+
pointer-events: auto;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.pdf-secure-overlay {
|
|
226
|
+
position: absolute;
|
|
227
|
+
top: 0;
|
|
228
|
+
left: 0;
|
|
229
|
+
right: 0;
|
|
230
|
+
bottom: 0;
|
|
231
|
+
background: transparent;
|
|
232
|
+
/* Görünmez koruma katmanı - tüm mouse eventlerini yakalar */
|
|
233
|
+
pointer-events: auto;
|
|
234
|
+
cursor: default;
|
|
235
|
+
z-index: 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.pdf-secure-inline-footer {
|
|
239
|
+
display: none;
|
|
240
|
+
align-items: center;
|
|
241
|
+
justify-content: center;
|
|
242
|
+
gap: 16px;
|
|
243
|
+
padding: 12px;
|
|
244
|
+
background: #f8f8f8;
|
|
245
|
+
border-top: 1px solid #ddd;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.pdf-secure-prev,
|
|
249
|
+
.pdf-secure-next {
|
|
250
|
+
padding: 6px 12px;
|
|
251
|
+
background: #fff;
|
|
252
|
+
border: 1px solid #ddd;
|
|
253
|
+
border-radius: 4px;
|
|
254
|
+
cursor: pointer;
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
color: #333;
|
|
257
|
+
transition: all 0.2s;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.pdf-secure-prev:hover:not(:disabled),
|
|
261
|
+
.pdf-secure-next:hover:not(:disabled) {
|
|
262
|
+
background: #f0f0f0;
|
|
263
|
+
border-color: #999;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.pdf-secure-prev:disabled,
|
|
267
|
+
.pdf-secure-next:disabled {
|
|
268
|
+
opacity: 0.4;
|
|
269
|
+
cursor: not-allowed;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.pdf-secure-page-info {
|
|
273
|
+
font-size: 14px;
|
|
274
|
+
color: #666;
|
|
275
|
+
min-width: 60px;
|
|
276
|
+
text-align: center;
|
|
142
277
|
}
|