* @link http://wiki.cs50.net/render50 * @package render50 * @version 0.98 * * GNU General Public License, version 2 * http://www.gnu.org/licenses/gpl-2.0.html */ // constants define("VERSION", "0.98"); // report all errors error_reporting(E_ALL); ini_set("display_errors", true); // GeSHi require_once(dirname(__FILE__) . "/../lib/geshi/geshi.php"); // mPDF require_once(dirname(__FILE__) . "/../lib/mpdf50/mpdf.php"); // override mPDF's error handling class _mPDF extends mPDF { function Error($msg) { die("Error: $msg\n"); } } // explain usage if (in_array("-h", $argv) || in_array("--help", $argv)) { echo "Usage: php /path/to/render50.php output inputs [-i includes] [-x excludes]\n\n"; echo " output\n"; echo " name of the PDF to be outputted; if output itself does not end in .pdf,\n"; echo " the extension will be automatically appended\n\n"; echo " inputs\n"; echo " space-separated list of files and/or directories to be rendered (unless\n"; echo " implicitly excluded by -i or implicitly excluded by -x); if inputs is\n"; echo " just --, the list of files and/or directories will be read from standard\n"; echo " input, one per line\n\n"; echo " -i includes\n"; echo " space-separated list of files to be included in rendering to the exclusion\n"; echo " of files otherwise implied by inputs, as in\n"; echo " `php /path/to/render.php output.pdf directory -i *.php`\n\n"; echo " -x excludes\n"; echo " space-separated list of files to be excluded from rendering, even if\n"; echo " otherwise implied by inputs, as in\n"; echo " `php /path/to/render.php output.pdf directory -x *.css *.js`\n\n"; exit(1); } else if (in_array("-v", $argv) || in_array("--version", $argv)) { echo "render50-" . VERSION . "\n"; exit(1); } // ensure proper usage if ($argc < 3) { echo "Usage: php /path/to/render50.php output inputs [-i includes] [-x excludes]\n"; exit(1); } // filename for output $output = preg_match("/\.pdf$/i", $argv[1]) ? $argv[1] : $argv[1] . ".pdf"; // check for includes for ($i = 3; $i < $argc; $i++) { if ($argv[$i] == "-i") $includes = array(); else if ($argv[$i][0] == "-" && isset($includes)) break; else if (isset($includes)) array_push($includes, str_replace("*", ".*", preg_replace("/({|}|\||\.)/", '\\\$1', $argv[$i]))); } if (isset($includes)) $includes = "{^" . join("|", $includes) . "$}"; // check for excludes for ($i = 3; $i < $argc; $i++) { if ($argv[$i] == "-x") $excludes = array(); else if ($argv[$i][0] == "-" && isset($excludes)) break; else if (isset($excludes)) array_push($excludes, str_replace("*", ".*", preg_replace("/({|}|\||\.)/", '\\\$1', $argv[$i]))); } if (isset($excludes)) $excludes = "{^" . join("|", $excludes) . "$}"; // check STDIN else command line for inputs if ($argv[2] == "--") { echo "Taking inputs from STDIN, one per line... Hit Ctrl-D when done else Ctrl-C to cancel.\n"; $patterns = file("php://stdin", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); } else { $patterns = array(); for ($i = 2; $i < $argc; $i++) { if ($argv[$i][0] == "-") break; array_push($patterns, $argv[$i]); } } // glob patterns lest shell (e.g., Windows) not have done so $inputs = array(); foreach ($patterns as $pattern) $inputs = array_merge($inputs, glob($pattern, GLOB_BRACE)); // files to render $files = array(); // directories into which to descend $directories = array(); // parse command line for files and directories foreach ($inputs as $input) { // ensure file (or directory) exists if (!file_exists($input)) die("File does not exist: {$input}\n"); // ensure file (or directory) is readable if (!is_readable($input)) die("File cannot be read: {$input}\n"); // push file or directory onto array if (is_dir($input)) array_push($directories, $input); else array_push($files, $input); } // descend into directories while (count($directories) > 0) { $directory = array_shift($directories); foreach (scandir($directory) as $child) { // ignore . and .. if ($child == "." || $child == "..") continue; // prepare path $path = rtrim($directory, "/") . DIRECTORY_SEPARATOR . $child; // push path onto array if (is_dir($path)) array_push($directories, $path); else array_push($files, $path); } } // create PDF $mpdf = new _mPDF("c", "Letter-L", 8); $mpdf->simpleTables = true; // create highlighter $geshi = new GeSHi(); $geshi->enable_keyword_links(false); $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); $geshi->set_header_type(GESHI_HEADER_DIV); $geshi->set_tab_width(4); // prepare to count pages $pages = 0; // iterate over files foreach ($files as $file) { // skip implicit exclusions if (isset($includes) && !preg_match($includes, $file)) continue; // skip explicit exclusions if (isset($excludes) && preg_match($excludes, $file)) continue; // skip dotfiles if (substr(basename($file), 0, 1) == ".") continue; // infer language $language = $geshi->get_language_name_from_extension(pathinfo($file, PATHINFO_EXTENSION)); // ignore unknown languages if (!$language) { // whitelist Makefile, README, and *.txt if (!preg_match("/^(?:Makefile|README|.*\.txt)$/i", pathinfo($file, PATHINFO_FILENAME))) continue; } // load file echo "Rendering $file...\n"; $geshi->set_source(utf8_encode(file_get_contents($file))); $geshi->set_language($language, true); // set header $properties = array( "border-bottom: 1px solid #808080", "color: #808080", "font-family: monospace", "text-align: right" ); $mpdf->SetHTMLHeader("