Hi everybody!

Some time ago the SVG backend was taught to handle transparency.

This patch allows to use transparency when creating PDF files.

The patch is just a quick test. Postscript code in ps/music-drawing-routines.ps 
would need some work for better results, documentation is missing, etc.

A test file (tmp_transparency_test.ly) is included in the patch.

Knut

>From 3c35f12df9d71d4e91b1d9a47a1a5804e9a4aea1 Mon Sep 17 00:00:00 2001
From: Knut Petersen <knut_peter...@t-online.de>
Date: Thu, 26 Nov 2020 17:33:29 +0100
Subject: [PATCH] Implement transparency for PDFs.

Some time ago the SVG backend was taught
to handle transparency.

This patch now makes it possible to use
transparency also when creating PDF files.
This can be useful for example to place a
transparent stamp on a sheet of music.

Creating PNGs with transparency and
transparent background also is possible,
but only within the limits of the pngalpha
device of Ghostscript. Imagemagick convert is
clearly superior here. If you want to try it,
use the following command:

  lilypond \
    -dpixmap-format="pngalpha" \
    -dresolution=600 --png \
     tmp_transparency_test.ly

A current Ghostscript is required, e.g. 9.53.x
or git master. The actually quite good 9.26 is
too old, many intermediate versions are buggy.

This patch is just a quick test, you would
probably rework some postscript programs in
ps/music-drawing-routines.ps for better results.

If every backend supports transparency via an
alpha value, there is also the question if
the transparency property should be kept.

Status: Proof of concept.

Signed-off-by: Knut Petersen <knut_peter...@t-online.de>
---
 ps/music-drawing-routines.ps | 10 +++++
 scm/backend-library.scm      |  1 +
 scm/framework-ps.scm         | 19 +++++++++
 scm/output-ps.scm            | 12 +++---
 tmp_transparency_test.ly     | 79 ++++++++++++++++++++++++++++++++++++
 5 files changed, 115 insertions(+), 6 deletions(-)
 create mode 100644 tmp_transparency_test.ly

diff --git a/ps/music-drawing-routines.ps b/ps/music-drawing-routines.ps
index 9c4068da59..5b38453298 100644
--- a/ps/music-drawing-routines.ps
+++ b/ps/music-drawing-routines.ps
@@ -326,4 +326,14 @@ bind def
 		0 rmoveto
 	}repeat
 }bind def
+
+/setrgbacolor % r g b a
+{
+  4 1 roll
+  setrgbcolor
+  dup
+  .setfillconstantalpha
+  .setstrokeconstantalpha
+} bind def
+
 %end music-drawing-routines.ps
diff --git a/scm/backend-library.scm b/scm/backend-library.scm
index 89994e7795..4771282269 100644
--- a/scm/backend-library.scm
+++ b/scm/backend-library.scm
@@ -63,6 +63,7 @@
            "-dNODISPLAY"
            ;; see function gs-safe-run below where we use .setsafe instead
            "-dNOSAFER"
+           "-dALLOWPSTRANSPARENCY"
            "-dNOPAUSE"
            "-dBATCH"
            (if (and is-eps (not fit-page)) "-dEPSCrop")
diff --git a/scm/framework-ps.scm b/scm/framework-ps.scm
index bf5c7a3f05..81e0d38d6a 100644
--- a/scm/framework-ps.scm
+++ b/scm/framework-ps.scm
@@ -109,6 +109,14 @@
    (string-append
     (format #f "%%Page: ~a ~a\n" page-number page-number)
     "%%BeginPageSetup\n"
+    "<< /PageUsesTransparency true\n"
+    "   /CompatibilityLevel 1.4\n"
+    "   /PageSpotColors 0\n"
+    ">> setpagedevice\n"
+    "0 .pushpdf14devicefilter\n"
+    "<< >> clippath pathbbox newpath .begintransparencygroup\n"
+    "1.0 .setfillconstantalpha\n"
+    "1.0 .setstrokeconstantalpha\n"
     (if landscape?
         "page-width output-scale lily-output-units mul mul 0 translate 90 rotate\n"
         "")
@@ -116,6 +124,7 @@
     "\n"
     "gsave 0 paper-height translate set-ps-scale-to-lily-scale\n"))
   (ly:outputter-dump-stencil outputter page-stencil)
+  (ly:outputter-dump-string outputter ".endtransparencygroup\n.poppdf14devicefilter\n")
   (ly:outputter-dump-string outputter "stroke grestore\nshowpage\n"))
 
 (define (supplies-or-needs paper load-fonts?)
@@ -625,6 +634,16 @@
   (display (setup-variables paper) port)
 
   ;; adobe note 5002: should initialize variables before loading routines.
+  (display "systemdict /.setalphaisshape known not
+{
+  /PageUsesTransparency    false def
+  /.pushpdf14devicefilter  {pop} bind def
+  /.poppdf14devicefilter   {} bind def
+  /.begintransparencygroup {pop pop pop pop pop} bind def
+  /.endtransparencygroup   {} bind def
+  /.setfillconstantalpha   {pop} bind def
+  /.setstrokeconstantalpha {pop} bind def
+} if\n" port)
   (display (procset "music-drawing-routines.ps") port)
   (display (procset "lilyponddefs.ps") port)
   (display "%%EndProlog\n" port)
diff --git a/scm/output-ps.scm b/scm/output-ps.scm
index 2635709728..f0d4f3a40b 100644
--- a/scm/output-ps.scm
+++ b/scm/output-ps.scm
@@ -197,12 +197,12 @@
 
 ;; save current color on stack and set new color
 (define* (setcolor r #:optional (g #f) (b #f) (a #f))
-  ;; TODO: figure out a way to support alpha transparency
-  ;; using /SetTransparency pdfmark
-  (ly:format "gsave ~4l setrgbcolor\n"
-             (if (string? r)
-                 (list-head (css->colorlist r) 3)
-                 (list r g b))))
+  (if a (ly:format "gsave ~4l setrgbacolor\n"
+                   (list r g b a))
+        (ly:format "gsave ~4l setrgbcolor\n"
+                   (if (string? r)
+                       (list-head (css->colorlist r) 3)
+                       (list r g b)))))
 
 ;; restore color from stack
 (define (resetcolor) "grestore\n")
diff --git a/tmp_transparency_test.ly b/tmp_transparency_test.ly
new file mode 100644
index 0000000000..d921be4c92
--- /dev/null
+++ b/tmp_transparency_test.ly
@@ -0,0 +1,79 @@
+\version "2.23.0"
+
+\pointAndClickOff
+
+\paper {
+  #(set-paper-size "a4")
+  indent = 0\cm
+  left-margin = 2\cm
+  line-width = 17\cm
+  top-margin = 1.5\cm
+  bottom-margin = 1.5\cm
+  ragged-right = ##f
+  ragged-bottom = ##f
+  ragged-last-bottom = ##f
+  tagline = ##f
+}
+
+  \markup {
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 1 0 0 0.1) \filled-box #'(-10 .  20) #'( -20 .   10) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 1 0 0.2) \filled-box #'(  0 .  30) #'( -30 .    0) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 1 0.3) \filled-box #'( 10 .  40) #'( -40 .  -10) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 1 0 0 0.4) \filled-box #'( 20 .  50) #'( -50 .  -20) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 1 0 0.5) \filled-box #'( 30 .  60) #'( -60 .  -30) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 1 0.6) \filled-box #'( 40 .  70) #'( -70 .  -40) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 1 0 0 0.7) \filled-box #'( 50 .  80) #'( -80 .  -50) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 1 0 0.8) \filled-box #'( 60 .  90) #'( -90 .  -60) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 1 0.9) \filled-box #'( 70 . 100) #'(-100 .  -70) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.1) \filled-box #'( 60 .  90) #'(-110 .  -80) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.2) \filled-box #'( 50 .  80) #'(-120 .  -90) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.3) \filled-box #'( 40 .  70) #'(-130 . -100) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.4) \filled-box #'( 30 .  60) #'(-140 . -110) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.5) \filled-box #'( 20 .  50) #'(-150 . -120) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.6) \filled-box #'( 10 .  40) #'(-160 . -130) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.7) \filled-box #'(  0 .  30) #'(-170 . -140) #3
+  \with-dimensions #'(-0 . 0) #'(0 . 0) \with-color #(rgb-color 0 0 0 0.8) \filled-box #'(-10 .  20) #'(-180 . -150) #3
+}
+
+\relative a' {
+  \override Staff.StaffSymbol.color   = #(rgb-color 1 0 0 0.33)
+  \override Staff.BarLine.color       = #(rgb-color 1 0 0 0.33)
+  \override Score.BarNumber.color     = #(rgb-color 1 0 0 0.33)
+  \override Staff.TimeSignature.color = #(rgb-color 0 0 1 0.33)
+  \override Staff.Clef.color          = #(rgb-color 0 0 1 0.33)
+  \override Voice.NoteHead.color      = #(rgb-color 0 1 0 0.33)
+  \override Voice.Stem.color          = #(rgb-color 0 1 0 0.33)
+  \override Voice.Beam.color          = #(rgb-color 0 1 0 0.33)
+  \override Lyrics.LyricText.color    = #(rgb-color 0 0 0 0.33)
+  \repeat unfold 2 { a1 2 2 4 4 4 4 8 8 8 8 8 8 8 8} \bar "|."
+} \addlyrics { \repeat unfold 30 { foo } }
+
+\relative a' {
+  \override Staff.StaffSymbol.color   = #(rgb-color 1 0 0 0.66)
+  \override Staff.BarLine.color       = #(rgb-color 1 0 0 0.66)
+  \override Score.BarNumber.color     = #(rgb-color 1 0 0 0.66)
+  \override Staff.TimeSignature.color = #(rgb-color 0 0 1 0.66)
+  \override Staff.Clef.color          = #(rgb-color 0 0 1 0.66)
+  \override Voice.NoteHead.color      = #(rgb-color 0 1 0 0.66)
+  \override Voice.Stem.color          = #(rgb-color 0 1 0 0.66)
+  \override Voice.Beam.color          = #(rgb-color 0 1 0 0.66)
+  \override Lyrics.LyricText.color    = #(rgb-color 0 0 0 0.66)
+  \repeat unfold 2 { a1 2 2 4 4 4 4 8 8 8 8 8 8 8 8} \bar "|."
+} \addlyrics { \repeat unfold 30 { foo } }
+
+\relative a' {
+  \override Staff.StaffSymbol.color   = #(rgb-color 1 0 0 1.00)
+  \override Staff.BarLine.color       = #(rgb-color 1 0 0 1.00)
+  \override Score.BarNumber.color     = #(rgb-color 1 0 0 1.00)
+  \override Staff.TimeSignature.color = #(rgb-color 0 0 1 1.00)
+  \override Staff.Clef.color          = #(rgb-color 0 0 1 1.00)
+  \override Voice.NoteHead.color      = #(rgb-color 0 1 0 1.00)
+  \override Voice.Stem.color          = #(rgb-color 0 1 0 1.00)
+  \override Voice.Beam.color          = #(rgb-color 0 1 0 1.00)
+  \override Lyrics.LyricText.color    = #(rgb-color 0 0 0 1.00)
+  \repeat unfold 2 { a1 2 2 4 4 4 4 8 8 8 8 8 8 8 8} \bar "|."
+} \addlyrics { \repeat unfold 30 { foo } }
+
+\markup {
+  \with-dimensions #'(-0 . 0) #'(0 . 0){\column{\vspace #-24 \rotate #55 \with-color #(rgb-color 1 0 1 0.5) \fill-line { \fontsize #18 "Transparency Test" }}
+}}
-- 
2.29.2

Reply via email to