Class: Presently::Export

Inherits:
Object
  • Object
show all
Defined in:
lib/presently/export.rb

Overview

Renders a self-contained, print-ready HTML page containing all slides for PDF export.

All slides are rendered in a single page with CSS ‘break-after: page`, so a single WebDriver `print()` call produces a multi-page PDF without any merging step.

Defined Under Namespace

Classes: PageSize

Constant Summary collapse

TEMPLATE =
XRB::Template.load_file(File.expand_path("export.xrb", __dir__))

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(presentation:, page_size: PageSize::DEFAULT, notes: true, speaker: true, timing: true) ⇒ Export

Returns a new instance of Export.



59
60
61
62
63
64
65
66
# File 'lib/presently/export.rb', line 59

def initialize(presentation:, page_size: PageSize::DEFAULT, notes: true, speaker: true, timing: true)
	@presentation = presentation
	@page_size    = page_size
	@notes        = notes
	@speaker      = speaker
	@timing       = timing
	@renderer     = SlideRenderer.new
end

Instance Attribute Details

#notesObject (readonly)

Returns the value of attribute notes.



72
73
74
# File 'lib/presently/export.rb', line 72

def notes
  @notes
end

#page_sizeObject (readonly)

Returns the value of attribute page_size.



69
70
71
# File 'lib/presently/export.rb', line 69

def page_size
  @page_size
end

#speakerObject (readonly)

Returns the value of attribute speaker.



75
76
77
# File 'lib/presently/export.rb', line 75

def speaker
  @speaker
end

#timingObject (readonly)

Returns the value of attribute timing.



78
79
80
# File 'lib/presently/export.rb', line 78

def timing
  @timing
end

Class Method Details

.options_from_query(query) ⇒ Object

Parse export options from a URL query string.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/presently/export.rb', line 35

def self.options_from_query(query)
	return {} unless query
	
	params = URI.decode_www_form(query).to_h
	
	page_size = PageSize.new(
		slide_width_px:  (params["slide_width_px"]  || PageSize::DEFAULT.slide_width_px).to_i,
		slide_height_px: (params["slide_height_px"] || PageSize::DEFAULT.slide_height_px).to_i,
		notes_height_px: (params["notes_height_px"] || PageSize::DEFAULT.notes_height_px).to_i,
	)
	
	{
		notes:     params["notes"]   != "false",
		speaker:   params["speaker"] != "false",
		timing:    params["timing"]  != "false",
		page_size: page_size,
	}
end

Instance Method Details

#callObject

Render the full export page to an HTML string.



154
155
156
# File 'lib/presently/export.rb', line 154

def call
	TEMPLATE.to_string(self)
end

#expected_time_at(index) ⇒ Object

Calculate the expected elapsed time at the start of the given slide index.



148
149
150
# File 'lib/presently/export.rb', line 148

def expected_time_at(index)
	@presentation.slides.first(index).sum(&:duration)
end

#format_duration(seconds) ⇒ Object

Format a duration in seconds as ‘MM:SS`.



139
140
141
142
143
# File 'lib/presently/export.rb', line 139

def format_duration(seconds)
	minutes = seconds / 60
	secs    = seconds % 60
	format("%02d:%02d", minutes, secs)
end

#render_notes(slide, index) ⇒ Object

Render the notes panel for a single slide.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/presently/export.rb', line 91

def render_notes(slide, index)
	builder = XRB::Builder.new
	
	# Meta strip — mirrors the presenter view's timing bar.
	builder.tag(:div, class: "export-meta") do
		builder.tag(:span, class: "export-slide-number") do
			builder.text("Slide #{index} of #{@presentation.slide_count}")
		end
		
		builder.tag(:span, class: "export-filename") do
			builder.tag(:code){builder.text(File.basename(slide.path))}
		end
		
		if @timing
			elapsed = expected_time_at(index - 1)
			builder.tag(:span, class: "export-elapsed") do
				builder.text("Elapsed: #{format_duration(elapsed)}")
			end
			builder.tag(:span, class: "export-duration") do
				builder.text("Slide: #{format_duration(slide.duration)}")
			end
		end
		
		if @speaker && slide.speaker
			builder.tag(:span, class: "export-speaker") do
				builder.tag(:span, class: "speaker-label"){builder.text("🎤")}
				builder.text(" #{slide.speaker}")
			end
		end
	end
	
	# Notes panel — mirrors the presenter view's .notes section.
	builder.tag(:div, class: "notes") do
		builder.tag(:div, class: "notes-content") do
			if slide.notes && !slide.notes.empty?
				builder.raw(slide.notes.to_html)
			else
				builder.tag(:p, class: "no-notes"){builder.text("No presenter notes for this slide.")}
			end
		end
	end
	
	XRB::MarkupString.raw(builder.to_s)
end

#render_slide(slide) ⇒ Object

Render a single slide to an HTML string.



83
84
85
# File 'lib/presently/export.rb', line 83

def render_slide(slide)
	@renderer.render_to_html(slide)
end

#slidesObject

The slides in the presentation.



160
161
162
# File 'lib/presently/export.rb', line 160

def slides
	@presentation.slides
end

#The slide canvas and notes panel dimensions.=(slidecanvas) ⇒ Object



69
# File 'lib/presently/export.rb', line 69

attr :page_size

#Whether presenter notes are included.=(presenternotesareincluded. = (value)) ⇒ Object



72
# File 'lib/presently/export.rb', line 72

attr :notes

#Whether slide timing is included.=(slidetimingisincluded. = (value)) ⇒ Object



78
# File 'lib/presently/export.rb', line 78

attr :timing

#Whether speaker names are included.=(speakernamesareincluded. = (value)) ⇒ Object



75
# File 'lib/presently/export.rb', line 75

attr :speaker