Merge pull request #182360 from Yarny0/cups-pdf
cups-pdf(-to-pdf): init
This commit is contained in:
commit
861c7b189c
8 changed files with 302 additions and 0 deletions
|
@ -37,6 +37,15 @@
|
|||
<link linkend="opt-programs.bash.blesh.enable">programs.bash.blesh</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link xlink:href="https://github.com/alexivkin/CUPS-PDF-to-PDF">cups-pdf-to-pdf</link>,
|
||||
a pdf-generating cups backend based on
|
||||
<link xlink:href="https://www.cups-pdf.de/">cups-pdf</link>.
|
||||
Available as
|
||||
<link linkend="opt-services.printing.cups-pdf.enable">services.printing.cups-pdf</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link xlink:href="https://github.com/junegunn/fzf">fzf</link>,
|
||||
|
|
|
@ -18,6 +18,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
|
||||
- [blesh](https://github.com/akinomyoga/ble.sh), a line editor written in pure bash. Available as [programs.bash.blesh](#opt-programs.bash.blesh.enable).
|
||||
|
||||
- [cups-pdf-to-pdf](https://github.com/alexivkin/CUPS-PDF-to-PDF), a pdf-generating cups backend based on [cups-pdf](https://www.cups-pdf.de/). Available as [services.printing.cups-pdf](#opt-services.printing.cups-pdf.enable).
|
||||
|
||||
- [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).
|
||||
|
||||
- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).
|
||||
|
|
|
@ -1028,6 +1028,7 @@
|
|||
./services/networking/znc/default.nix
|
||||
./services/printing/cupsd.nix
|
||||
./services/printing/ipp-usb.nix
|
||||
./services/printing/cups-pdf.nix
|
||||
./services/scheduling/atd.nix
|
||||
./services/scheduling/cron.nix
|
||||
./services/scheduling/fcron.nix
|
||||
|
|
185
nixos/modules/services/printing/cups-pdf.nix
Normal file
185
nixos/modules/services/printing/cups-pdf.nix
Normal file
|
@ -0,0 +1,185 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
|
||||
# cups calls its backends as user `lp` (which is good!),
|
||||
# but cups-pdf wants to be called as `root`, so it can change ownership of files.
|
||||
# We add a suid wrapper and a wrapper script to trick cups into calling the suid wrapper.
|
||||
# Note that a symlink to the suid wrapper alone wouldn't suffice, cups would complain
|
||||
# > File "/nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-cups-progs/lib/cups/backend/cups-pdf" has insecure permissions (0104554/uid=0/gid=20)
|
||||
|
||||
# wrapper script that redirects calls to the suid wrapper
|
||||
cups-pdf-wrapper = pkgs.writeTextFile {
|
||||
name = "${pkgs.cups-pdf-to-pdf.name}-wrapper.sh";
|
||||
executable = true;
|
||||
destination = "/lib/cups/backend/cups-pdf";
|
||||
checkPhase = ''
|
||||
${pkgs.stdenv.shellDryRun} "$target"
|
||||
${lib.getExe pkgs.shellcheck} "$target"
|
||||
'';
|
||||
text = ''
|
||||
#! ${pkgs.runtimeShell}
|
||||
exec "${config.security.wrapperDir}/cups-pdf" "$@"
|
||||
'';
|
||||
};
|
||||
|
||||
# wrapped cups-pdf package that uses the suid wrapper
|
||||
cups-pdf-wrapped = pkgs.buildEnv {
|
||||
name = "${pkgs.cups-pdf-to-pdf.name}-wrapped";
|
||||
# using the wrapper as first path ensures it is used
|
||||
paths = [ cups-pdf-wrapper pkgs.cups-pdf-to-pdf ];
|
||||
ignoreCollisions = true;
|
||||
};
|
||||
|
||||
instanceSettings = name: {
|
||||
freeformType = with lib.types; nullOr (oneOf [ int str path package ]);
|
||||
# override defaults:
|
||||
# inject instance name into paths,
|
||||
# also avoid conflicts between user names and special dirs
|
||||
options.Out = lib.mkOption {
|
||||
type = with lib.types; nullOr singleLineStr;
|
||||
default = "/var/spool/cups-pdf-${name}/users/\${USER}";
|
||||
defaultText = "/var/spool/cups-pdf-{instance-name}/users/\${USER}";
|
||||
example = "\${HOME}/cups-pdf";
|
||||
description = lib.mdDoc ''
|
||||
output directory;
|
||||
`''${HOME}` will be expanded to the user's home directory,
|
||||
`''${USER}` will be expanded to the user name.
|
||||
'';
|
||||
};
|
||||
options.AnonDirName = lib.mkOption {
|
||||
type = with lib.types; nullOr singleLineStr;
|
||||
default = "/var/spool/cups-pdf-${name}/anonymous";
|
||||
defaultText = "/var/spool/cups-pdf-{instance-name}/anonymous";
|
||||
example = "/var/lib/cups-pdf";
|
||||
description = lib.mdDoc "path for anonymously created PDF files";
|
||||
};
|
||||
options.Spool = lib.mkOption {
|
||||
type = with lib.types; nullOr singleLineStr;
|
||||
default = "/var/spool/cups-pdf-${name}/spool";
|
||||
defaultText = "/var/spool/cups-pdf-{instance-name}/spool";
|
||||
example = "/var/lib/cups-pdf";
|
||||
description = lib.mdDoc "spool directory";
|
||||
};
|
||||
options.Anonuser = lib.mkOption {
|
||||
type = lib.types.singleLineStr;
|
||||
default = "root";
|
||||
description = lib.mdDoc ''
|
||||
User for anonymous PDF creation.
|
||||
An empty string disables this feature.
|
||||
'';
|
||||
};
|
||||
options.GhostScript = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = lib.getExe pkgs.ghostscript;
|
||||
defaultText = lib.literalExpression "lib.getExe pkgs.ghostscript";
|
||||
example = lib.literalExpression ''''${pkgs.ghostscript}/bin/ps2pdf'';
|
||||
description = lib.mdDoc "location of GhostScript binary";
|
||||
};
|
||||
};
|
||||
|
||||
instanceConfig = { name, config, ... }: {
|
||||
options = {
|
||||
enable = (lib.mkEnableOption (lib.mdDoc "this cups-pdf instance")) // { default = true; };
|
||||
installPrinter = (lib.mkEnableOption (lib.mdDoc ''
|
||||
a CUPS printer queue for this instance.
|
||||
The queue will be named after the instance and will use the {file}`CUPS-PDF_opt.ppd` ppd file.
|
||||
If this is disabled, you need to add the queue yourself to use the instance
|
||||
'')) // { default = true; };
|
||||
confFileText = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
description = lib.mdDoc ''
|
||||
This will contain the contents of {file}`cups-pdf.conf` for this instance, derived from {option}`settings`.
|
||||
You can use this option to append text to the file.
|
||||
'';
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule (instanceSettings name);
|
||||
default = {};
|
||||
example = {
|
||||
Out = "\${HOME}/cups-pdf";
|
||||
UserUMask = "0033";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Settings for a cups-pdf instance, see the descriptions in the template config file in the cups-pdf package.
|
||||
The key value pairs declared here will be translated into proper key value pairs for {file}`cups-pdf.conf`.
|
||||
Setting a value to `null` disables the option and removes it from the file.
|
||||
'';
|
||||
};
|
||||
};
|
||||
config.confFileText = lib.pipe config.settings [
|
||||
(lib.filterAttrs (key: value: value != null))
|
||||
(lib.mapAttrs (key: builtins.toString))
|
||||
(lib.mapAttrsToList (key: value: "${key} ${value}\n"))
|
||||
lib.concatStrings
|
||||
];
|
||||
};
|
||||
|
||||
cupsPdfCfg = config.services.printing.cups-pdf;
|
||||
|
||||
copyConfigFileCmds = lib.pipe cupsPdfCfg.instances [
|
||||
(lib.filterAttrs (name: lib.getAttr "enable"))
|
||||
(lib.mapAttrs (name: lib.getAttr "confFileText"))
|
||||
(lib.mapAttrs (name: pkgs.writeText "cups-pdf-${name}.conf"))
|
||||
(lib.mapAttrsToList (name: confFile: "ln --symbolic --no-target-directory ${confFile} /var/lib/cups/cups-pdf-${name}.conf\n"))
|
||||
lib.concatStrings
|
||||
];
|
||||
|
||||
printerSettings = lib.pipe cupsPdfCfg.instances [
|
||||
(lib.filterAttrs (name: lib.getAttr "enable"))
|
||||
(lib.filterAttrs (name: lib.getAttr "installPrinter"))
|
||||
(lib.mapAttrsToList (name: instance: (lib.mapAttrs (key: lib.mkDefault) {
|
||||
inherit name;
|
||||
model = "CUPS-PDF_opt.ppd";
|
||||
deviceUri = "cups-pdf:/${name}";
|
||||
description = "virtual printer for cups-pdf instance ${name}";
|
||||
location = instance.settings.Out;
|
||||
})))
|
||||
];
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.services.printing.cups-pdf = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc ''
|
||||
the cups-pdf virtual pdf printer backend.
|
||||
By default, this will install a single printer `pdf`.
|
||||
but this can be changed/extended with {option}`services.printing.cups-pdf.instances`
|
||||
'');
|
||||
instances = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule instanceConfig);
|
||||
default.pdf = {};
|
||||
example.pdf.settings = {
|
||||
Out = "\${HOME}/cups-pdf";
|
||||
UserUMask = "0033";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Permits to raise one or more cups-pdf instances.
|
||||
Each instance is named by an attribute name, and the attribute's values control the instance' configuration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cupsPdfCfg.enable {
|
||||
services.printing.enable = true;
|
||||
services.printing.drivers = [ cups-pdf-wrapped ];
|
||||
hardware.printers.ensurePrinters = printerSettings;
|
||||
# the cups module will install the default config file,
|
||||
# but we don't need it and it would confuse cups-pdf
|
||||
systemd.services.cups.preStart = lib.mkAfter ''
|
||||
rm -f /var/lib/cups/cups-pdf.conf
|
||||
${copyConfigFileCmds}
|
||||
'';
|
||||
security.wrappers.cups-pdf = {
|
||||
group = "lp";
|
||||
owner = "root";
|
||||
permissions = "+r,ug+x";
|
||||
setuid = true;
|
||||
source = "${pkgs.cups-pdf-to-pdf}/lib/cups/backend/cups-pdf";
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = [ lib.maintainers.yarny ];
|
||||
|
||||
}
|
|
@ -155,6 +155,7 @@ in {
|
|||
coturn = handleTest ./coturn.nix {};
|
||||
couchdb = handleTest ./couchdb.nix {};
|
||||
cri-o = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cri-o.nix {};
|
||||
cups-pdf = handleTest ./cups-pdf.nix {};
|
||||
custom-ca = handleTest ./custom-ca.nix {};
|
||||
croc = handleTest ./croc.nix {};
|
||||
deluge = handleTest ./deluge.nix {};
|
||||
|
|
40
nixos/tests/cups-pdf.nix
Normal file
40
nixos/tests/cups-pdf.nix
Normal file
|
@ -0,0 +1,40 @@
|
|||
import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
name = "cups-pdf";
|
||||
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
imports = [ ./common/user-account.nix ];
|
||||
environment.systemPackages = [ pkgs.poppler_utils ];
|
||||
fonts.fonts = [ pkgs.dejavu_fonts ]; # yields more OCR-able pdf
|
||||
services.printing.cups-pdf.enable = true;
|
||||
services.printing.cups-pdf.instances = {
|
||||
opt = {};
|
||||
noopt.installPrinter = false;
|
||||
};
|
||||
hardware.printers.ensurePrinters = [{
|
||||
name = "noopt";
|
||||
model = "CUPS-PDF_noopt.ppd";
|
||||
deviceUri = "cups-pdf:/noopt";
|
||||
}];
|
||||
};
|
||||
|
||||
# we cannot check the files with pdftotext, due to
|
||||
# https://github.com/alexivkin/CUPS-PDF-to-PDF/issues/7
|
||||
# we need `imagemagickBig` as it has ghostscript support
|
||||
|
||||
testScript = ''
|
||||
from subprocess import run
|
||||
machine.wait_for_unit("cups.service")
|
||||
for name in ("opt", "noopt"):
|
||||
text = f"test text {name}".upper()
|
||||
machine.wait_until_succeeds(f"lpstat -v {name}")
|
||||
machine.succeed(f"su - alice -c 'echo -e \"\n {text}\" | lp -d {name}'")
|
||||
# wait until the pdf files are completely produced and readable by alice
|
||||
machine.wait_until_succeeds(f"su - alice -c 'pdfinfo /var/spool/cups-pdf-{name}/users/alice/*.pdf'")
|
||||
machine.succeed(f"cp /var/spool/cups-pdf-{name}/users/alice/*.pdf /tmp/{name}.pdf")
|
||||
machine.copy_from_vm(f"/tmp/{name}.pdf", "")
|
||||
run(f"${pkgs.imagemagickBig}/bin/convert -density 300 $out/{name}.pdf $out/{name}.jpeg", shell=True, check=True)
|
||||
assert text.encode() in run(f"${lib.getExe pkgs.tesseract} $out/{name}.jpeg stdout", shell=True, check=True, capture_output=True).stdout
|
||||
'';
|
||||
|
||||
meta.maintainers = [ lib.maintainers.yarny ];
|
||||
})
|
62
pkgs/misc/cups/drivers/cups-pdf-to-pdf/default.nix
Normal file
62
pkgs/misc/cups/drivers/cups-pdf-to-pdf/default.nix
Normal file
|
@ -0,0 +1,62 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, fetchFromGitHub
|
||||
, cups
|
||||
, coreutils
|
||||
, nixosTests
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "cups-pdf-to-pdf";
|
||||
version = "unstable-2021-12-22";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "alexivkin";
|
||||
repo = "CUPS-PDF-to-PDF";
|
||||
rev = "c14428c2ca8e95371daad7db6d11c84046b1a2d4";
|
||||
hash = "sha256-pa4PFf8OAFSra0hSazmKUfbMYL/cVWvYA1lBf7c7jmY=";
|
||||
};
|
||||
|
||||
buildInputs = [ cups ];
|
||||
|
||||
postPatch = ''
|
||||
sed -r 's|(gscall, size, ")cp |\1${coreutils}/bin/cp |' cups-pdf.c -i
|
||||
'';
|
||||
|
||||
# gcc command line is taken from original cups-pdf's README file
|
||||
# https://fossies.org/linux/cups-pdf/README
|
||||
# however, we replace gcc with $CC following
|
||||
# https://nixos.org/manual/nixpkgs/stable/#sec-darwin
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
$CC -O9 -s cups-pdf.c -o cups-pdf -lcups
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
install -Dt $out/lib/cups/backend cups-pdf
|
||||
install -Dm 0644 -t $out/etc/cups cups-pdf.conf
|
||||
install -Dm 0644 -t $out/share/cups/model *.ppd
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru.tests.vmtest = nixosTests.cups-pdf;
|
||||
|
||||
meta = with lib; {
|
||||
description = "A CUPS backend that turns print jobs into searchable PDF files";
|
||||
homepage = "https://github.com/alexivkin/CUPS-PDF-to-PDF";
|
||||
license = licenses.gpl2Only;
|
||||
maintainers = [ maintainers.yarny ];
|
||||
longDescription = ''
|
||||
cups-pdf is a CUPS backend that generates a PDF file for each print job and puts this file
|
||||
into a folder on the local machine such that the print job's owner can access the file.
|
||||
|
||||
https://www.cups-pdf.de/
|
||||
|
||||
cups-pdf-to-pdf is a fork of cups-pdf which tries hard to preserve the original text of the print job by avoiding rasterization.
|
||||
|
||||
Note that in order to use this package, you have to make sure that the cups-pdf program is called with root privileges.
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -36718,6 +36718,8 @@ with pkgs;
|
|||
|
||||
cups-dymo = callPackage ../misc/cups/drivers/dymo {};
|
||||
|
||||
cups-pdf-to-pdf = callPackage ../misc/cups/drivers/cups-pdf-to-pdf {};
|
||||
|
||||
cups-toshiba-estudio = callPackage ../misc/cups/drivers/estudio {};
|
||||
|
||||
cups-zj-58 = callPackage ../misc/cups/drivers/zj-58 { };
|
||||
|
|
Loading…
Reference in a new issue