image


Intro


Like I said earlier in previos articles, Buildroot is a great system for embedded Linux development. But sometimes strange things can happen.


Once upon a workday, I got the following task: add printing system in firmware (Kraftway terminal Linux next generation). Ok, so I had to add cups + cups filter and to build firmware. I set a postscript-printer and got an error "Filter failed". Trivial tasks turned into serious work.


In this article, I wrote my own way of solving this problem. It may be useful for other developers and IT-specialist and, also, for a deeper understanding of the Buildroot.


If you are a Buildroot beginner, I recommend reading my previous articles.


Understanding of the printing system in Linux


First of all, CUPS is not an ALL-IN-ONE printing solution. Below, I wrote a simple printing schema:


  • application sends user file (pdf,txt,jpeg, other) to CUPS
  • CUPS transforms user file to internal PDF (via QPDF)
  • CUPS selects needed filters and sends PDF to them
  • filters transform PDF to printer format:
    • ghostscript uses freetype for font rendering
    • gilters use image libs(libjpeg,libpng) for image rendering
  • filters send printer-ready data to backend
  • backend(lpd,smb,usb,etc) sends data to printer

Ghostscript may be replaced with another render (like Poppler), but I use this schema. Filters depend on the printer manufacturer.The full printing system schema is more complex. But this one is simple enough to understand the article.


Step one. Web interface


"Ok", — said I, rolling up my sleeves.


Buildroot has a 'target finalize' stage that cleans unnecessary files such as mans, docs, and others. Our target is bigger and more complex, so I'll show only the first 10 lines:


alexey@comp:~/test/article/buildroot$ grep -n '.PHONY: target-finalize' -A 10 Makefile
743:.PHONY: target-finalize
744-target-finalize: $(PACKAGES) $(TARGET_DIR) host-finalize
745-    @$(call MESSAGE,"Finalizing target directory")
746-    $(call per-package-rsync,$(sort $(PACKAGES)),target,$(TARGET_DIR))
747-    $(foreach hook,$(TARGET_FINALIZE_HOOKS),$($(hook))$(sep))
748-    rm -rf $(TARGET_DIR)/usr/include $(TARGET_DIR)/usr/share/aclocal 749-        $(TARGET_DIR)/usr/lib/pkgconfig $(TARGET_DIR)/usr/share/pkgconfig 750-        $(TARGET_DIR)/usr/lib/cmake $(TARGET_DIR)/usr/share/cmake
751-    find $(TARGET_DIR)/usr/{lib,share}/ -name '*.cmake' -print0 | xargs -0 rm -f
752-    find $(TARGET_DIR)/lib/ $(TARGET_DIR)/usr/lib/ $(TARGET_DIR)/usr/libexec/ 753-        \( -name '*.a' -o -name '*.la' \) -print0 | xargs -0 rm -f

CUPS has a special directory, which contains a web interface file. By default, the location of CUPS documents directory resides in /usr/share/doc, but this path is cleaned by Buildroot on the target-finalize stage, on image-build. I will use cups.css as a detector of the CUPS doc-dir path. This file is part of the CUPS web interface.


Let's build the full system:


alexey@comp:~/test/article/buildroot$ find output/ -iname cups.css
output/host/x86_64-buildroot-linux-gnu/sysroot/usr/share/doc/cups/cups.css
output/build/cups-2.3.1/doc/cups.css

As you can see, no style.css is present in ${TARGET_DIR} because buildroot remove them:


And some lines later:


765: rm -rf $(TARGET_DIR)/usr/doc $(TARGET_DIR)/usr/share/doc

Let's rebuild cups without image create (make cups-rebuild command):


alexey@comp:~/test/article/buildroot$ find output/ -iname cups.css
output/target/usr/share/doc/cups/cups.css
output/host/x86_64-buildroot-linux-gnu/sysroot/usr/share/doc/cups/cups.css
output/build/cups-2.3.1/doc/cups.css

I wrote a short patch that moves this directory to /usr/share. This patch was applied to the Buildroot master(since 2020.02 version).


Step two. QPDF — shadow warrior


QPDF is used in CUPS for internal file converting, and it's a required dependence for cups. Using trace and cups error-log, I’ve founded a strange message:


access ("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)

I tested how it works and found a problem:


$ file  /usr/share/cups/data/secret.pdf
/usr/share/cups/data/secret.pdf: PDF document, version 1.2

$ qpdf  /usr/share/cups/data/secret.pdf -
open (): No such file or directory

I tested this command on my desktop with Linux Mint and got a normal result:


alexey@comp:~/Downloads$ file /usr/share/cups/data/secret.pdf
/usr/share/cups/data/secret.pdf: PDF document, version 1.2
alexey@comp:~/Downloads$ qpdf /usr/share/cups/data/secret.pdf - |head
%PDF-1.2
%????
1 0 obj
<< /Pages 3 0 R /Type /Catalog >>
endobj
2 0 obj
<< /CreationDate (D:20140612184736+02'00') /ModDate (D:20140612184736+02'00') /Producer (GPL Ghostscript 9.14) >>
endobj
3 0 obj
<< /Count 1 /Kids [ 4 0 R ] /Type /Pages >>

So looks like the problem is in QPDF build. I've studied the package file (package/QPDF/QPDF.mk) and found strange option '--without-random'


alexey@comp:~/test/article/buildroot$ head -n 15 package/QPDF/QPDF.mk
################################################################################
#
# QPDF
#
################################################################################

QPDF_VERSION = 9.1.1
QPDF_SITE = http://downloads.sourceforge.net/project/qpdf/qpdf/$(QPDF_VERSION)
QPDF_INSTALL_STAGING = YES
QPDF_LICENSE = Apache-2.0 or Artistic-2.0
QPDF_LICENSE_FILES = LICENSE.txt Artistic-2.0
QPDF_DEPENDENCIES = host-pkgconf zlib jpeg

QPDF_CONF_OPTS = --without-random

Next, I checked the available conf option:


alexey@comp:~/test/article/buildroot$ ./output/build/qpdf-9.1.1/configure --help |grep random
  --enable-insecure-random
                          whether to use stdlib's random number generator
  --enable-os-secure-random
                          whether to try to use OS-provided secure random
  --with-random=FILE      Use FILE as random number seed [auto-detected]

While I didn't find these options, I found another one "--with-random=". After that, I wrote a simple patch, rebuilt QPDF and got proper qpd work. This patch replaces "with-random" option with "--with-random=/dev/urandom"
So, minus one problem on my way.


Step three. Cups-filters and Ghostscript


CUPS-filters are needed to transform CUPS internal representation to printer-ready format. They depend on the printer model. But CUPS-FILTER (early known as foomatic) is a good collection of different printer filters, especially for a generic-like printer (generic-pdf, generic-postscript, and other).


These are the important strings in the cups-filters readme:


For compiling and using this package CUPS, libqpdf (8.3.0 or newer), libjpeg, libpng, libtiff, freetype, font-tconfig, liblcms (liblcms2 recommended), libavahi-common, libavahi-client, libdbus, and glib are needed. It is highly recommended, especially if non-PDF printers are used, to have at least one of Ghostscript, Poppler, or MuPDF.

OK, I chose Ghostscript:


Ghostscript has better color management and is generally optimized more for printing.

Now, let's look at the cups-filters package file (package/cups-filters/cups-filters.mk):


head -n 21 package/cups-filters/cups-filters.mk
################################################################################
#
# cups-filters
#
################################################################################

CUPS_FILTERS_VERSION = 1.26.0
CUPS_FILTERS_SITE = http://openprinting.org/download/cups-filters
CUPS_FILTERS_LICENSE = GPL-2.0, GPL-2.0+, GPL-3.0, GPL-3.0+, LGPL-2, LGPL-2.1+, MIT, BSD-4-Clause
CUPS_FILTERS_LICENSE_FILES = COPYING

CUPS_FILTERS_DEPENDENCIES = cups libglib2 lcms2 qpdf fontconfig freetype jpeg

CUPS_FILTERS_CONF_OPTS = --disable-imagefilters     --disable-mutool     --disable-foomatic     --disable-braille     --with-cups-config=$(STAGING_DIR)/usr/bin/cups-config     --with-sysroot=$(STAGING_DIR)     --with-pdftops=pdftops     --with-jpeg

First, I add selected Ghostscript support by adding "--with-pdftops"; select depends on Ghostscript: if enabled — add the dependency for the current build:


ifeq ($(BR2_PACKAGE_GHOSTSCRIPT),y)
CUPS_FILTERS_CONF_OPTS += --with-pdftops=gs
CUPS_FILTERS_DEPENDENCIES += ghostscript
else
CUPS_FILTERS_CONF_OPTS += --with-pdftops=pdftops
endif

Also, I added image printing (such as jpeg) support by removing " --disable-imagefilters" from config options. After that, cups-filters began to use Ghostscript as the default render.


This patch is submitted, but I don't have any response, and it's not applied to buildroot.


Step four. Ghostscript + CUPS = love


In reality, this step parallels with step 4. And it's the hardest work in this task.


alexey@comp:~/test/article/buildroot$ grep cups  package/ghostscript/ghostscript.mk
    --disable-cups \

Ghostscript is built without cups-support! I thought it's a mistake, but it's true! I added cups support:


ifeq ($(BR2_PACKAGE_CUPS),y)
GHOSTSCRIPT_DEPENDENCIES += cups
GHOSTSCRIPT_PRE_CONFIGURE_HOOKS += GHOSTSCRIPT_CUPS_CONFIG_FIX
GHOSTSCRIPT_CONF_OPTS += --enable-cups
else
GHOSTSCRIPT_CONF_OPTS += --disable-cups
endif

Easy, but it doesn't help:


gs --help| grep -i cups
/usr/share/cups/fonts : /usr/share/ghostscript/fonts :

And I didn’t see the cup device support. But on my Mint:


alexey@comp:~/test/article/buildroot$ gs --help |grep -i cups
   chp2200 cif cljet5 cljet5c cljet5pr coslw2p coslwxl cups declj250 deskjet
   /usr/share/cups/fonts : /usr/share/ghostscript/fonts :

I read Ghostscript build config and found that configure does not find cups. I opened Ghostscript configure.ac file and found something interesting:


# this is an unpleasant hack
# but if we are cross compiling, and there isn't a matching
# pkconfig for the --host setting, then don't use the 'local'
# pkconfig at all
if test x"$cross_compiling" = x"yes"; then
  AC_PATH_PROG(BUILD_PKGCONFIG, pkg-config)
  if test x"$BUILD_PKGCONFIG" = x"$PKGCONFIG" ; then
    PKGCONFIG=
  fi
fi

It seems that the author of the Ghostscript check used cross-compile. Buildroot uses cross-compile, but it uses its own PATH and other environments to prevent using build-machine utils inside of toolchain. And this hack does not work properly. Also, an equivalent hack is used for cups-config and strip_xe:


AC_PATH_TOOL(CUPSCONFIG,cups-config)
        if test x"$cross\_compiling" = x"yes"; then
                AC_PATH_PROG(BUILD_CUPSCONFIG, cups-config)
                if test x"$BUILD_CUPSCONFIG" = x"$CUPSCONFIG" ; then
                        CUPSCONFIG=
                fi
              fi

I wrote a patch to remove this hack, and pkg-config started to work fine. [Full patch is submitted (http://lists.busybox.net/pipermail/buildroot/2020-April/279673.html), but I have no response, and it's not applied. Also, copy cups-config to $(HOST_DIR)/usr/bin/ because pkg-config search in this dir.


Proper pkg-config's work resolves the problem with Freetype. Freetype is the default font render for Ghostscript. Without it, cups won't print normal text page — only blank space instead of text and images.


Instead of conclusion


Of course, I lost a lot of time before I found all these problems and fixed them. Yes, I understand that Buildroot, QPDF, cups, Ghostscript are opensource software, and I want to say "Thank you" to their developers and maintainers.


I hope that the Buildroot-community will apply all of my patches. If not, everyone can read this article and get patches from the Buildroot mailing lists.