You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ascore/ascore_pipelines.php

369 lines
14 KiB
PHP

<?php
#ini_set('display_errors','1'); error_reporting(E_ALL ^ (E_NOTICE | E_WARNING));
if (!defined("_ECRIRE_INC_VERSION")) return;
#---------------------------------------------------#
# Plugin : AScore #
# Auteur : Patrice Vanneufville, 2021 #
# Contact : patrice¡.!vanneufville¡@!laposte¡.!net #
# Licence : GPL #
#--------------------------------------------------------------------------#
# Documentation : https://contrib.spip.net/ #
#--------------------------------------------------------------------------#
defined('_VEROVIO_SCRIPT') || define('_VEROVIO_SCRIPT',
'<script src="https://www.verovio.org/javascript/latest/verovio-toolkit.js" type="text/javascript"></script>'
// '<script src="https://www.verovio.org/javascript/latest/verovio-toolkit-hum.js" type="text/javascript"></script>'
// '<script src="https://www.verovio.org/javascript/develop/verovio-toolkit-light.js" type="text/javascript"></script>'
// '<script src="' . find_in_path('javascript/verovio-toolkit-hum.js') . '" type="text/javascript" id="verovio"></script>'
. '<script src="' . find_in_path('javascript/svg2bitmap.js') . '" type="text/javascript" id="svg2bitmap"></script>');
defined('_VEROVIO_CSS') || define('_VEROVIO_CSS', '<style>
.ascore_msg {
color:darkred;
font-weight:bold;
}
.ascore_download {
margin-top: 0.8em;
}
pre[rel]:not([rel=""]):before {
font-family: Gotham Narrow SSm A,Gotham Narrow SSm B,Rubik,Lato,Lucida Grande,Lucida Sans Unicode,Tahoma,Sans-Serif;
font-style: normal;
font-weight: 800;
font-size: .6rem;
content: attr(rel);
color: #fff;
position: absolute;
top: 0.1rem;
right: 0.4rem;
padding: 0;
color: #3c78a7!important;
}
pre.ascore_code {
width: 98%;
padding: 0.8em;
position:relative;
text-align: left;
line-height: normal;
font-size: .7rem;
max-height: 250px;
overflow: auto;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
}
pre.ascore_code code {
background: transparent;
}
.ascore_center {
text-align: center;
}
.ascore_right {
text-align: right;
}
.ascore_left {
text-align: left;
}
</style>');
// Format JSON : g.pgHead tspan.text
defined('_VEROVIO_CSS_TITRE') || define('_VEROVIO_CSS_TITRE', '"font-weight": "bold", "font-family": "sans-serif"');
// fonction pre-traitement, pipeline pre_propre
function ascore_pre_typo($texte) {
if(!strlen($texte)) return '';
$listeBalises = array('abc', 'pae');
// ne sachant pas quelle balise est la premiere...
$texte = str_replace(
array(_SCORE_ABC_DEBUT, _SCORE_ABC_FIN, _SCORE_PAE_DEBUT, _SCORE_PAE_FIN),
array(_SCORE_DEBUT."(abc)", _SCORE_FIN, _SCORE_DEBUT."(pae)", _SCORE_FIN),
$texte
);
// puis recherche des balises
if(strpos($texte, _SCORE_DEBUT)===false || strpos($texte, _SCORE_FIN)===false)
return $texte;
// isolation du code de la partition
list($texteAvant, $suite) = explode(_SCORE_DEBUT, $texte, 2);
list($texte, $texteApres) = explode(_SCORE_FIN, $suite, 2);
$optionsJS = $xOptionsJS = array();
$format = $options = $msg = '';
// autre balise que _SCORE_DEBUT ?
foreach($listeBalises as $b)
if(strncmp($texte, "($b)", $l = strlen($b)+2) === 0) {
$format = $b;
$texte = substr($texte, $l);
}
// le xml est-il protégé par <html/>?
if(strncmp($texte, '<span class="base64"', 20)===0 || strncmp($texte, '<div class="base64"', 19)===0)
$texte = echappe_retour($texte);
// grrrr si SPIP a tout pete
if(strpos($texte, 'base64')!==false || strpos($texte, '<img ')!==false) {
$msg = "\n_ ".htmlentities(($format?"<$format>":_SCORE_DEBUT).'<html>'._T('ascore:vos_donnees').'</html>'.($format?"<$format>":_SCORE_FIN));
$msg = _T('ascore:donnees_alterees', array('syntaxe' => $msg));
}
// des options de configuration ?
if(strpos($texte, '[data]')!==false) {
list($options, $texte) = explode('[data]', $texte, 2);
$options = ascore_parse_options($options);
foreach ($options as $opt) if($opt[2]) {
$val = ascore_check_boolean($opt[3]);
$key = ascore_aliases($opt[2]);
if(substr($key, 0, 1)==='x') $xOptionsJS[$key] = $val;
else $optionsJS[$key] = $val;
}
}
$texte = trim($texte);
if(!isset($xOptionsJS['xTitreCSS']))
$xOptionsJS['xTitreCSS'] = _VEROVIO_CSS_TITRE;
$mini = isset($xOptionsJS['xMini']) && $xOptionsJS['xMini'];
// syntaxe simplifiee : mini = 35 500 (zoom largeur)
if($mini && preg_match(',(\d+)\s+(\d+),', $xOptionsJS['xMini'], $regs)) {
$xOptionsJS['xMini'] = $regs[1];
if(!isset($xOptionsJS['xLargeur'])) $xOptionsJS['xLargeur'] = $regs[2];
}
// syntaxe simplifiee : inline = 35 500 1em (zoom largeur declageY)
$inline = isset($xOptionsJS['xInline']) && $xOptionsJS['xInline'];
if($inline && preg_match(',(\d+)\s+(\d+)\s*([^\s]*),', $xOptionsJS['xInline'], $regs)) {
$xOptionsJS['xMini'] = $regs[1];
if(!isset($xOptionsJS['xLargeur'])) $xOptionsJS['xLargeur'] = $regs[2];
$xOptionsJS['xStyle'] = 'display:inline-block; ';
if($regs[3]) $xOptionsJS['xStyle'] .= "transform: translateY($regs[3]); ";
$xOptionsJS['xBalise'] = 'span';
}
if( (isset($xOptionsJS[$zoom='xZoom']) && $xOptionsJS['xZoom'])
|| (isset($OptionsJS[$zoom='scale']) && $OptionsJS['scale']) ) {
if($mini)
$msg = ($msg?"<br/>":'') . typo(_T('ascore:ignore_zoom', array('oups'=>'mini', 'zoom' => ascore_aliases($zoom, true))));
elseif($inline)
$msg = ($msg?"<br/>":'') . typo(_T('ascore:ignore_zoom', array('oups'=>'inline', 'zoom' => ascore_aliases($zoom, true))));
else
$optionsJS['scale'] = $xOptionsJS['xZoom'];
}
if(isset($xOptionsJS['xMarges']) && preg_match(',(\d+)(?:[^\d]+(\d+))?(?:[^\d]+(\d+))?(?:[^\d]+(\d+))?,', $xOptionsJS['xMarges'], $regs)) {
// syntaxe similaire au margin CSS
switch(count($regs)) {
case 2: $regs[2] = $regs[3] = $regs[4] = $regs[1]; break;
case 3: $regs[3] = $regs[1]; $regs[4] = $regs[2]; break;
case 4: $regs[4] = $regs[2]; break;
}
list(, $optionsJS['pageMarginTop'], $optionsJS['pageMarginRight'], $optionsJS['pageMarginBottom'], $optionsJS['pageMarginLeft']) = $regs;
}
if($format == 'abc') {
$optionsJS['inputFrom'] = 'abc';
$texte = ascore_corrige_abc($texte, $xOptionsJS, $mini);
} elseif($format == 'pae') {
$optionsJS['inputFrom'] = 'pae';
$texte = ascore_corrige_pae($texte, $xOptionsJS, $mini);
}
$balise = isset($xOptionsJS['xBalise']) ? $xOptionsJS['xBalise'] : 'div';
$data = $texte ? base64_encode(unicode2charset(charset2unicode($texte),'iso-8859-1')) : '';
$optionsJS = json_encode($optionsJS, JSON_FORCE_OBJECT + JSON_NUMERIC_CHECK);
$xOptionsJS = json_encode($xOptionsJS, JSON_FORCE_OBJECT + JSON_NUMERIC_CHECK);
$source = hash('sha256', $data . $optionsJS . $xOptionsJS);
// recherche du cache
$dir = sous_repertoire(_DIR_CACHE, 'ascore');
$recalcul = in_array(_request('var_mode'), array(/*'calcul',*/ 'recalcul', 'preview'));
if(!$recalcul && lire_fichier($dir . $source . '.html', $c)) {
// utilisation du cache puisqu'il existe
$c = unicode2charset(charset2unicode($c,'iso-8859-1'));
$score = code_echappement($c, '', true);
} else {
// recuperation du fond 'modele/score.html'
$score = trim(recuperer_fond('modeles/score', array(
'balise' => $balise,
'data' => $data,
'source' => $source,
'id_score' => 0,
'msg' => $msg,
'options' => $optionsJS,
'x_options' => $xOptionsJS,
)));
}
if($inline)
$score = code_echappement($score, '', true, 'span');
return $texteAvant . $score . ascore_pre_typo($texteApres, true);
}
// fonction qui retire les commentaires simples (un seul "%")
// les pseudos-commentaires (ligne debutant par "%%") sont preserves
// le resultat est passe par trim()
function acsore_scan_comments_abc($ligne) {
$i = strpos($ligne, '%');
if($i === false) return $ligne;
// protection pseudos-commentaire
if(strncmp($ligne = trim($ligne), '%%', 2) === 0) return $ligne . ' %';
// suppression du commentaire
$ligne = substr($ligne, 0, $i);
return trim($ligne);
}
// fonction qui ordonne et complete correctement les entetes
// rappel section d'entete ; "X:" en premier, "T:" en second, "K:" en dernier
// $mini est un mode d'affichage simplifie
function ascore_corrige_abc($texte, &$xOptionsJS, $mini = false) {
if(!($texte = trim($texte))) return '';
$texte = preg_split('/[\r\n]+/', $texte);
$x = 0; $t = 100 ; $e = 200; $k = 800; $z = 900;
$chiffrage = $tonalite = '';
$out = array(); $entete = true;
foreach($texte as $i=>$ligne) {
// syntaxe servant a decaler horizontalement des annotations placees au-dessus de la portee
// Exemple "-2.9%|"zAB
// Ici on protege le "%" qui est une marque de commentaire en format ABC
if(strpos($ligne, '"') !== false)
$ligne = preg_replace(',"(\-?[\d\.]+)%([^"]+)"z,', '"$1pc$2"z', $ligne);
// suppression des commentaires
$ligne = acsore_scan_comments_abc($ligne);
// est-ce une ligne d'entete ?
$entete = $entete && preg_match(',^([A-Za-z]):,', $ligne, $regs);
if($entete) {
if($regs[1]=='X') $out[$x++] = $ligne; // section
elseif($regs[1]=='T') $out[$t++] = $ligne; // titre
elseif($regs[1]=='K') $out[$k++] = $tonalite // clef & tonalite
= str_replace(array('rythme', 'rhythm', 'percussion'), 'perc stafflines=1', $ligne);
elseif($regs[1]=='M') $out[$e++] = $chiffrage = $ligne; // chiffrage
else $out[$e++] = $ligne;
}
else $out[$z++] = $ligne;
}
$nb0 = count($out);
// insertion des entetes necessaires
if(!$x) $out[0] = 'X: 1';
if($t == 100) $out[$t] = 'T:';
if($k == 800) $out[$k] = $mini ? 'K: C' : 'K:';
//if($z == 900) $out[$z] = 'x';
ksort($out);
if($nb0==1 && strlen($tonalite))
// raccourci pour une cle et/ou une armure seule "K: bla bla"
$out = "X: 1\nT:\n$tonalite\nx";
elseif($nb0==1 && strlen($chiffrage))
// raccourci pour un chiffrage seul "M: bla bla"
$out = "X: 1\nT:\n$chiffrage\nK: perc stafflines=0\nx";
elseif($nb0==1 && isset($out[900]) && preg_match(',^\d+/\d+$,', $chiffrage=$out[900]))
// raccourci pour un chiffrage seul type "12/8"
$out = "X: 1\nT:\nM: $chiffrage\nK: perc stafflines=0\nx";
else
$out = trim(implode("\n", $out));
// barre de mesure si absente, car le toolkit n'aime pas ça pour l'instant...
if(/*$mini && */substr($out, -1)!=='|' && substr($out, -2)!=='|]') {
$out .= '|';
$xOptionsJS['xClass'] = (isset($xOptionsJS['xClass']) && $xOptionsJS['xClass'])
? $xOptionsJS['xClass'] . ' sansBarreDeFin' : 'sansBarreDeFin';
}
return $out;
}
// fonction qui complete correctement les entetes
function ascore_corrige_pae($texte, &$xOptionsJS, $mini = false) {
if(!($texte = trim($texte))) return '';
if(strpos($texte, '@data:')===false)
$texte = '@data:' . $texte;
return $texte;
}
/*
// pipeline ramasse-miettes permet de recuperer le HTML propre *après* la reinsertion des modeles
// les scripts de partitions (base ou inline) sont empeches en partie privee, il a fallu les echapper.
// textwheel >= 1.3.5
function ascore_post_echappe_html_propre($texte) {
return echappe_retour($texte, 'VEROVIO_SCRIPTS');
}
*/
// pour inserer un js
function ascore_javascript($b) {
$f = find_in_path("javascript/$b.js");
$out = array();
foreach(array('option_inconnue', 'doc_introuvable', 'log', 'inline_introuvable' ) as $i => $l)
$out[] = $i . ': "' . addslashes(typo(_T('ascore:erreur_'.$l))) . '"';
return "<script type=\"text/javascript\">\nconst ascore_errors = {\n\t"
. implode(",\n\t", $out)
. "\n};\n</script>"
. ($f ? '<script type="text/javascript" src="'.$f."\"></script>\n" : '');
}
// pipeline header_prive
function ascore_header_prive($flux) {
$flux .= _VEROVIO_CSS . _VEROVIO_SCRIPT . ascore_javascript('verovio');
return $flux;
}
// pipeline insert_head
function ascore_insert_head($flux) {
if(_request('var_mode'))
return ascore_header_prive($flux);
if(!defined('_VEROVIO_HEAD'))
include_spip('ascore_fonctions');
return $flux . _VEROVIO_CSS . _VEROVIO_HEAD;
}
// Ajouter le modele score a la liste des modeles de documents
function ascore_declarer_tables_objets_sql($tables) {
$tables['spip_documents']['modeles'][] = 'score';
return $tables;
}
// Le pipeline affichage_final, execute a chaque hit sur toute la page
// Incorporation du js dans <head> a la place de _VEROVIO_HEAD, si un score
// est present dans la page
function ascore_affichage_final($flux) {
if(!defined('_VEROVIO_HEAD')) include_spip('ascore_fonctions');
return str_replace(_VEROVIO_HEAD,
strpos($flux, 'modele_ascore')!==false ? _VEROVIO_SCRIPT . ascore_javascript('verovio') : '',
$flux);
}
/*
// Afficher la configuration trouvee
function ascore_affiche_milieu($flux){
if ($flux['args']['exec'] == 'article') {
$flux['data'].= ascore_configuration_inline($flux['args']['id_article']);
}
return $flux;
}*/
// decoder une ligne de config (retrait des comentaires facon PHP et mise en forme du parametre)
// retour : array(param, valeur, param original, ligne epuree)
// code issu du plugin Jeux
function ascore_parse_ligne_options($ligne) {
$ligne = preg_replace(',\/\*(.*?)\*\/,', '', $ligne);
$ligne = trim(preg_replace(',\/\/(.*)$,', '', $ligne));
$arr = strpos($ligne, '=')!==false ? explode('=', $ligne, 2) : explode(':', $ligne, 2);
if(count($arr) != 2) return array(false, false, false, strlen($ligne) ? '/* ' . $ligne . '*/' : false);
$arr[0] = trim($arr[0]); $arr[1] = trim($arr[1]);
return array($arr[0], $arr[1], $arr[0], $arr[0] . ': ' . $arr[1]);
}
// decoder une section d'optionis
// retourne le tableau des parametres : array[] = array($ligne epuree, $param_original, $param, $valeur, $ligne_originale)
// code issu du plugin Jeux
function ascore_parse_options($texte_config) {
$arr_config = array();
// s'il existe un commentaire on abandonne le séparateur "," : chaque option doit être sur une ligne differente
$regexp = strpos($texte_config, '/')!==false ? '/[\r\n]+/' : '/[\r\n]+|,/';
$texte_config = preg_split($regexp, trim($texte_config));
foreach ($texte_config as $ligne) {
// array(param, valeur, param original, ligne epuree)
$arr = ascore_parse_ligne_options($ligne);
if(strlen($arr[3])) {
if($arr[0])
$arr_config[] = array($arr[3], $arr[2], $arr[0], $arr[1], $ligne);
else
$arr_config[] = array($arr[3], false, false, false, $ligne);
}
}
return $arr_config;
}
function ascore_check_boolean($value) {
if(in_array($value, array('true', 'vrai', 'oui', 'yes', 'on', /*'1', */'si', 'ja', strtolower(_T('item_oui')))))
return true;
if(in_array($value, array('false', 'faux', 'non', 'no', 'off', /*'0', */'nein', strtolower(_T('item_non')))))
return false;
return $value;
}
?>