Skip to content

Commit d83f95d

Browse files
committed
#92 - Convert pptx to a Carousel-based HTML slideshow
1 parent 9301da8 commit d83f95d

File tree

10 files changed

+325
-16
lines changed

10 files changed

+325
-16
lines changed

src/Configuration/ConfigurationDefaults.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ public function __construct(&$settings) {
3636
$lsb = trim(`which lsb_release`);
3737
if ($lsb !== '') {
3838
$lsb = escapeshellarg($lsb);
39-
$settings['operating_system'] = trim(`$lsb -is`);
40-
$settings['operating_system_version'] = trim(`$lsb -rs`);
39+
$settings['operating_system'] = trim(`$lsb -is`);
40+
$settings['operating_system_version'] = trim(`$lsb -rs`);
4141
}
4242
}
4343
elseif ($settings['operating_system'] === 'Windows NT') {
@@ -123,10 +123,21 @@ public function __construct(&$settings) {
123123
"flatten" => 1,
124124
),
125125
),
126+
'pdf->(zip/jpg|directory/jpg)' => array(
127+
'imagemagick:default' => array(
128+
'#engine' => 'Convert\\ImageMagick',
129+
"colorspace" => "sRGB",
130+
// Double-resolution output
131+
'density' => '144',
132+
),
133+
),
126134
'pdf->(zip/png|directory/png)' => array(
127135
'imagemagick:default' => array(
128136
'#engine' => 'Convert\\ImageMagick',
129137
"colorspace" => "sRGB",
138+
// Supersampling with double-resolution output
139+
'density' => '288',
140+
'resize' => '50%',
130141
),
131142
),
132143
'ps->pdf' => array(
@@ -152,11 +163,24 @@ public function __construct(&$settings) {
152163
'#engine' => 'Convert\\Unoconv',
153164
),
154165
),
166+
'(ppt|pptx)->(directory/jpg|zip/jpg)' => array(
167+
'pptx->pdf->img' => array(
168+
'#engine' => 'Chain',
169+
'chain' => 'pptx->pdf->*',
170+
),
171+
),
155172
'pptx->json' => array(
156173
'nativemeta:default' => array(
157174
'#engine' => 'Convert\\NativeMeta',
158175
),
159176
),
177+
'pptx->(directory/slideshow|zip/slideshow)' => array(
178+
'nativeslideshow:schedule' => array(
179+
'#engine' => 'Convert\\NativeSlideshow',
180+
'mode' => 'schedule',
181+
'captions' => FALSE,
182+
),
183+
),
160184
'rtf->pdf' => array(
161185
'unoconv:default' => array(
162186
'#engine' => 'Convert\\Unoconv',

src/Engine/Chain.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,35 @@ public function convertFile($source, $destination) {
2323
}
2424

2525
// Iterate through the chain.
26-
$s_path = $this->getTempFile(array_shift($links));
26+
$c0 = array_shift($links);
27+
$s_path = $this->getTempFile($c0);
2728
copy($source, $s_path);
2829
while (!empty($links)) {
2930
try {
30-
$d_path = $this->getTempFile(array_shift($links));
31-
$this->converter->convertFile($s_path, $d_path);
31+
$c1 = array_shift($links);
32+
if (empty($links)) {
33+
$d_path = $destination;
34+
}
35+
else {
36+
$d_path = $this->getTempFile($c1);
37+
}
38+
$this->converter->convertFile($s_path, $d_path, "$c0->$c1");
3239
unlink($s_path);
33-
if (!is_file($d_path)) {
40+
if (!is_file($d_path) && !is_dir($d_path)) {
3441
throw new \ErrorException("Conversion failed.");
3542
}
3643
$s_path = $d_path;
44+
$c0 = $c1;
3745
} catch (\Exception $e) {
38-
unlink($s_path);
39-
if (is_file($d_path)) {
46+
if (is_file($s_path) && $s_path !== $source) {
47+
unlink($s_path);
48+
}
49+
if (is_file($d_path) && $d_path !== $destination) {
4050
unlink($d_path);
4151
}
4252
throw $e;
4353
}
4454
}
45-
46-
rename($d_path, $destination);
4755
return $this;
4856
}
4957

src/Engine/Convert/ImageMagick.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@
1515
use FileConverter\Engine\Helper\Archive;
1616
class ImageMagick extends EngineBase {
1717
protected $cmd_options = array(
18+
array(
19+
'name' => 'density',
20+
'mode' => Shell::SHELL_ARG_BASIC_SGL,
21+
// Density is important when converting from PDF or vector
22+
// System default: 72
23+
// Higher density effectively increases the image size
24+
// Higher density can combine with `-resize 50%` for "supersampling"
25+
'default' => NULL,
26+
'group' => 1,
27+
),
1828
array(
1929
'name' => 'resize',
2030
'mode' => Shell::SHELL_ARG_BASIC_SGL,
@@ -65,14 +75,14 @@ public function convertFile($source, $destination) {
6575
$tmp = $archive->getTempDirectory();
6676
// Rename the multiple image files in a standardized way
6777
if (is_file("$imagePath/page.$ext")) {
68-
$path = "$tmp/img/page1.$ext";
78+
$path = "$tmp/page1.$ext";
6979
$this->isTempWritable($path);
7080
rename("$imagePath/page.$ext", $path);
7181
}
7282
else {
7383
$i = 0;
7484
while (is_file("$imagePath/page-$i.$ext")) {
75-
$path = "$tmp/img/page" . ($i + 1) . ".$ext";
85+
$path = "$tmp/page" . ($i + 1) . ".$ext";
7686
$this->isTempWritable($path);
7787
rename("$imagePath/page-$i.$ext", $path);
7888
++$i;

src/Engine/Convert/NativeMeta.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function convertFile($source, $destination) {
3737
$files = Splash::fromArray($files);
3838

3939
// Build the slides.
40-
$meta['slides'] = array();
40+
$meta['items'] = array();
4141
foreach ($files->regex("@ppt/slides/slide\d+.xml$@") as $file) {
4242
$slide = array();
4343
$number = preg_replace('@^ppt/slides/slide(\d+)\.xml$@s', '\1', $file);
@@ -74,9 +74,9 @@ public function convertFile($source, $destination) {
7474
$slide['notes'] = $note;
7575
}
7676

77-
$meta['slides'][$number - 1] = $slide;
77+
$meta['items'][$number - 1] = $slide;
7878
}
79-
ksort($meta['slides']);
79+
ksort($meta['items']);
8080

8181
break;
8282

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
/*
3+
* This file is part of the FileConverter package.
4+
*
5+
* (c) Greg Payne
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace FileConverter\Engine\Convert;
12+
use FileConverter\Engine\EngineBase;
13+
use FileConverter\Engine\Helper\Archive;
14+
use GuzzleHttp\json_decode;
15+
16+
class NativeSlideshow extends EngineBase {
17+
/**
18+
* @todo use Message::fromString($raw) to convert from eml to other formats
19+
*/
20+
public function convertFile($source, $destination) {
21+
// Add default configurations
22+
$this->configuration = array_replace(array(
23+
'mode' => 'default',
24+
), (array) $this->configuration);
25+
26+
// Create a folder containing all of the images
27+
$imageArchive = new Archive($this);
28+
$imagePath = $imageArchive->getTempDirectory();
29+
$this->converter->convertFile($source, $imagePath, $this->conversion[0]
30+
. '->directory/jpg');
31+
32+
// Build the slideshow configuration
33+
$slideshow = array(
34+
'title' => 'Slideshow',
35+
'items' => array(),
36+
);
37+
foreach ($this->configuration as $k => $v) {
38+
if ($k{0} !== '#') {
39+
$slideshow[$k] = $v;
40+
}
41+
}
42+
try {
43+
// Get the configuration for the slides.
44+
$tmpJson = $this->getTempFile('.json');
45+
$this->converter->convertFile($source, $tmpJson, $this->conversion[0]
46+
. '->json');
47+
$slideshow = array_replace($slideshow, (array) json_decode(file_get_contents($tmpJson), TRUE));
48+
49+
// Apply the slideshow mode.
50+
foreach ($slideshow['items'] as $i => &$slide) {
51+
if (!isset($slide['notes'])) {
52+
if ($slideshow['mode'] === 'schedule') {
53+
unset($slideshow['items'][$i]);
54+
}
55+
}
56+
else {
57+
// Extract settings from the slide notes.
58+
$keys = array(
59+
'@(?<k>SLIDESHOW\.[^:]+):(?<v>[^\n]+)(?:\n|$)@s',
60+
'@(?<k>BEGIN):(?<v>[^\n]+)(?:\n|$)@s',
61+
'@(?<k>TITLE):(?<v>[^\n]+)(?:\n|$)@s',
62+
'@(?<k>DESCRIPTION):(?<v>[^\n]+)(?:\n|$)@s',
63+
);
64+
foreach ($keys as $match) {
65+
if (preg_match($match, $slide['notes'], $arr)) {
66+
if (strpos($arr['k'], 'SLIDESHOW.') === 0) {
67+
$slideshow[strtolower(substr($arr['k'], 10))] = $arr['v'];
68+
}
69+
else {
70+
$slide[strtolower($arr['k'])] = $arr['v'];
71+
}
72+
}
73+
}
74+
unset($slide['notes']);
75+
}
76+
}
77+
unset($slide);
78+
$slideshow['items'] = array_values($slideshow['items']);
79+
} catch (\Exception $e) {
80+
// Build the default meta data based on the images.
81+
$slideshow['mode'] = 'default';
82+
$i = 1;
83+
while (is_file("$imagePath/page$i.jpg")) {
84+
$slideshow['items'][] = array(
85+
'number' => $i,
86+
'title' => "Slide $i",
87+
);
88+
++$i;
89+
}
90+
}
91+
92+
// Build the actual slideshow archive.
93+
$archive = new Archive($this);
94+
$tmp = $archive->getTempDirectory();
95+
file_put_contents("$tmp/meta.json", json_encode($slideshow));
96+
97+
// Copy the images as appropriate.
98+
foreach ($slideshow['items'] as $slide) {
99+
$from = "$imagePath/page$slide[number].jpg";
100+
$to = "$tmp/img/slide$slide[number].jpg";
101+
$this->isTempWritable($to);
102+
copy($from, $to);
103+
}
104+
105+
// Copy the slideshow resources.
106+
$to = "$tmp/js/slideshow.js";
107+
$this->isTempWritable($to);
108+
copy(__DIR__ . '/Resources/Slideshow/slideshow.js', $to);
109+
$to = "$tmp/css/slideshow.css";
110+
$this->isTempWritable($to);
111+
copy(__DIR__ . '/Resources/Slideshow/slideshow.css', $to);
112+
113+
// The index file involves template variables.
114+
// Provide a raw
115+
$to = "$tmp/index.htm";
116+
$this->isTempWritable($to);
117+
$content = file_get_contents(__DIR__ . '/Resources/Slideshow/index.htm');
118+
$content = strtr($content, array(
119+
'{{ slideshow.title|escape }}' => htmlspecialchars($slideshow['title']),
120+
'{{ slideshow|json_encode() }}' => json_encode($slideshow),
121+
));
122+
file_put_contents($to, $content);
123+
124+
// Save the slideshow.
125+
$archive->save($destination);
126+
}
127+
128+
protected function getHelpInstallation($os, $os_version) {
129+
$help = array(
130+
'title' => 'Native Slideshow Extractor',
131+
);
132+
switch ($os) {
133+
case 'Ubuntu':
134+
$help['os'] = 'confirmed on Ubuntu 16.04';
135+
$help['notes'] = array(
136+
'composer update',
137+
);
138+
return $help;
139+
}
140+
141+
return parent::getHelpInstallation($os, $os_version);
142+
}
143+
144+
public function isAvailable() {
145+
return TRUE;
146+
}
147+
148+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
3+
<!DOCTYPE html>
4+
<html lang="en">
5+
<head>
6+
<meta charset="utf-8">
7+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
8+
<meta name="viewport" content="width=device-width, initial-scale=1">
9+
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
10+
<title>{{ slideshow.title|escape }}</title>
11+
12+
<!-- Bootstrap CDN: http://getbootstrap.com/getting-started/ -->
13+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
14+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
15+
16+
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
17+
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
18+
<!--[if lt IE 9]>
19+
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
20+
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
21+
<![endif]-->
22+
</head>
23+
<body>
24+
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
25+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
26+
<!-- Include all compiled plugins (below), or include individual files as needed -->
27+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
28+
29+
<!-- Embed the slideshow js -->
30+
<script src="js/slideshow.js"></script>
31+
<script type="text/javascript">
32+
slideshow({{ slideshow|json_encode() }});
33+
</script>
34+
</body>
35+
</html>

src/Engine/Convert/Resources/Slideshow/slideshow.css

Whitespace-only changes.

0 commit comments

Comments
 (0)