WelcomeWelcome | FAQFAQ | DownloadsDownloads | WikiWiki

Author Topic: driverless, CUPS-less way to print PDF file  (Read 2858 times)

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
driverless, CUPS-less way to print PDF file
« on: September 12, 2023, 07:56:34 AM »
We have a Brother printer on our wireless LAN. All that I, personally, need from this printer is to print an occasional PDF file from my laptop. I run TCL 14 x86_64 on the laptop.

The full GNU/Linux printing stack (cups, avahi, cups-browsed, ghostscript, dbus, etc, etc, etc) looks to me like a giant, ugly hairball and I don't want to spoil the beautiful TCL minimalism just to print an occasional PDF. Therefore, I went in search of a tiny solution.

Since the printer supports IPP, in the end all I needed was to know the printer's URI and to have two small utilities (mutool and ipptool) somewhere in my PATH.

Here is the script I wrote for what I need, in case it is helpful to someone:

Code: [Select]
#!/bin/sh

# print-pdf v1.0
#
# Purpose: Print pdf file(s) to network printer as simply as possible
#
# Dependencies: mutool (included in mupdf.tcz) and ipptool (included in cups.tcz)
#
# Notes:
#  * printer driver is not required
#  * running cups is not required
#  * all that's required is this:
#    * you know the printer's url
#    * you can ping the printer
#    * printer supports ipp
#    * you have mutool, ipptool, and this script in your PATH
#
# Usage: Arguments to script are absolute paths to pdf files to be printed
#
# Usage example:
#   $ print-pdf $HOME/Documents/into_the_core.pdf

printer_uri="ipp://192.168.20.7:631/ipp/print"

# 1. create a disposable, generic .ipp file:
cat << EOF > /tmp/instructions-$$.ipp
{
    VERSION 2.0
    OPERATION Print-Job
    REQUEST-ID 42

    GROUP operation-attributes-tag
    ATTR charset "attributes-charset" "utf-8"
    ATTR naturalLanguage "attributes-natural-language" "en"
    ATTR uri "printer-uri" "$printer_uri"
    ATTR name "requesting-user-name" "tc"
    ATTR mimeMediaType "document-format" "image/pwg-raster"

    FILE "/tmp/print-me-$$.pwg"
}
EOF

# 2. create a .pwg file from each pdf given as an argument, then send it to the printer:
for fullpath in "$@"; do
mutool convert -o /tmp/print-me-$$.pwg "$fullpath"
ipptool -tv -f /tmp/print-me-$$.pwg "$printer_uri" /tmp/instructions-$$.ipp
rm /tmp/print-me-$$.pwg
done

# 3. remove the .ipp file:
rm /tmp/instructions-$$.ipp

A few more notes:

1. In my case, mutool is needed because my printer does not support pdf files directly. If yours does, then you can send pdf files to printer directly and can skip the mutool step and intermediary pwg file. You can find what your printer supports by using ipptool and sending it a "Get-Printer-Attributes" query. Do an internet search for how to do this, it's not hard.

2. mupdf.tcz in x86_64 repo does not currently include mutool, but it will soon ;)

3. to get ipptool, you can either load cups (no need to start cups itself) or do what I did and extract just ipptool from cups.tcz and put it in your PATH. ipptool's dependencies are pretty minimal.
« Last Edit: September 12, 2023, 08:04:15 AM by GNUser »

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #1 on: September 12, 2023, 09:08:38 AM »
Oh, I forgot to mention: Printer should always be at the same local IP address. If not, it is a more complex situation that is not compatible with a minimalistic solution.

A static local IP address can be accomplished on DHCP client side (i.e., in printer's network settings) or on DHCP server side (i.e., by configuring DHCP server in your router to always give the desired IP address to the client who has printer's MAC address).

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #2 on: September 12, 2023, 06:29:42 PM »
For completion's sake, here is an example of getting a printer's attributes (including supported document formats):

Code: [Select]
$ cat get-attributes.ipp
{
    VERSION 2.0
    OPERATION Get-Printer-Attributes
    REQUEST-ID 42

    GROUP operation-attributes-tag
    ATTR charset "attributes-charset" "utf-8"
    ATTR naturalLanguage "attributes-natural-language" "en"
    ATTR uri "printer-uri" "ipp://192.168.20.7:631/ipp/print"
    ATTR name "requesting-user-name" "bruno"
}

$ ipptool -tv ipp://192.168.20.7:631/ipp/print get-attributes.ipp
---snip---
        document-format-supported (1setOf mimeMediaType) = application/octet-stream,image/urf,image/jpeg,image/pwg-raster
        document-format-default (mimeMediaType) = application/octet-stream
        printer-is-accepting-jobs (boolean) = true
---snip---

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #3 on: September 13, 2023, 12:00:47 PM »
I generalized the script so that it prints non-pdf files as well. Also, if a file is in color, user can force black and white (monochrome) print.

It seems I'm now spamming my own thread ::) Hopefully the thread will be useful to someone, somewhere, someday.

Code: [Select]
#!/bin/sh

# print v2.0
# Bruno "GNUser" Dantas GPLv3

# Purpose: To print files without cups, avahi, or other complications
# Dependencies: mutool (part of mupdf.tcz) and ipptool (part of cups.tcz); libreoffice (only if support for non-pdf files is desired)
# Syntax: $ print [monochrome] <absolute path to file> [<additional files>]
# Usage example: $ print /home/bruno/Downloads/into_the_core.pdf

# user variable:
URI="ipp://192.168.20.7:631/ipp/print"

# internal variables:
PDF_DIR=/tmp/pdf-$$
PWG_DIR=/tmp/pwg-$$
IPP_DIR=/tmp/ipp-$$

parse_abspath()
{
# abspath: /foo/bar/baz.qux.quux
# dirname: /foo/bar
# filename: baz.qux.quux
# extension: quux
# stem: baz.qux

abspath="$1"
dirname="$(dirname "$abspath")"
filename="$(basename "$abspath")"
extension=${abspath##*.}
stem=${filename%.*}
}

create_ipp_file()
{
cat << EOF > "$IPP_DIR/$stem.ipp"
{
    VERSION 2.0
    OPERATION Print-Job
    REQUEST-ID 42

    GROUP operation-attributes-tag
    ATTR charset "attributes-charset" "utf-8"
    ATTR naturalLanguage "attributes-natural-language" "en"
    ATTR uri "printer-uri" "$URI"
    ATTR name "requesting-user-name" "bruno"
    ATTR mimeMediaType "document-format" "image/pwg-raster"

    FILE "$abspath"
}
EOF
}

# MAIN
# monochrome requested?
if [ "$1" = "monochrome" ]; then
monochrome=true
shift
else
monochrome=false
fi

# populate PDF_DIR
mkdir $PDF_DIR
for arg in "$@"; do
parse_abspath "$arg"
if echo "$extension" | grep -qi "pdf"; then
cp "$arg" $PDF_DIR
else
libreoffice --convert-to pdf "$arg" --outdir $PDF_DIR
fi
done

# populate PWG_DIR
mkdir $PWG_DIR
for pdf_file in $PDF_DIR/*; do
parse_abspath "$pdf_file"
if $monochrome; then
mutool convert -O colorspace=gray -o "$PWG_DIR/$stem.pwg" "$pdf_file"
else
mutool convert -o "$PWG_DIR/$stem.pwg" "$pdf_file"
fi
done

# send each pwg to printer
mkdir $IPP_DIR
for pwg_file in $PWG_DIR/*; do
parse_abspath "$pwg_file"
create_ipp_file
ipptool -tv -f "$pwg_file" "$URI" "$IPP_DIR/$stem.ipp"
done

# cleanup
rm -rf $PDF_DIR
rm -rf $PWG_DIR
rm -rf $IPP_DIR

Offline gadget42

  • Hero Member
  • *****
  • Posts: 791
Re: driverless, CUPS-less way to print PDF file
« Reply #4 on: September 14, 2023, 09:19:50 AM »
you're not spamming when you are adding additional valuable information.

after reading your posts i have been encouraged by them to conduct some experiments of my own.

thanks!
The fluctuation theorem has long been known for a sudden switch of the Hamiltonian of a classical system Z54 . For a quantum system with a Hamiltonian changing from... https://forum.tinycorelinux.net/index.php/topic,25972.msg166580.html#msg166580

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #5 on: September 14, 2023, 09:50:19 AM »
Hi gadget42. Thank you for clearing me of any wrongdoing :)

I did discover two more valuable pieces of information since my last post:

1. libreoffice can convert almost anything into a pdf, even image files (e.g., jpg). The result is that the print script (version 2.0 in reply #3) has been able to successfully print every filetype I've thrown at it so far.

2. The libreoffice developers provide an AppImage and it works perfectly on TCL. It's a single file with no dependencies (other than the usual tweaks for AppImage support on TCL), which seems magical when you consider that libreoffice.tcz contains 3,500 files and its dependency tree looks like this.

If you discover any new pearls in your experiments, please do share!
« Last Edit: September 14, 2023, 09:59:14 AM by GNUser »

Offline patrikg

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 713
Re: driverless, CUPS-less way to print PDF file
« Reply #6 on: September 14, 2023, 10:09:05 AM »
I can also add some value to this, you talked about libreoffice.
You can also batch convert files with libreoffice to pdf or another file format.
Use the command line option --convert-to

I have done it many times to convert hole directories with lots of doc's files.
And what great with this is that the program get's headless, you don't need any windows system.

Cut from the help.
Code: [Select]
   --convert-to OutputFileExtension[:OutputFilterName] \                     
     [--outdir output_dir] [--convert-images-to]                               
                       Batch convert files (implies --headless). If --outdir   
                       isn't specified, then current working directory is used
                       as output_dir. If --convert-images-to is given, its     
                       parameter is taken as the target filter format for *all*
                       images written to the output format. If --convert-to is
                       used more than once, the last value of                 
                       OutputFileExtension[:OutputFilterName] is effective. If
                       --outdir is used more than once, only its last value is
                       effective. For example:                                 
                   --convert-to pdf *.odt                                     
                   --convert-to epub *.doc                                     
                   --convert-to pdf:writer_pdf_Export --outdir /home/user *.doc
                   --convert-to "html:XHTML Writer File:UTF8" \             
                                --convert-images-to "jpg" *.doc             
                   --convert-to "txt:Text (encoded):UTF8" *.doc             

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #7 on: September 14, 2023, 10:46:08 AM »
Hi patrikg. libreoffice is used in my script only once, with that flag:
Code: [Select]
libreoffice --convert-to pdf "$arg" --outdir $PDF_DIR

Offline patrikg

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 713
Re: driverless, CUPS-less way to print PDF file
« Reply #8 on: September 15, 2023, 02:49:10 AM »
Sorry @GNUser, I tldr your script. :)
By the way, I think also I could suggest another program that uses chrome/chromium to build presentations and you can also convert them to pdf.

https://marp.app/
https://github.com/marp-team/marp-cli/releases

Used that with vagrant and scripted a test script to do some performance test between uses intel nic vs virtio nic in a VM.

Here you go :)

Code: [Select]
vm_box_name = ["Server", "Client"]
vm_name = "Ubuntu 22.04.1 LTS"
vm_host_name = "ubuntu2204"
nicname = ["Intel", "VirtIO"]
iperf3dir="/home/vagrant/iperf3/"
ip="192.168.56."

Vagrant.configure("2") do | config |
(0..1).each do | nictype |
(0..1).each do | sc |
config.vm.define vm_box_name[sc] + "-" + nicname[nictype] do | node |
node.vm.box = "ubuntu/jammy64"
node.vm.box_check_update = false
node.vm.hostname = "#{vm_box_name[sc]}#{vm_host_name}".downcase
node.vm.synced_folder ".", "/vagrant", disabled: true

if sc == 1
node.vm.synced_folder ".", iperf3dir, owner: "vagrant", group: "vagrant"
end

if nictype == 0
node.vm.network "private_network", ip: ip + ((nictype+1) * 10 + sc).to_s
else
node.vm.network "private_network", ip: ip + ((nictype+1) * 10 + sc).to_s, nic_type: "virtio"
end

node.vm.provider "virtualbox" do | vb |
vb.name = "#{vm_name} #{vm_box_name[sc]} #{nicname[nictype]}"
vb.check_guest_additions = false
vb.memory = 2048
end

node.vm.provision "shell", name: "Startup", keep_color: true, inline: <<-SCRIPT
#/usr/local/bin/env bash
iperf3dir="#{iperf3dir}"

title=("Private Network Intel Nic" "Private Network VirtIO Nic")

if [ "#{sc}" == "0" ]; then
if [ ! -f updateinstallflag#{sc}#{nictype} ]; then
echo "Updating local repo directory"
apt -y update

echo "Installing iperf3"
apt -y install iperf3

touch updateinstallflag#{sc}#{nictype}

echo "Starting iperf3 in background"
(iperf3 -s -1 && systemctl poweroff) &
exit 0
else
echo "Starting iperf3 in background"
(iperf3 -s -1 && systemctl poweroff) &
exit 0
fi
else
if [ ! -f updateinstallflag#{sc}#{nictype} ]; then
echo "Updating local repo directory"
apt -y update

if [[ "#{sc}" == "1" && "#{nictype}" == "1" ]]; then
touch updateinstallflag#{sc}#{nictype}

echo "Installing iperf3 chromium-browser jq"
apt -y install iperf3 chromium-browser jq

echo "Installing marp-cli"
curl -s -O -L https://github.com/marp-team/marp-cli/releases/download/v2.3.0/marp-cli-v2.3.0-linux.tar.gz
tar -xf marp-cli-v2.3.0-linux.tar.gz
else
touch updateinstallflag#{sc}#{nictype}
echo "Installing iperf3 jq"
apt -y install iperf3 jq
fi
fi

ping -c 4 #{ip + ((nictype + 1)*10).to_s}

echo "Doing iperf3 to ${title[#{nictype}]}, wait a little bit"
iperf3 -c #{ip + ((nictype + 1)*10).to_s} --json --logfile ${iperf3dir}output#{nictype}.json

if [[ "#{sc}" == "1" && "#{nictype}" == "0" ]]; then
echo "---" > ${iperf3dir}output.md
echo "title: Emulated nic vs virtio" >> ${iperf3dir}output.md
echo "description: VirtualBox Hypervisor" >> ${iperf3dir}output.md
echo "paginate: true" >> ${iperf3dir}output.md
echo "marp: true" >> ${iperf3dir}output.md
echo "theme: uncover" >> ${iperf3dir}output.md
echo "---" >> ${iperf3dir}output.md
echo "![bg](https://picsum.photos/720)" >> ${iperf3dir}output.md
echo "<!-- _class: invert -->" >> ${iperf3dir}output.md
echo "# Emulated nic vs virtio" >> ${iperf3dir}output.md
echo "## <span style='color:orange;'>$(date +"%Y-%m-%d")</span><!-- fit -->" >> ${iperf3dir}output.md
echo "## <span style='color:black;'>Let's see who's the winner is</span><!-- fit -->" >> ${iperf3dir}output.md
fi

echo "" >> ${iperf3dir}output.md
echo "---" >> ${iperf3dir}output.md
echo "<!-- _class: invert -->" >> ${iperf3dir}output.md
echo "${title[#{nictype}]}" >> ${iperf3dir}output.md
echo "* IP" >> ${iperf3dir}output.md
echo "    * Local: $(jq -r '.start.connected[].local_host' ${iperf3dir}output#{nictype}.json)" >> ${iperf3dir}output.md
echo "    * Remote: $(jq -r ".start.connected[].remote_host" ${iperf3dir}output#{nictype}.json)" >> ${iperf3dir}output.md
echo "* Bits/s" >> ${iperf3dir}output.md
echo "    * Sum sent: $(jq -r '.end.sum_sent.bits_per_second' ${iperf3dir}output#{nictype}.json | LC_ALL=C numfmt --to=iec-i)" >> ${iperf3dir}output.md
echo "    * Sum received: $(jq -r '.end.sum_received.bits_per_second' ${iperf3dir}output#{nictype}.json | LC_ALL=C numfmt --to=iec-i)" >> ${iperf3dir}output.md
echo "* Bytes" >> ${iperf3dir}output.md
echo "    * Sum sent: $(jq -r '.end.sum_sent.bytes' ${iperf3dir}output#{nictype}.json | LC_ALL=C numfmt --to=iec-i)" >> ${iperf3dir}output.md
echo "    * Sum received: $(jq -r '.end.sum_received.bytes' ${iperf3dir}output#{nictype}.json | LC_ALL=C numfmt --to=iec-i)" >> ${iperf3dir}output.md

if [[ "#{sc}" == "1" && "#{nictype}" == "1" ]]; then
echo "---" >> ${iperf3dir}output.md
echo "<!-- _class: invert -->" >> ${iperf3dir}output.md
echo "# Thanks!" >> ${iperf3dir}output.md
echo "* Teacher" >> ${iperf3dir}output.md
echo "    * For making this comes true" >> ${iperf3dir}output.md
echo "    * For letting us know to use this great tools" >> ${iperf3dir}output.md
echo "* HashiCorp" >> ${iperf3dir}output.md
echo "    * For using ruby as language to use when config Vagrant" >> ${iperf3dir}output.md
su - vagrant -c "./marp --pdf --html -I ${iperf3dir} -o ${iperf3dir}"
fi
systemctl poweroff
fi
SCRIPT
end
end
end
end

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1511
Re: driverless, CUPS-less way to print PDF file
« Reply #9 on: September 29, 2023, 12:41:35 PM »
I added polish and a minimal gui, then submitted this for the tc14 x86_64 repo. I considered calling it "ipp-print" but went with "easyprint" in the end. Bug reports are welcome.

Having to load cups + dbus + avahi (mDNS) to print to my LAN printer at a static ip address always struck me as cracking a nut with a sledgehammer. I'm glad that ipp makes it possible to crack the nut with a nutcracker :)

Note:
    I discovered the hard way that converting office documents (which are complex binary files and, depending on type, follow proprietary standards) into any other format is not a simple task. Therefore, for sake of minimalism, easyprint only prints pdf files out of the box.
   However, if you have the libreoffice extension or libreoffice appimage, then easyprint will automatically handle anything you throw at it--document files, image files, you name it. If you have the appimage, it should be somewhere in your PATH and you should change its name from LibreOffice_whatever.AppImage to simply libreoffice (or at least have a link in your PATH called libreoffice that points to the appimage).
« Last Edit: September 29, 2023, 12:56:44 PM by GNUser »