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.



67
68
69
70
71
72
73
74
# File 'lib/presently/export.rb', line 67

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.



80
81
82
# File 'lib/presently/export.rb', line 80

def notes
  @notes
end

#page_sizeObject (readonly)

Returns the value of attribute page_size.



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

def page_size
  @page_size
end

#speakerObject (readonly)

Returns the value of attribute speaker.



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

def speaker
  @speaker
end

#timingObject (readonly)

Returns the value of attribute timing.



86
87
88
# File 'lib/presently/export.rb', line 86

def timing
  @timing
end

Class Method Details

.options_from_query(query) ⇒ Object

Parse export options from a URL query string.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/presently/export.rb', line 43

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.



162
163
164
# File 'lib/presently/export.rb', line 162

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.



156
157
158
# File 'lib/presently/export.rb', line 156

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

#format_duration(seconds) ⇒ Object

Format a duration in seconds as MM:SS.



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

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.



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
135
136
137
138
139
140
141
142
# File 'lib/presently/export.rb', line 99

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(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.



91
92
93
# File 'lib/presently/export.rb', line 91

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

#slidesObject

The slides in the presentation.



168
169
170
# File 'lib/presently/export.rb', line 168

def slides
	@presentation.slides
end

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



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

attr :page_size

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



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

attr :notes

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



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

attr :timing

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



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

attr :speaker