You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
cfFilterPDFToPDF() in 2.1.1 tagged its output with % This file was generated by pdftopdf / %%PDFTOPDFNumCopies / %%PDFTOPDFCollate. The PDFio rewrite in 2.2.x emits none of them. libppd's ppdFilterPDFToPS() uses those markers to know that page-level options were already applied upstream; without them it forwards page-ranges to its pstops stage, which applies the range a second time — to a document already reduced to exactly that range. Unless the range starts at page 1, the second selection re-indexes into the already-trimmed document, so the job silently loses pages or comes out empty altogether — and foomatic-rip still exits 0, with CUPS recording job-completed-successfully.
The same missing markers are likely behind #226 and #231 (copy count lost), which are the %%PDFTOPDFNumCopies half of the same contract.
application/vnd.cups-pdf costs 0, so cupsd routes application/postscript → universal → application/vnd.cups-pdf → foomatic-rip. foomatic-rip must then convert PDF back to PostScript for the driver.
Steps to reproduce
Any queue whose PPD is a Foomatic one with a PostScript-input driver, and any 20-page document with numbered pages:
Actual: 1 page — the original page 5 — then nothing at all. Both jobs report job-completed-successfully, with nothing logged at the default LogLevel.
Also seen in normal use
The same happens printing from a GUI application with every option left at its default except the page range — here Okular, which sends the whole document as PostScript plus a page-ranges attribute. With ranges far enough into the document to hit the empty case, the job ends job-completed-successfully with job-impressions-completed=0 and total 0 in page_log, having printed nothing.
Extracting the same pages beforehand and printing them with no page-ranges works.
Everything below is what I found investigating it, rather than part of the reproduction.
page-ranges is applied twice
The option is applied at two successive stages of the chain. universal/cfFilterPDFToPDF selects the pages, correctly. foomatic-rip then has to convert that PDF back to PostScript for the driver, and its pstops stage applies the same page-ranges again — to a document that by then contains only those pages.
Until 2.2.x a guard prevented exactly this: pdftopdf marked its output with a comment, and libppd skipped page-level options whenever it saw that marker. The PDFio rewrite stopped emitting the marker, so the guard no longer fires.
The first stage on its own is correct, and its output carries no marker:
Passing that correct 3-page selection to foomatic-rip with the same option re-applies it:
export PPD=$PWD/QUEUE.ppd PATH=/usr/lib/cups/filter:$PATH# what should happen — the range was already applied upstream, so this stage is a no-op
foomatic-rip 1 u t 1 "" s1.pdf > correct.prn
# what happens in 2.2.x — the range is applied a second time
foomatic-rip 1 u t 1 "page-ranges=3-5" s1.pdf > wrong.prn
pstops receives %%Pages: 3 in both runs, so it is re-selecting from the already-trimmed document. Substituting cat for the driver in the PPD makes that output countable: without the option, three pages (%%Page: 1 1, 2 2, 3 3); with it, a single page (%%Page: 3 1) whose content is Page 5.
Root cause
libppd/ppd/pdftops.c:578 strips page-management options from the pstops option list only when pdftopdfapplied is set:
if (pdftopdfapplied)
{
for (j=0, exclude=pstops_exclude_page_management[j]; exclude;
j++, exclude=pstops_exclude_page_management[j])
if (!strcasecmp(option->name, exclude))
break;
if (exclude)
continue;
}
pstops_exclude_page_management[] already lists page-ranges, input-page-ranges, page-set, number-up, Collate, OutputOrder, … under a comment stating the intent exactly: "we have applied these options already to the PDF input … by use of the pdftopdf filter before this filter."
pdftopdfapplied is set only by parse_pdftopdf_comment() (pdftops.c:143), which scans the head of the PDF for %%PDFTOPDFNumCopies, %%PDFTOPDFCollate, or % This file was generated by pdftopdf.
libcupsfilters 2.1.1 wrote those comments at cupsfilters/pdftopdf/pdftopdf.cxx:964 (output.push_back("% This file was generated by pdftopdf") … proc->set_comments(output)). In 2.2.x/master, cupsfilters/pdftopdf.c has no marker emission at all — grepping it for PDFTOPDF returns nothing — and its output begins %PDF-1.7 straight into objects.
So pdftopdfapplied stays 0 and the guard never fires.
Confirmed by running the universal filter over the same input against each library version. 2.1.1 emits all three markers, 2.2.1 emits none:
% This file was generated by pdftopdf
%%PDFTOPDFNumCopies : 1
%%PDFTOPDFCollate : false
Feeding each of those results to the same foomatic-rip isolates the effect — only the presence of the markers differs:
input produced by
output bytes, no option
output bytes, page-ranges=3-5
2.1.1
30744
30744
byte-identical — guard strips the option
2.2.1
30728
24476
range applied a second time
Scope
Seven stock PPDs, four drivers (foo2hbpl2, foo2zjs, foo2qpdl, foo2lava), and both the Ghostscript and the Poppler-pdftops renderer branch of ppdFilterPDFToPS().
Also reproduced on a second, independently installed Arch machine, on a default printer configuration with no manual settings at all. No local CUPS customisation on either machine — no MIME overrides in /etc/cups, no /etc/foomatic/filter.conf, cupsd.conf unmodified from the packaged default.
Any range not starting at page 1, because the second pass re-indexes into the already-trimmed document. page-ranges=3-5 on a 20-page file prints a single page — the original page 5 — with no error of any kind; ranges reaching further into the document lose everything and print nothing.
Silent — the job reports success and nothing appears in error_log at default LogLevel; at debug level the only trace is No pages left, outputting empty file. That part is intended handling for a genuine zero-page job (Graceful/Consistent handling of zero-page jobs cups-filters#117); the defect is that a valid selection becomes a zero-page job.
number-up, page-set, Collate and OutputOrder sit behind the same guard, so the same double application likely reaches them too.
That issue shows the same symptom and the same No pages left line, explained there as page-ranges counting impressions when number-up > 1 (apple/cups#4838). That does not apply here: number-up = 1 and copies = 1 throughout, and the input to the failing stage already contains exactly the requested pages, so the second application is redundant regardless of impression semantics.
Diagnosed and drafted with Claude (Claude Code), then reviewed, corrected and cut down by me before filing. Every command, number and code reference above was actually run or checked on the affected systems; hypotheses that did not survive testing were removed rather than left in as speculation.
Summary
cfFilterPDFToPDF()in 2.1.1 tagged its output with% This file was generated by pdftopdf/%%PDFTOPDFNumCopies/%%PDFTOPDFCollate. The PDFio rewrite in 2.2.x emits none of them. libppd'sppdFilterPDFToPS()uses those markers to know that page-level options were already applied upstream; without them it forwardspage-rangesto itspstopsstage, which applies the range a second time — to a document already reduced to exactly that range. Unless the range starts at page 1, the second selection re-indexes into the already-trimmed document, so the job silently loses pages or comes out empty altogether — andfoomatic-ripstill exits 0, with CUPS recordingjob-completed-successfully.The same missing markers are likely behind #226 and #231 (copy count lost), which are the
%%PDFTOPDFNumCopieshalf of the same contract.Environment
Arch Linux x86_64 — cups 2.4.19, cups-filters 2.0.1, libcupsfilters 2.2.1, libppd 2.1.1, poppler 26.08.0, ghostscript 10.07.1.
PPD
Dell C1765 Foomatic/foo2hbpl2(foo2zjs):application/vnd.cups-pdfcosts 0, so cupsd routesapplication/postscript → universal → application/vnd.cups-pdf → foomatic-rip. foomatic-rip must then convert PDF back to PostScript for the driver.Steps to reproduce
Any queue whose PPD is a Foomatic one with a PostScript-input driver, and any 20-page document with numbered pages:
Expected: 3 pages, then 6 pages.
Actual: 1 page — the original page 5 — then nothing at all. Both jobs report
job-completed-successfully, with nothing logged at the default LogLevel.Also seen in normal use
The same happens printing from a GUI application with every option left at its default except the page range — here Okular, which sends the whole document as PostScript plus a
page-rangesattribute. With ranges far enough into the document to hit the empty case, the job endsjob-completed-successfullywithjob-impressions-completed=0andtotal 0inpage_log, having printed nothing.Extracting the same pages beforehand and printing them with no
page-rangesworks.Everything below is what I found investigating it, rather than part of the reproduction.
page-rangesis applied twiceThe option is applied at two successive stages of the chain.
universal/cfFilterPDFToPDFselects the pages, correctly.foomatic-ripthen has to convert that PDF back to PostScript for the driver, and itspstopsstage applies the samepage-rangesagain — to a document that by then contains only those pages.Until 2.2.x a guard prevented exactly this:
pdftopdfmarked its output with a comment, and libppd skipped page-level options whenever it saw that marker. The PDFio rewrite stopped emitting the marker, so the guard no longer fires.The first stage on its own is correct, and its output carries no marker:
Passing that correct 3-page selection to
foomatic-ripwith the same option re-applies it:pstopsreceives%%Pages: 3in both runs, so it is re-selecting from the already-trimmed document. Substitutingcatfor the driver in the PPD makes that output countable: without the option, three pages (%%Page: 1 1,2 2,3 3); with it, a single page (%%Page: 3 1) whose content isPage 5.Root cause
libppd/ppd/pdftops.c:578strips page-management options from the pstops option list only whenpdftopdfappliedis set:pstops_exclude_page_management[]already listspage-ranges,input-page-ranges,page-set,number-up,Collate,OutputOrder, … under a comment stating the intent exactly: "we have applied these options already to the PDF input … by use of thepdftopdffilter before this filter."pdftopdfappliedis set only byparse_pdftopdf_comment()(pdftops.c:143), which scans the head of the PDF for%%PDFTOPDFNumCopies,%%PDFTOPDFCollate, or% This file was generated by pdftopdf.libcupsfilters 2.1.1 wrote those comments at
cupsfilters/pdftopdf/pdftopdf.cxx:964(output.push_back("% This file was generated by pdftopdf")…proc->set_comments(output)). In 2.2.x/master,cupsfilters/pdftopdf.chas no marker emission at all — grepping it forPDFTOPDFreturns nothing — and its output begins%PDF-1.7straight into objects.So
pdftopdfappliedstays 0 and the guard never fires.Confirmed by running the
universalfilter over the same input against each library version. 2.1.1 emits all three markers, 2.2.1 emits none:Feeding each of those results to the same
foomatic-ripisolates the effect — only the presence of the markers differs:page-ranges=3-5Scope
foo2hbpl2,foo2zjs,foo2qpdl,foo2lava), and both the Ghostscript and the Poppler-pdftopsrenderer branch ofppdFilterPDFToPS()./etc/cups, no/etc/foomatic/filter.conf,cupsd.confunmodified from the packaged default.page-ranges=3-5on a 20-page file prints a single page — the original page 5 — with no error of any kind; ranges reaching further into the document lose everything and print nothing.error_logat default LogLevel; at debug level the only trace isNo pages left, outputting empty file.That part is intended handling for a genuine zero-page job (Graceful/Consistent handling of zero-page jobs cups-filters#117); the defect is that a valid selection becomes a zero-page job.number-up,page-set,CollateandOutputOrdersit behind the same guard, so the same double application likely reaches them too.Not the
number-upinteraction of OpenPrinting/cups-filters#690That issue shows the same symptom and the same
No pages leftline, explained there aspage-rangescounting impressions whennumber-up > 1(apple/cups#4838). That does not apply here:number-up = 1andcopies = 1throughout, and the input to the failing stage already contains exactly the requested pages, so the second application is redundant regardless of impression semantics.Diagnosed and drafted with Claude (Claude Code), then reviewed, corrected and cut down by me before filing. Every command, number and code reference above was actually run or checked on the affected systems; hypotheses that did not survive testing were removed rather than left in as speculation.