Skip to content

Commit

Permalink
#92 - Convert pptx to a Carousel-based HTML slideshow
Browse files Browse the repository at this point in the history
  • Loading branch information
stackpr committed Mar 30, 2017
1 parent 9301da8 commit d83f95d
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 16 deletions.
28 changes: 26 additions & 2 deletions src/Configuration/ConfigurationDefaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public function __construct(&$settings) {
$lsb = trim(`which lsb_release`);
if ($lsb !== '') {
$lsb = escapeshellarg($lsb);
$settings['operating_system'] = trim(`$lsb -is`);
$settings['operating_system_version'] = trim(`$lsb -rs`);
$settings['operating_system'] = trim(`$lsb -is`);
$settings['operating_system_version'] = trim(`$lsb -rs`);
}
}
elseif ($settings['operating_system'] === 'Windows NT') {
Expand Down Expand Up @@ -123,10 +123,21 @@ public function __construct(&$settings) {
"flatten" => 1,
),
),
'pdf->(zip/jpg|directory/jpg)' => array(
'imagemagick:default' => array(
'#engine' => 'Convert\\ImageMagick',
"colorspace" => "sRGB",
// Double-resolution output
'density' => '144',
),
),
'pdf->(zip/png|directory/png)' => array(
'imagemagick:default' => array(
'#engine' => 'Convert\\ImageMagick',
"colorspace" => "sRGB",
// Supersampling with double-resolution output
'density' => '288',
'resize' => '50%',
),
),
'ps->pdf' => array(
Expand All @@ -152,11 +163,24 @@ public function __construct(&$settings) {
'#engine' => 'Convert\\Unoconv',
),
),
'(ppt|pptx)->(directory/jpg|zip/jpg)' => array(
'pptx->pdf->img' => array(
'#engine' => 'Chain',
'chain' => 'pptx->pdf->*',
),
),
'pptx->json' => array(
'nativemeta:default' => array(
'#engine' => 'Convert\\NativeMeta',
),
),
'pptx->(directory/slideshow|zip/slideshow)' => array(
'nativeslideshow:schedule' => array(
'#engine' => 'Convert\\NativeSlideshow',
'mode' => 'schedule',
'captions' => FALSE,
),
),
'rtf->pdf' => array(
'unoconv:default' => array(
'#engine' => 'Convert\\Unoconv',
Expand Down
24 changes: 16 additions & 8 deletions src/Engine/Chain.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,35 @@ public function convertFile($source, $destination) {
}

// Iterate through the chain.
$s_path = $this->getTempFile(array_shift($links));
$c0 = array_shift($links);
$s_path = $this->getTempFile($c0);
copy($source, $s_path);
while (!empty($links)) {
try {
$d_path = $this->getTempFile(array_shift($links));
$this->converter->convertFile($s_path, $d_path);
$c1 = array_shift($links);
if (empty($links)) {
$d_path = $destination;
}
else {
$d_path = $this->getTempFile($c1);
}
$this->converter->convertFile($s_path, $d_path, "$c0->$c1");
unlink($s_path);
if (!is_file($d_path)) {
if (!is_file($d_path) && !is_dir($d_path)) {
throw new \ErrorException("Conversion failed.");
}
$s_path = $d_path;
$c0 = $c1;
} catch (\Exception $e) {
unlink($s_path);
if (is_file($d_path)) {
if (is_file($s_path) && $s_path !== $source) {
unlink($s_path);
}
if (is_file($d_path) && $d_path !== $destination) {
unlink($d_path);
}
throw $e;
}
}

rename($d_path, $destination);
return $this;
}

Expand Down
14 changes: 12 additions & 2 deletions src/Engine/Convert/ImageMagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
use FileConverter\Engine\Helper\Archive;
class ImageMagick extends EngineBase {
protected $cmd_options = array(
array(
'name' => 'density',
'mode' => Shell::SHELL_ARG_BASIC_SGL,
// Density is important when converting from PDF or vector
// System default: 72
// Higher density effectively increases the image size
// Higher density can combine with `-resize 50%` for "supersampling"
'default' => NULL,
'group' => 1,
),
array(
'name' => 'resize',
'mode' => Shell::SHELL_ARG_BASIC_SGL,
Expand Down Expand Up @@ -65,14 +75,14 @@ public function convertFile($source, $destination) {
$tmp = $archive->getTempDirectory();
// Rename the multiple image files in a standardized way
if (is_file("$imagePath/page.$ext")) {
$path = "$tmp/img/page1.$ext";
$path = "$tmp/page1.$ext";
$this->isTempWritable($path);
rename("$imagePath/page.$ext", $path);
}
else {
$i = 0;
while (is_file("$imagePath/page-$i.$ext")) {
$path = "$tmp/img/page" . ($i + 1) . ".$ext";
$path = "$tmp/page" . ($i + 1) . ".$ext";
$this->isTempWritable($path);
rename("$imagePath/page-$i.$ext", $path);
++$i;
Expand Down
6 changes: 3 additions & 3 deletions src/Engine/Convert/NativeMeta.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function convertFile($source, $destination) {
$files = Splash::fromArray($files);

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

$meta['slides'][$number - 1] = $slide;
$meta['items'][$number - 1] = $slide;
}
ksort($meta['slides']);
ksort($meta['items']);

break;

Expand Down
148 changes: 148 additions & 0 deletions src/Engine/Convert/NativeSlideshow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
/*
* This file is part of the FileConverter package.
*
* (c) Greg Payne
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FileConverter\Engine\Convert;
use FileConverter\Engine\EngineBase;
use FileConverter\Engine\Helper\Archive;
use GuzzleHttp\json_decode;

class NativeSlideshow extends EngineBase {
/**
* @todo use Message::fromString($raw) to convert from eml to other formats
*/
public function convertFile($source, $destination) {
// Add default configurations
$this->configuration = array_replace(array(
'mode' => 'default',
), (array) $this->configuration);

// Create a folder containing all of the images
$imageArchive = new Archive($this);
$imagePath = $imageArchive->getTempDirectory();
$this->converter->convertFile($source, $imagePath, $this->conversion[0]
. '->directory/jpg');

// Build the slideshow configuration
$slideshow = array(
'title' => 'Slideshow',
'items' => array(),
);
foreach ($this->configuration as $k => $v) {
if ($k{0} !== '#') {
$slideshow[$k] = $v;
}
}
try {
// Get the configuration for the slides.
$tmpJson = $this->getTempFile('.json');
$this->converter->convertFile($source, $tmpJson, $this->conversion[0]
. '->json');
$slideshow = array_replace($slideshow, (array) json_decode(file_get_contents($tmpJson), TRUE));

// Apply the slideshow mode.
foreach ($slideshow['items'] as $i => &$slide) {
if (!isset($slide['notes'])) {
if ($slideshow['mode'] === 'schedule') {
unset($slideshow['items'][$i]);
}
}
else {
// Extract settings from the slide notes.
$keys = array(
'@(?<k>SLIDESHOW\.[^:]+):(?<v>[^\n]+)(?:\n|$)@s',
'@(?<k>BEGIN):(?<v>[^\n]+)(?:\n|$)@s',
'@(?<k>TITLE):(?<v>[^\n]+)(?:\n|$)@s',
'@(?<k>DESCRIPTION):(?<v>[^\n]+)(?:\n|$)@s',
);
foreach ($keys as $match) {
if (preg_match($match, $slide['notes'], $arr)) {
if (strpos($arr['k'], 'SLIDESHOW.') === 0) {
$slideshow[strtolower(substr($arr['k'], 10))] = $arr['v'];
}
else {
$slide[strtolower($arr['k'])] = $arr['v'];
}
}
}
unset($slide['notes']);
}
}
unset($slide);
$slideshow['items'] = array_values($slideshow['items']);
} catch (\Exception $e) {
// Build the default meta data based on the images.
$slideshow['mode'] = 'default';
$i = 1;
while (is_file("$imagePath/page$i.jpg")) {
$slideshow['items'][] = array(
'number' => $i,
'title' => "Slide $i",
);
++$i;
}
}

// Build the actual slideshow archive.
$archive = new Archive($this);
$tmp = $archive->getTempDirectory();
file_put_contents("$tmp/meta.json", json_encode($slideshow));

// Copy the images as appropriate.
foreach ($slideshow['items'] as $slide) {
$from = "$imagePath/page$slide[number].jpg";
$to = "$tmp/img/slide$slide[number].jpg";
$this->isTempWritable($to);
copy($from, $to);
}

// Copy the slideshow resources.
$to = "$tmp/js/slideshow.js";
$this->isTempWritable($to);
copy(__DIR__ . '/Resources/Slideshow/slideshow.js', $to);
$to = "$tmp/css/slideshow.css";
$this->isTempWritable($to);
copy(__DIR__ . '/Resources/Slideshow/slideshow.css', $to);

// The index file involves template variables.
// Provide a raw
$to = "$tmp/index.htm";
$this->isTempWritable($to);
$content = file_get_contents(__DIR__ . '/Resources/Slideshow/index.htm');
$content = strtr($content, array(
'{{ slideshow.title|escape }}' => htmlspecialchars($slideshow['title']),
'{{ slideshow|json_encode() }}' => json_encode($slideshow),
));
file_put_contents($to, $content);

// Save the slideshow.
$archive->save($destination);
}

protected function getHelpInstallation($os, $os_version) {
$help = array(
'title' => 'Native Slideshow Extractor',
);
switch ($os) {
case 'Ubuntu':
$help['os'] = 'confirmed on Ubuntu 16.04';
$help['notes'] = array(
'composer update',
);
return $help;
}

return parent::getHelpInstallation($os, $os_version);
}

public function isAvailable() {
return TRUE;
}

}
35 changes: 35 additions & 0 deletions src/Engine/Convert/Resources/Slideshow/index.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>{{ slideshow.title|escape }}</title>

<!-- Bootstrap CDN: http://getbootstrap.com/getting-started/ -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

<!-- Embed the slideshow js -->
<script src="js/slideshow.js"></script>
<script type="text/javascript">
slideshow({{ slideshow|json_encode() }});
</script>
</body>
</html>
Empty file.
Loading

0 comments on commit d83f95d

Please sign in to comment.