*/ if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); class syntax_plugin_abc extends DokuWiki_Syntax_Plugin { function getType(){ return 'protected'; } function getPType(){ return 'block'; } function getSort(){ return 192; } function connectTo($mode) { $this->Lexer->addEntryPattern('Lexer->addExitPattern('','plugin_abc'); } function handle($match, $state, $pos, Doku_Handler $handler){ if ( $state == DOKU_LEXER_UNMATCHED ) { $matches = preg_split('/>/u',$match,2); $matches[0] = trim($matches[0]); return array($matches[1],$matches[0]); } return true; } /** * Create output */ function render($mode, Doku_Renderer $renderer, $data) { global $INFO; global $ACT; global $conf; if($mode == 'xhtml' && strlen($data[0]) > 1){ $src = $data[0]; $origSrc = $src; $trans = "0 ".$data[1]; // "0" includes the original key $debug = $conf['allowdebug']; $error = $this->_checkExecs(); if($this->getConf('abcok') && (!$INFO['rev'] || ($INFO['rev'] && ($ACT=='preview'))) && !$error){ //do not create/show files if an old revision is viewed, but always if the page is previewed and never when there is an error $entitiesFile = dirname(__FILE__).'/conf/entities.conf'; if (@file_exists($entitiesFile)) { $entities = confToHash($entitiesFile); $src = strtr($src,$entities); } $fileBase = $this->_getFileBase($origSrc); $srcFile = $fileBase.'.abc'; $srcChanged = (!file_exists($srcFile) || (file_exists($srcFile) && $src!=io_readFile($srcFile))); if ($srcChanged) io_saveFile($srcFile, $src); if ($this->getConf('abc2abc') && is_executable($this->getConf('abc2abc'))) { $transSrc = $this->_getTransSrc($trans); $transNew = $this->_getTransNew($fileBase, $transSrc); } else { $transSrc = array(0); $transNew = array(); } $renderList = $srcChanged ? $transSrc : $transNew; if($debug || $_REQUEST['purge']) $renderList = $transSrc; // create files foreach ($renderList as $transMode) { // if no transposition is allowed and the tune shall be transposed // by 0 semitones (= not at all), then nothing is appended to the fileBase; // else append the amount of semitiones to the fileBase $curFileBase = ($transMode==0) ? $fileBase : $fileBase."_".$transMode; $abcFile = $curFileBase.'.abc'; io_saveFile($abcFile, $src); ob_start(); if ($transMode!=0) { $this->_transpose($abcFile, $srcFile, $transMode); } $debugLog = $this->_createImgFile($abcFile, $curFileBase); if ($this->getConf('displayType')==1 || $this->getConf('displayType')==2) { $this->_createMidiFile($abcFile, $curFileBase); } if ($this->getConf('displayType')==2) { $this->_createPsFile($abcFile, $curFileBase); if ($this->getConf('ps2pdf')) { $this->_createPdfFile($abcFile, $curFileBase); } } $errorLog = ob_get_contents(); ob_end_clean(); } if (($this->getConf('displayErrorlog') || $debug) && $errorLog) { $errorLog = str_replace($this->_getAbc2psVersion(), "abc2ps", $errorLog); //hide abc2ps version for security reasons //TODO: hide lines starting with "writing MIDI file", "File", "Output written on", ... for boring reasons msg(nl2br($errorLog), 2); } if ($debugLog) { msg($debugLog); } // display files foreach ($transSrc as $transMode) { $curFileBase = ($transMode==0) ? $fileBase : $fileBase."_".$transMode; $renderer->doc .= $this->_showFiles($curFileBase); } // always have the abc source in the html source (for search engine optimization) // only per css visible when displaySource = 1 if ($this->getConf('displaySource')) $visible = " visible"; $renderer->doc .= '
'.NL; $renderer->doc .= $renderer->file($origSrc); $renderer->doc .= '
'.NL; } else { if ($error && $this->getConf('abcok')) { msg($error, -1); } $renderer->doc .= $renderer->file($origSrc); } return true; } return false; } /** * check if all needed programs are set, existent and executable */ function _checkExecs() { global $conf; $error .= $this->_checkExec($this->getConf('abc2ps'), 'abc2ps'); $error .= $this->_checkExec($conf['im_convert'], 'im_convert'); if (($this->getConf('displayType')==1) || ($this->getConf('displayType')==2)) { $tmpError1 = $this->_checkExec($this->getConf('abc2midi'), 'abc2midi'); if ($tmpError1) $error .= $tmpError1."If you do not wish to install it, you can change the 'displayType' to '0' ('image only').
"; } if($this->getConf('ps2pdf') && ($this->getConf('displayType')==2)) { $tmpError2 = $this->_checkExec($this->getConf('ps2pdf'), 'ps2pdf'); if ($tmpError2) $error .= $tmpError2."If you do not wish to install it, you can leave it blank and a ps file will be generated instead.
"; } return $error; } /** * check if a program is set, existent and executable */ function _checkExec($execFile, $execName) { if (!$execFile) { $error .= "The '".$execName."' config option is not set.
"; } else if (!file_exists($execFile)) { $error .= "'".$execFile."' (".$execName.") is not existent.
"; } else if (!is_executable($execFile)) { $error .= "'".$execFile."' (".$execName.") is not executable.
"; } return $error; } /** * get to-be directory and filename (without extension) * * all files are stored in the media directory into 'plugin_abc//' * and the filename is a mixture of abc-id and abc-title (e.g. 42_the_title.abc|...) * */ function _getFileBase($src) { global $ID; global $ACT; global $conf; $mediadir = $conf['mediadir']; // where to store the abc media files $abcdir = $this->getConf('mediaNS') ? $mediadir.'/'.$this->getConf('mediaNS') : $mediadir; io_makeFileDir($abcdir); $fileDir = $abcdir.'/'.utf8_encodeFN(str_replace(':','/',getNS($ID))); // the abcID is what comes after the 'X:' preg_match("/\s?X\s?:(.*?)\n/s", $src, $matchesX); $abcID = preg_replace('/\s?X\s?:/', '', $matchesX[0]); // the abcTitle is what comes after the (first) 'T:' preg_match("/\s?T\s?:(.*?)\n/s", $src, $matchesT); $abcTitle = preg_replace('/\s?T\s?:/', '', $matchesT[0]); $fileName = cleanID($abcID."_".$abcTitle); // no double slash when in root namespace $slashStr = (getNS($ID)) ? "/" : ""; // have different fileBase for previewing $previewPrefix = ($ACT!='preview') ? "" : "x"; $fileBase = $fileDir.$slashStr.$previewPrefix.$fileName; // unfortunately abcm2ps seems not to be able to handle // file names (realpath) of more than 120 characters $realFileBaseLen = (strlen(fullpath($abcdir)) - strlen($abcdir)) + strlen($fileBase); $char_len = 114; if ($realFileBaseLen >= $char_len) { $truncLen = strlen($fileBase) + ($char_len - $realFileBaseLen); $fileBase = substr($fileBase, 0, $truncLen); } return $fileBase; } /** * get transposition parameters from the source into a reasonable array */ function _getTransSrc($trans) { $transSrc = explode(" ", $trans); // the semitones to transpose have to be integers $transSrc = array_map("intval", $transSrc); // do not transpose by the same amount of semitones more than once $transSrc = array_unique($transSrc); // do not transpose higher or lower than 24 semitones $transSrc = array_filter($transSrc, create_function('$t', 'return($t<24 && $t >-24);')); // do not allow transposition into more than 8 keys array_splice($transSrc, 8); return $transSrc; } /** * get all new and old trans params * return the new params, delete the corresponding old files */ function _getTransNew($fileBase, $transSrc) { // get all abc files belonging to the fileBase $filesArrABC = glob(dirname($fileBase)."/{".basename($fileBase)."*.abc}", GLOB_BRACE); $transFS = array(0); // always include the original key // get all numbers after the '_' and before the '.abc' foreach ($filesArrABC as $f) { $f = basename($f, ".abc"); $tr = substr(strrchr($f,'_'),1); if (intval($tr)) $transFS[] = $tr; } $transNew = array_diff($transSrc, $transFS); $transOld = array_diff($transFS, $transSrc); // delete old transposed files foreach ($transOld as $o) { $filesArrAll = glob(dirname($fileBase)."/{".basename($fileBase)."_".$o."*}", GLOB_BRACE); foreach ($filesArrAll as $d) { unlink($d); } } return $transNew; } /** * transpose and create transposed abc */ function _transpose($abcFile, $srcFile, $trans) { passthru(fullpath($this->getConf('abc2abc'))." $srcFile -e -t $trans > $abcFile"); } /** * create img file */ function _createImgFile($abcFile, $fileBase) { global $conf; $epsFile = $fileBase.'001.eps'; $imgFile = $fileBase.'.png'; $debug = $conf['allowdebug']; $debugOutput = ''; // create eps file $epsCommand = fullpath($this->getConf('abc2ps'))." $abcFile ".$this->getConf('params4img')." -E -O $fileBase."; passthru($epsCommand." 2>&1"); if($debug) { $debugOutput .= "

Debug Info for $abcFile

"; $debugOutput .= "

EPS file '".$epsFile."'"; $debugOutput .= file_exists($epsFile) ? " exists" : " does not exist"; $debugOutput .= ", command used to create it:

"; $debugOutput .= "
".$epsCommand."
"; } // convert eps to png file $pngCommand = fullpath($conf['im_convert'])." $epsFile $imgFile"; passthru($pngCommand); if($debug) { $debugOutput .= "

PNG file '".$imgFile."'"; $debugOutput .= file_exists($imgFile) ? " exists" : " does not exist"; $debugOutput .= ", command used to create it:

"; $debugOutput .= "
".$pngCommand."
"; } else { if(file_exists($epsFile)) unlink($epsFile); } return $debugOutput; } /** * create ps file */ function _createPsFile($abcFile, $fileBase) { $psFile = $fileBase.'.ps'; $fmt = $this->getConf('fmt'); $addFmt = ($fmt && file_exists($fmt)) ? " -F ".fullpath($fmt) : ""; passthru(fullpath($this->getConf('abc2ps'))." $abcFile $addFmt ".$this->getConf('params4ps')." -O $psFile 2>&1"); } /** * create pdf file */ function _createPdfFile($abcFile, $fileBase) { $psFile = $fileBase.'.ps'; $pdfFile = $fileBase.'.pdf'; passthru(fullpath($this->getConf('ps2pdf'))." $psFile $pdfFile"); if(file_exists($psFile)) unlink($psFile); } /** * create midi file */ function _createMidiFile($abcFile, $fileBase) { $midFile = $fileBase.'.mid'; passthru(fullpath($this->getConf('abc2midi'))." $abcFile -o $midFile"); } /** * get abc2ps version */ function _getAbc2psVersion() { ob_start(); passthru(fullpath($this->getConf('abc2ps'))." -V 2>&1"); $version = ob_get_contents(); $version = explode("\n",$version); ob_end_clean(); return $version[0]; } /** * get file and check if it exists */ function _getFile($fileBase, $ext) { $file = $fileBase.$ext; return (file_exists($file)) ? $file : 0; } /** * get ID that has to be called from fetch.php */ function _getFileID($file) { global $ID; return (getNS($ID)) ? getNS($ID).":".basename($file) : basename($file); } /** * html for internal media */ function _showFile($file) { if (!$file) { return "Error: The file could not be generated."; } $mediaNS = $this->getConf('mediaNS').":"; $name = $this->_getFileID($file); $id = $mediaNS.$name; $url = ml($id, array('t' => time())); // add timestamp for cache busting list($ext, $mime) = mimetype($file, false); if(substr($mime, 0, 5) == 'image') { $imgSize = getimagesize($file); $imgSize = $imgSize[3]; return ''; } else { $class = 'mediafile mf_'.preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); return ''.$name.''; } } /** * html for displaying all files; dependant on displayType */ function _showFiles($fileBase) { $imgFile = $this->_getFile($fileBase, '.png'); $midFile = $this->_getFile($fileBase, '.mid'); $abcFile = $this->_getFile($fileBase, '.abc'); $psFile = $this->_getFile($fileBase, '.ps'); $pdfFile = $this->_getFile($fileBase, '.pdf'); $mediaNS = $this->getConf('mediaNS').":"; $showImg = $this->_showFile($imgFile); switch ($this->getConf('displayType')) { // image only (case 0 and default) default: case 0: $display = '

'.$showImg.'

'.NL; break; // image linked to midi case 1: $display = $showImg; if($midFile) { $display = ''.$display.''; } $display .= '

'.$display.'

'.NL; break; // image with list of abc, midi, ps/pdf case 2: $display = ''.NL; $display .= '

'.$showImg.'

'.NL; break; } $display = '
'.NL.$display.'
'.NL; return $display; } }