mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 11:00:47 +02:00
Booklet and server sign (#4371)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: a <a> Co-authored-by: ConnorYoh <[email protected]> Co-authored-by: Reece Browne <[email protected]>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
a <a>
ConnorYoh
Reece Browne
parent
c76edebf0f
commit
46a4a978fc
@@ -15,7 +15,7 @@ interface ButtonSelectorProps<T> {
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
const ButtonSelector = <T extends string>({
|
||||
const ButtonSelector = <T extends string | number>({
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
@@ -45,7 +45,10 @@ const ButtonSelector = <T extends string>({
|
||||
flex: fullWidth ? 1 : undefined,
|
||||
height: 'auto',
|
||||
minHeight: '2.5rem',
|
||||
fontSize: 'var(--mantine-font-size-sm)'
|
||||
fontSize: 'var(--mantine-font-size-sm)',
|
||||
lineHeight: '1.4',
|
||||
paddingTop: '0.5rem',
|
||||
paddingBottom: '0.5rem'
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Stack, Text, Divider, Collapse, Button, NumberInput } from "@mantine/core";
|
||||
import { BookletImpositionParameters } from "../../../hooks/tools/bookletImposition/useBookletImpositionParameters";
|
||||
import ButtonSelector from "../../shared/ButtonSelector";
|
||||
|
||||
interface BookletImpositionSettingsProps {
|
||||
parameters: BookletImpositionParameters;
|
||||
onParameterChange: (key: keyof BookletImpositionParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const BookletImpositionSettings = ({ parameters, onParameterChange, disabled = false }: BookletImpositionSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Divider ml='-md'></Divider>
|
||||
|
||||
|
||||
{/* Double Sided */}
|
||||
<Stack gap="sm">
|
||||
<label
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 'var(--mantine-spacing-xs)' }}
|
||||
title={t('bookletImposition.doubleSided.tooltip', 'Creates both front and back sides for proper booklet printing')}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={parameters.doubleSided}
|
||||
onChange={(e) => {
|
||||
const isDoubleSided = e.target.checked;
|
||||
onParameterChange('doubleSided', isDoubleSided);
|
||||
// Reset to BOTH when turning double-sided back on
|
||||
if (isDoubleSided) {
|
||||
onParameterChange('duplexPass', 'BOTH');
|
||||
} else {
|
||||
// Default to FIRST pass when going to manual duplex
|
||||
onParameterChange('duplexPass', 'FIRST');
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Text size="sm">{t('bookletImposition.doubleSided.label', 'Double-sided printing')}</Text>
|
||||
</label>
|
||||
|
||||
{/* Manual Duplex Pass Selection - only show when double-sided is OFF */}
|
||||
{!parameters.doubleSided && (
|
||||
<Stack gap="xs" ml="lg">
|
||||
<Text size="sm" fw={500} c="orange">
|
||||
{t('bookletImposition.manualDuplex.title', 'Manual Duplex Mode')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{t('bookletImposition.manualDuplex.instructions', 'For printers without automatic duplex. You\'ll need to run this twice:')}
|
||||
</Text>
|
||||
|
||||
<ButtonSelector
|
||||
label={t('bookletImposition.duplexPass.label', 'Print Pass')}
|
||||
value={parameters.duplexPass}
|
||||
onChange={(value) => onParameterChange('duplexPass', value)}
|
||||
options={[
|
||||
{ value: 'FIRST', label: t('bookletImposition.duplexPass.first', '1st Pass') },
|
||||
{ value: 'SECOND', label: t('bookletImposition.duplexPass.second', '2nd Pass') }
|
||||
]}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Text size="xs" c="blue" fs="italic">
|
||||
{parameters.duplexPass === 'FIRST'
|
||||
? t('bookletImposition.duplexPass.firstInstructions', 'Prints front sides → stack face-down → run again with 2nd Pass')
|
||||
: t('bookletImposition.duplexPass.secondInstructions', 'Load printed stack face-down → prints back sides')
|
||||
}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Advanced Options */}
|
||||
<Stack gap="sm">
|
||||
<Button
|
||||
variant="subtle"
|
||||
onClick={() => setAdvancedOpen(!advancedOpen)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{t('bookletImposition.advanced.toggle', 'Advanced Options')} {advancedOpen ? '▲' : '▼'}
|
||||
</Button>
|
||||
|
||||
<Collapse in={advancedOpen}>
|
||||
<Stack gap="md" mt="md">
|
||||
{/* Right-to-Left Binding */}
|
||||
<label
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 'var(--mantine-spacing-xs)' }}
|
||||
title={t('bookletImposition.rtlBinding.tooltip', 'For Arabic, Hebrew, or other right-to-left languages')}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={parameters.spineLocation === 'RIGHT'}
|
||||
onChange={(e) => onParameterChange('spineLocation', e.target.checked ? 'RIGHT' : 'LEFT')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Text size="sm">{t('bookletImposition.rtlBinding.label', 'Right-to-left binding')}</Text>
|
||||
</label>
|
||||
|
||||
{/* Add Border Option */}
|
||||
<label
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 'var(--mantine-spacing-xs)' }}
|
||||
title={t('bookletImposition.addBorder.tooltip', 'Adds borders around each page section to help with cutting and alignment')}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={parameters.addBorder}
|
||||
onChange={(e) => onParameterChange('addBorder', e.target.checked)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Text size="sm">{t('bookletImposition.addBorder.label', 'Add borders around pages')}</Text>
|
||||
</label>
|
||||
|
||||
{/* Gutter Margin */}
|
||||
<Stack gap="xs">
|
||||
<label
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 'var(--mantine-spacing-xs)' }}
|
||||
title={t('bookletImposition.addGutter.tooltip', 'Adds inner margin space for binding')}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={parameters.addGutter}
|
||||
onChange={(e) => onParameterChange('addGutter', e.target.checked)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Text size="sm">{t('bookletImposition.addGutter.label', 'Add gutter margin')}</Text>
|
||||
</label>
|
||||
|
||||
{parameters.addGutter && (
|
||||
<NumberInput
|
||||
label={t('bookletImposition.gutterSize.label', 'Gutter size (points)')}
|
||||
value={parameters.gutterSize}
|
||||
onChange={(value) => onParameterChange('gutterSize', value || 12)}
|
||||
min={6}
|
||||
max={72}
|
||||
step={6}
|
||||
disabled={disabled}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{/* Flip on Short Edge */}
|
||||
<label
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 'var(--mantine-spacing-xs)' }}
|
||||
title={!parameters.doubleSided
|
||||
? t('bookletImposition.flipOnShortEdge.manualNote', 'Not needed in manual mode - you flip the stack yourself')
|
||||
: t('bookletImposition.flipOnShortEdge.tooltip', 'Enable for short-edge duplex printing (automatic duplex only - ignored in manual mode)')
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={parameters.flipOnShortEdge}
|
||||
onChange={(e) => onParameterChange('flipOnShortEdge', e.target.checked)}
|
||||
disabled={disabled || !parameters.doubleSided}
|
||||
/>
|
||||
<Text size="sm" c={!parameters.doubleSided ? "dimmed" : undefined}>
|
||||
{t('bookletImposition.flipOnShortEdge.label', 'Flip on short edge')}
|
||||
</Text>
|
||||
</label>
|
||||
|
||||
{/* Paper Size Note */}
|
||||
<Text size="xs" c="dimmed" fs="italic">
|
||||
{t('bookletImposition.paperSizeNote', 'Paper size is automatically derived from your first page.')}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Collapse>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookletImpositionSettings;
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Stack, Text, TextInput } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CertSignParameters } from "../../../hooks/tools/certSign/useCertSignParameters";
|
||||
import FileUploadButton from "../../shared/FileUploadButton";
|
||||
|
||||
interface CertificateFilesSettingsProps {
|
||||
parameters: CertSignParameters;
|
||||
onParameterChange: (key: keyof CertSignParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CertificateFilesSettings = ({ parameters, onParameterChange, disabled = false }: CertificateFilesSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{/* Certificate Files based on type */}
|
||||
{parameters.certType === 'PEM' && (
|
||||
<Stack gap="sm">
|
||||
<FileUploadButton
|
||||
file={parameters.privateKeyFile}
|
||||
onChange={(file) => onParameterChange('privateKeyFile', file || undefined)}
|
||||
accept=".pem,.der,.key"
|
||||
disabled={disabled}
|
||||
placeholder={t('certSign.choosePrivateKey', 'Choose Private Key File')}
|
||||
/>
|
||||
{parameters.privateKeyFile && (
|
||||
<FileUploadButton
|
||||
file={parameters.certFile}
|
||||
onChange={(file) => onParameterChange('certFile', file || undefined)}
|
||||
accept=".pem,.der,.crt,.cer"
|
||||
disabled={disabled}
|
||||
placeholder={t('certSign.chooseCertificate', 'Choose Certificate File')}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{parameters.certType === 'PKCS12' && (
|
||||
<FileUploadButton
|
||||
file={parameters.p12File}
|
||||
onChange={(file) => onParameterChange('p12File', file || undefined)}
|
||||
accept=".p12"
|
||||
disabled={disabled}
|
||||
placeholder={t('certSign.chooseP12File', 'Choose PKCS12 File')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{parameters.certType === 'PFX' && (
|
||||
<FileUploadButton
|
||||
file={parameters.p12File}
|
||||
onChange={(file) => onParameterChange('p12File', file || undefined)}
|
||||
accept=".pfx"
|
||||
disabled={disabled}
|
||||
placeholder={t('certSign.choosePfxFile', 'Choose PFX File')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{parameters.certType === 'JKS' && (
|
||||
<FileUploadButton
|
||||
file={parameters.jksFile}
|
||||
onChange={(file) => onParameterChange('jksFile', file || undefined)}
|
||||
accept=".jks,.keystore"
|
||||
disabled={disabled}
|
||||
placeholder={t('certSign.chooseJksFile', 'Choose JKS File')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{parameters.signMode === 'AUTO' && (
|
||||
<Text c="dimmed" size="sm">
|
||||
{t('certSign.serverCertMessage', 'Using server certificate - no files or password required')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* Password - only show when files are uploaded */}
|
||||
{parameters.certType && (
|
||||
(parameters.certType === 'PEM' && parameters.privateKeyFile && parameters.certFile) ||
|
||||
(parameters.certType === 'PKCS12' && parameters.p12File) ||
|
||||
(parameters.certType === 'PFX' && parameters.p12File) ||
|
||||
(parameters.certType === 'JKS' && parameters.jksFile)
|
||||
) && (
|
||||
<TextInput
|
||||
label={t('certSign.password', 'Certificate Password')}
|
||||
placeholder={t('certSign.passwordOptional', 'Leave empty if no password')}
|
||||
type="password"
|
||||
value={parameters.password}
|
||||
onChange={(event) => onParameterChange('password', event.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificateFilesSettings;
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Stack, Button } from "@mantine/core";
|
||||
import { CertSignParameters } from "../../../hooks/tools/certSign/useCertSignParameters";
|
||||
|
||||
interface CertificateFormatSettingsProps {
|
||||
parameters: CertSignParameters;
|
||||
onParameterChange: (key: keyof CertSignParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CertificateFormatSettings = ({ parameters, onParameterChange, disabled = false }: CertificateFormatSettingsProps) => {
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||
{/* First row - PKCS#12 and PFX */}
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={parameters.certType === 'PKCS12' ? 'filled' : 'outline'}
|
||||
color={parameters.certType === 'PKCS12' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('certType', 'PKCS12')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
PKCS12
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant={parameters.certType === 'PFX' ? 'filled' : 'outline'}
|
||||
color={parameters.certType === 'PFX' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('certType', 'PFX')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
PFX
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
{/* Second row - PEM and JKS */}
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={parameters.certType === 'PEM' ? 'filled' : 'outline'}
|
||||
color={parameters.certType === 'PEM' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('certType', 'PEM')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
PEM
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant={parameters.certType === 'JKS' ? 'filled' : 'outline'}
|
||||
color={parameters.certType === 'JKS' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('certType', 'JKS')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
JKS
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificateFormatSettings;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Stack, Button } from "@mantine/core";
|
||||
import { CertSignParameters } from "../../../hooks/tools/certSign/useCertSignParameters";
|
||||
import { useAppConfig } from "../../../hooks/useAppConfig";
|
||||
|
||||
interface CertificateTypeSettingsProps {
|
||||
parameters: CertSignParameters;
|
||||
onParameterChange: (key: keyof CertSignParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CertificateTypeSettings = ({ parameters, onParameterChange, disabled = false }: CertificateTypeSettingsProps) => {
|
||||
const { config } = useAppConfig();
|
||||
const isServerCertificateEnabled = config?.serverCertificateEnabled ?? false;
|
||||
|
||||
// Reset to MANUAL if AUTO is selected but feature is disabled
|
||||
if (parameters.signMode === 'AUTO' && !isServerCertificateEnabled) {
|
||||
onParameterChange('signMode', 'MANUAL');
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={parameters.signMode === 'MANUAL' ? 'filled' : 'outline'}
|
||||
color={parameters.signMode === 'MANUAL' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => {
|
||||
onParameterChange('signMode', 'MANUAL');
|
||||
// Reset cert type when switching to manual
|
||||
if (parameters.signMode === 'AUTO') {
|
||||
onParameterChange('certType', '');
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
Manual
|
||||
</div>
|
||||
</Button>
|
||||
{isServerCertificateEnabled && (
|
||||
<Button
|
||||
variant={parameters.signMode === 'AUTO' ? 'filled' : 'outline'}
|
||||
color={parameters.signMode === 'AUTO' ? 'green' : 'var(--text-muted)'}
|
||||
onClick={() => {
|
||||
onParameterChange('signMode', 'AUTO');
|
||||
// Clear cert type and files when switching to auto
|
||||
onParameterChange('certType', '');
|
||||
}}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
Auto (server)
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificateTypeSettings;
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Stack, Text, Button, TextInput, NumberInput } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CertSignParameters } from "../../../hooks/tools/certSign/useCertSignParameters";
|
||||
|
||||
interface SignatureAppearanceSettingsProps {
|
||||
parameters: CertSignParameters;
|
||||
onParameterChange: (key: keyof CertSignParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const SignatureAppearanceSettings = ({ parameters, onParameterChange, disabled = false }: SignatureAppearanceSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{/* Signature Visibility */}
|
||||
<Stack gap="sm">
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={!parameters.showSignature ? 'filled' : 'outline'}
|
||||
color={!parameters.showSignature ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('showSignature', false)}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
{t('certSign.appearance.invisible', 'Invisible')}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant={parameters.showSignature ? 'filled' : 'outline'}
|
||||
color={parameters.showSignature ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('showSignature', true)}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
{t('certSign.appearance.visible', 'Visible')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
{/* Visible Signature Options */}
|
||||
{parameters.showSignature && (
|
||||
<Stack gap="sm">
|
||||
<Text size="sm" fw={500}>
|
||||
{t('certSign.appearance.options.title', 'Signature Details')}
|
||||
</Text>
|
||||
<TextInput
|
||||
label={t('certSign.reason', 'Reason')}
|
||||
value={parameters.reason}
|
||||
onChange={(event) => onParameterChange('reason', event.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('certSign.location', 'Location')}
|
||||
value={parameters.location}
|
||||
onChange={(event) => onParameterChange('location', event.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('certSign.name', 'Name')}
|
||||
value={parameters.name}
|
||||
onChange={(event) => onParameterChange('name', event.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t('certSign.pageNumber', 'Page Number')}
|
||||
value={parameters.pageNumber}
|
||||
onChange={(value) => onParameterChange('pageNumber', value || 1)}
|
||||
min={1}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Stack gap="xs">
|
||||
<Text size="sm" fw={500}>
|
||||
{t('certSign.logoTitle', 'Logo')}
|
||||
</Text>
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={!parameters.showLogo ? 'filled' : 'outline'}
|
||||
color={!parameters.showLogo ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('showLogo', false)}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
{t('certSign.noLogo', 'No Logo')}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant={parameters.showLogo ? 'filled' : 'outline'}
|
||||
color={parameters.showLogo ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('showLogo', true)}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
|
||||
{t('certSign.showLogo', 'Show Logo')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignatureAppearanceSettings;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useBookletImpositionTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("bookletImposition.tooltip.header.title", "Booklet Creation Guide")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("bookletImposition.tooltip.description.title", "What is Booklet Imposition?"),
|
||||
description: t("bookletImposition.tooltip.description.text", "Creates professional booklets by arranging pages in the correct printing order. Your PDF pages are placed 2-up on landscape sheets so when folded and bound, they read in proper sequence like a real book.")
|
||||
},
|
||||
{
|
||||
title: t("bookletImposition.tooltip.example.title", "Example: 8-Page Booklet"),
|
||||
description: t("bookletImposition.tooltip.example.text", "Your 8-page document becomes 2 sheets:"),
|
||||
bullets: [
|
||||
t("bookletImposition.tooltip.example.bullet1", "Sheet 1 Front: Pages 8, 1 | Back: Pages 2, 7"),
|
||||
t("bookletImposition.tooltip.example.bullet2", "Sheet 2 Front: Pages 6, 3 | Back: Pages 4, 5"),
|
||||
t("bookletImposition.tooltip.example.bullet3", "When folded & stacked: Reads 1→2→3→4→5→6→7→8")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("bookletImposition.tooltip.printing.title", "How to Print & Assemble"),
|
||||
description: t("bookletImposition.tooltip.printing.text", "Follow these steps for perfect booklets:"),
|
||||
bullets: [
|
||||
t("bookletImposition.tooltip.printing.bullet1", "Print double-sided with 'Flip on long edge'"),
|
||||
t("bookletImposition.tooltip.printing.bullet2", "Stack sheets in order, fold in half"),
|
||||
t("bookletImposition.tooltip.printing.bullet3", "Staple or bind along the folded spine"),
|
||||
t("bookletImposition.tooltip.printing.bullet4", "For short-edge printers: Enable 'Flip on short edge' option")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("bookletImposition.tooltip.manualDuplex.title", "Manual Duplex (Single-sided Printers)"),
|
||||
description: t("bookletImposition.tooltip.manualDuplex.text", "For printers without automatic duplex:"),
|
||||
bullets: [
|
||||
t("bookletImposition.tooltip.manualDuplex.bullet1", "Turn OFF 'Double-sided printing'"),
|
||||
t("bookletImposition.tooltip.manualDuplex.bullet2", "Select '1st Pass' → Print → Stack face-down"),
|
||||
t("bookletImposition.tooltip.manualDuplex.bullet3", "Select '2nd Pass' → Load stack → Print backs"),
|
||||
t("bookletImposition.tooltip.manualDuplex.bullet4", "Fold and assemble as normal")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("bookletImposition.tooltip.advanced.title", "Advanced Options"),
|
||||
description: t("bookletImposition.tooltip.advanced.text", "Fine-tune your booklet:"),
|
||||
bullets: [
|
||||
t("bookletImposition.tooltip.advanced.bullet1", "Right-to-Left Binding: For Arabic, Hebrew, or RTL languages"),
|
||||
t("bookletImposition.tooltip.advanced.bullet2", "Borders: Shows cut lines for trimming"),
|
||||
t("bookletImposition.tooltip.advanced.bullet3", "Gutter Margin: Adds space for binding/stapling"),
|
||||
t("bookletImposition.tooltip.advanced.bullet4", "Short-edge Flip: Only for automatic duplex printers")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useCertSignTooltips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("certSign.tooltip.header.title", "About Managing Signatures")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("certSign.tooltip.overview.title", "What can this tool do?"),
|
||||
description: t("certSign.tooltip.overview.text", "This tool lets you check if your PDFs are digitally signed and add new digital signatures. Digital signatures prove who created or approved a document and show if it has been changed since signing."),
|
||||
bullets: [
|
||||
t("certSign.tooltip.overview.bullet1", "Check existing signatures and their validity"),
|
||||
t("certSign.tooltip.overview.bullet2", "View detailed information about signers and certificates"),
|
||||
t("certSign.tooltip.overview.bullet3", "Add new digital signatures to secure your documents"),
|
||||
t("certSign.tooltip.overview.bullet4", "Multiple files supported with easy navigation")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.tooltip.validation.title", "Checking Signatures"),
|
||||
description: t("certSign.tooltip.validation.text", "When you check signatures, the tool tells you if they're valid, who signed the document, when it was signed, and whether the document has been changed since signing."),
|
||||
bullets: [
|
||||
t("certSign.tooltip.validation.bullet1", "Shows if signatures are valid or invalid"),
|
||||
t("certSign.tooltip.validation.bullet2", "Displays signer information and signing date"),
|
||||
t("certSign.tooltip.validation.bullet3", "Checks if the document was modified after signing"),
|
||||
t("certSign.tooltip.validation.bullet4", "Can use custom certificates for verification")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.tooltip.signing.title", "Adding Signatures"),
|
||||
description: t("certSign.tooltip.signing.text", "To sign a PDF, you need a digital certificate (like PEM, PKCS12, or JKS). You can choose to make the signature visible on the document or keep it invisible for security only."),
|
||||
bullets: [
|
||||
t("certSign.tooltip.signing.bullet1", "Supports PEM, PKCS12, JKS, and server certificate formats"),
|
||||
t("certSign.tooltip.signing.bullet2", "Option to show or hide signature on the PDF"),
|
||||
t("certSign.tooltip.signing.bullet3", "Add reason, location, and signer name"),
|
||||
t("certSign.tooltip.signing.bullet4", "Choose which page to place visible signatures"),
|
||||
t("certSign.tooltip.signing.bullet5", "Use server certificate for simple 'Sign with Stirling-PDF' option")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useCertificateTypeTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("certSign.certType.tooltip.header.title", "About Certificate Types")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("certSign.certType.tooltip.what.title", "What's a certificate?"),
|
||||
description: t("certSign.certType.tooltip.what.text", "It's a secure ID for your signature that proves you signed. Unless you're required to sign via certificate, we recommend using another secure method like Type, Draw, or Upload.")
|
||||
},
|
||||
{
|
||||
title: t("certSign.certType.tooltip.which.title", "Which option should I use?"),
|
||||
description: t("certSign.certType.tooltip.which.text", "Choose the format that matches your certificate file:"),
|
||||
bullets: [
|
||||
t("certSign.certType.tooltip.which.bullet1", "PKCS12 (.p12) – one combined file (most common)"),
|
||||
t("certSign.certType.tooltip.which.bullet2", "PFX (.pfx) – Microsoft's version of PKCS12"),
|
||||
t("certSign.certType.tooltip.which.bullet3", "PEM – separate private-key and certificate .pem files"),
|
||||
t("certSign.certType.tooltip.which.bullet4", "JKS – Java .jks keystore for dev / CI-CD workflows")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.certType.tooltip.convert.title", "Key not listed?"),
|
||||
description: t("certSign.certType.tooltip.convert.text", "Convert your file to a Java keystore (.jks) with keytool, then pick JKS.")
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useSignModeTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("certSign.signMode.tooltip.header.title", "About PDF Signatures")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("certSign.signMode.tooltip.overview.title", "How signatures work"),
|
||||
description: t("certSign.signMode.tooltip.overview.text", "Both modes seal the document (any edits are flagged as tampering) and record who/when/how for auditing. Viewer trust depends on the certificate chain.")
|
||||
},
|
||||
{
|
||||
title: t("certSign.signMode.tooltip.manual.title", "Manual - Bring your certificate"),
|
||||
description: t("certSign.signMode.tooltip.manual.text", "Use your own certificate files for brand-aligned identity. Can display <b>Trusted</b> when your CA/chain is recognized."),
|
||||
bullets: [
|
||||
t("certSign.signMode.tooltip.manual.use", "Use for: customer-facing, legal, compliance.")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.signMode.tooltip.auto.title", "Auto - Zero-setup, instant system seal"),
|
||||
description: t("certSign.signMode.tooltip.auto.text", "Signs with a server <b>self-signed</b> certificate. Same <b>tamper-evident seal</b> and <b>audit trail</b>; typically shows <b>Unverified</b> in viewers."),
|
||||
bullets: [
|
||||
t("certSign.signMode.tooltip.auto.use", "Use when: you need speed and consistent internal identity across reviews and records.")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.signMode.tooltip.rule.title", "Rule of thumb"),
|
||||
description: t("certSign.signMode.tooltip.rule.text", "Need recipient <b>Trusted</b> status? <b>Manual</b>. Need a fast, tamper-evident seal and audit trail with no setup? <b>Auto</b>.")
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useSignatureAppearanceTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("certSign.appearance.tooltip.header.title", "About Signature Appearance")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("certSign.appearance.tooltip.invisible.title", "Invisible Signatures"),
|
||||
description: t("certSign.appearance.tooltip.invisible.text", "The signature is added to the PDF for security but won't be visible when viewing the document. Perfect for legal requirements without changing the document's appearance."),
|
||||
bullets: [
|
||||
t("certSign.appearance.tooltip.invisible.bullet1", "Provides security without visual changes"),
|
||||
t("certSign.appearance.tooltip.invisible.bullet2", "Meets legal requirements for digital signing"),
|
||||
t("certSign.appearance.tooltip.invisible.bullet3", "Doesn't affect document layout or design")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("certSign.appearance.tooltip.visible.title", "Visible Signatures"),
|
||||
description: t("certSign.appearance.tooltip.visible.text", "Shows a signature block on the PDF with your name, date, and optional details. Useful when you want readers to clearly see the document is signed."),
|
||||
bullets: [
|
||||
t("certSign.appearance.tooltip.visible.bullet1", "Shows signer name and date on the document"),
|
||||
t("certSign.appearance.tooltip.visible.bullet2", "Can include reason and location for signing"),
|
||||
t("certSign.appearance.tooltip.visible.bullet3", "Choose which page to place the signature"),
|
||||
t("certSign.appearance.tooltip.visible.bullet4", "Optional logo can be included")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user