Module: Prawn::SoftMask

Included in:
Document
Defined in:
lib/prawn/soft_mask.rb

Overview

This module is used to create arbitrary transparency in document. Using a soft mask allows creating more visually rich documents.

Stable API collapse

Instance Method Details

#soft_mask { ... } ⇒ void

This method returns an undefined value.

Apply soft mask.

You must group soft mask and graphics it’s applied to under ‘save_graphics_state` because soft mask is a part of graphic state in PDF.

Note that soft mask is applied only to the following content in the graphic state. Anything that comes before ‘soft_mask` is drawn without mask.

Conceptually, soft mask is an alpha channel. Luminosity of the drawing in the soft mask defines the transparency of the drawing the mask is applied to. 0.0 mask luminosity (“black”) results in a fully opaque target image and 1.0 mask luminosity (“white”) results in a fully transparent target image. Grey values result in some semi-transparent target image.

Note: you can use color in mask drawings but it makes harder to reason about the resulting value of alpha channel as it requires an additional luminosity calculation. However, this also allows achieving some advanced artistic effects (e.g. full-color photos in masks to get an effect similar to double exposure).

Examples:

pdf.save_graphics_state do
  pdf.soft_mask do
    pdf.fill_color "444444"
    pdf.fill_polygon [0, 40], [60, 10], [120, 40], [60, 68]
  end
  pdf.fill_color '000000'
  pdf.fill_rectangle [0, 50], 120, 68
end

Yields:

  • Mask content.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/prawn/soft_mask.rb', line 42

def soft_mask(&block)
  renderer.min_version(1.4)

  group_attrs = ref!(
    Type: :Group,
    S: :Transparency,
    CS: :DeviceRGB,
    I: false,
    K: false,
  )

  group = ref!(
    Type: :XObject,
    Subtype: :Form,
    BBox: state.page.dimensions,
    Group: group_attrs,
  )

  state.page.stamp_stream(group, &block)

  mask = ref!(
    Type: :Mask,
    S: :Luminosity,
    G: group,
  )

  g_state = ref!(
    Type: :ExtGState,
    SMask: mask,

    AIS: false,
    BM: :Normal,
    OP: false,
    op: false,
    OPM: 1,
    SA: true,
  )

  registry_key = {
    bbox: state.page.dimensions,
    mask: [group.stream.filters.normalized, group.stream.filtered_stream],
    page: state.page_count,
  }.hash

  if soft_mask_registry[registry_key]
    renderer.add_content("/#{soft_mask_registry[registry_key]} gs")
  else
    masks = page.resources[:ExtGState] ||= {}
    id = masks.empty? ? 'GS1' : masks.keys.max.succ
    masks[id] = g_state

    soft_mask_registry[registry_key] = id

    renderer.add_content("/#{id} gs")
  end
end