Eigene Erweiterung entwerfen/OraSVG

Zur Navigation springen Zur Suche springen
<?php
  /**
   * MediaWiki extension: OraSVG
   * =============================
   *
   * To activate, edit your LocalSettings.php,
   * save this code as 'OraSVG.php' and copy the file into the subdirectory OraSVG of your extensions-directory and add
   * require_once("$IP/extensions/InlineSVG/OraSVG.php");
   * Verwendung: <orasvg></orasvg>
   */

/*/  # User Settings

  # Parameter

  # default   ...  derzeit keine

  # End User Settings
/*/
  if ( !defined( 'MEDIAWIKI' ) ) {
    die( "This is not a valid entry point.\n" );
  }

  $wgExtensionCredits['parserhooks'][] = array(
    'path' => __FILE__,
    'name' => 'OraSVG',
    'version' => '0.9',
    'author'  =>$extension_AUTH,
    'url' => "$wgServer$wgScriptPath/index.php/Extension:OraSVG",
    'description' => 'stellt SVGs dar'
  );
  $wgExtensionFunctions[] = 'wfOraSVGExtension';

  function wfOraSVGExtension() {
      new OraSVG();
  }

class OraSVG
  {
      public function __construct()
     {
          global $wgParser;
          $wgParser->setHook('orasvg', array(&$this, 'wfOraSVG'));
     }
      public function wfOraSVG($input, $argv, $parser)
     {
       global $wgUploadDirectory, $wgUploadPath, $IP, $wgArticlePath, $wgTmpDirectory, $wgParser;
       global $oraDBUser, $oraDBPass, $oraDBDatabase;
       global $wgServer, $wgScriptPath, $wgUploadPath;
       $dbID = 'Standard-Oracle-Instanz';
      //  Aktionen und Definitionen
      $style = '
<style type="text/css"><![CDATA[
.numbers { fill: green; font-size: 12px; }
.smallnumbers { fill: green; font-size: 10px; }
.label { fill: blue; font-size: 15px; }
.linethick { fill: none; stroke:#6c0; stroke-width: 1; }
.linethin  { fill: none; stroke: #6c0; stroke-width: 0.5; }
.bar { stroke: #036; fill: none; opacity: 1; stroke-width: 0; }
.barg { fill: #6c0; }
.pfad { fill: none; stroke-width: 2; }
]]>
</style>';

      $text_a = '
<g transform="translate(' ;
      $text_b = ') rotate(60)"><text class="label" >' ;

      if (empty($input))
      {
        $page = "SELECT LEVEL X, TRUNC (DBMS_RANDOM.VALUE (1, 1000)) A
,TO_CHAR (ADD_MONTHS (TRUNC (SYSDATE, 'MM'), 1 - LEVEL), 'fmDay, fmDD. fmMonth YYYY', 'NLS_DATE_LANGUAGE=german') TAG
FROM DUAL CONNECT BY LEVEL < 20   ORDER BY 1";
      }
      else $page = str_replace('"', "\"", $input);

      // Zugangsdaten für die Oracle-Datenbank
      // es können verschiedene UserIDs bzw. Datenbankserver in der LocalSettings.php festgelegt werden

      $db_server   = $oraDBDatabase[$dbID];
      $db_username = $oraDBUser[$dbID];
      $db_password = $oraDBPass[$dbID];
      
      // öffnen der Verbindung
      $conn = oci_connect($db_username, $db_password, $db_server, 'AL32UTF8');
      if (!$conn)
      {
        // Verbindung hat nicht geklappt, Rückgabe einer Fehlermeldung
        $m = oci_error();
        return $m['message'] . "\n";
      }
      
      $code = empty($argv["mitcode"]) ? '' : $wgParser->recursiveTagParse("{{hili|lang=sql|code=" . $page . "}}");

      $tablinks = '';
      $stid = oci_parse($conn, $page);
      $result = oci_execute($stid);

      if (!$result)
        return "SQL Fehler : " . $wgParser->recursiveTagParse("{{hili|lang=sql|code=" . $page . "}}");

      $ncols = oci_num_fields($stid);  # Anzahl der Spalten
      $nrows = oci_num_rows ($stid);  # Anzahl der Zeilen
      $y_wert = $label = [];
      while ($row = oci_fetch_row($stid))
      {
        $y_wert[] =$row[1];   # Spalte 2 enthält den Produktionswert
        $label[]  =$row[2];   # Spalte 3 enthält die Beschriftung
      }

      if (empty($y_wert)) return "Im angegebenen Zeitraum sind keine Mengen vorhanden</p>" . $code;
      $max_y = max($y_wert);
      $min_y = min($y_wert);
      if ($min_y > 0) $min_y = 0; # wir fangen nicht über der Nullinie an

      $pfaddaten = [];

      if ($max_y == 0 and $min_y == 0)
      {
        return "Im angegebenen Zeitraum sind keine Mengen vorhanden</p>" . $code;
      }
      $punkte = count($y_wert);
      $faktor = 1200/$punkte;
      #$rh = round($max_y,-round(log10($max_y))) ;

      $hoch = empty($argv["hoch"]) ? 600 : intval($argv["hoch"]);
      $yfaktor = $hoch/($max_y - $min_y); # der Skalierungsfaktor für die y-Koordinaten, damit das Ausgaberechteck genau 600 hoch ist
      $rb = 1200;
      $ry = 0; # Startpunkt des Rechtecks mit den Querlinien

      for ($i=0; $i<$punkte; $i++)
      {
        $pfaddaten[$i] = $y_wert[$i] * $yfaktor;
      }
      #return implode(':', $pfaddaten);

      $farbe = '#036';
      if (!empty($argv["farbe"])) $farbe=$argv["farbe"];
      $pfad = '
<path d="' . $this->toPfad($pfaddaten, $faktor) . '"
class="pfad" stroke="' . $farbe . '" />';

      $rect = '
<rect width="' . $faktor . '" height="';
      $rectg = '
<rect class="barg" width="' . $faktor . '" height="';
      $labels = $bars = '';

      $delta = $faktor/5;
      $lablen = 0;
      #return $punkte;
      for ($i=0; $i<$punkte; $i++)
      {
        $x = $i * $faktor; $xx = $x + $delta;
        $y = $yfaktor * ($max_y - $y_wert[$i] - $min_y);
        $yy = $hoch;
        $ywert = $pfaddaten[$i];

        if (!empty($label[$i]))
        {
          $labels .= $text_a . $xx . ',' . $yy . $text_b . $label[$i] . '</text></g>';
          $bars .= $rectg . $ywert . '" x="' . $x . '" y="0"></rect>';
          if (strlen($label[$i]) > $lablen) $lablen=strlen($label[$i]);
        }
        else
        $bars .= $rect . $ywert . '" x="' . $x . '" y="0"></rect>';
      }
      $bars = '
<g class="bar" transform="translate(0,' . $hoch . ') scale(1,-1)">' . $bars . '</g>';

      if (!empty($argv["bars"]) and $argv["bars"] == 'no')
        $bars='';

      $ylabel = '
<g class="numbers">';
      $ysmall = '
<g class="smallnumbers">';
      $linethick = '
<g class="thick">';
      $linethin = '
<g class="thin">';

      $log = floor(log10($max_y)); # 10er logarithmus des Maximums der Messwerte
      $pow = 10**$log; # die Größenordnung der Messwerte z.B. 0.1, 1, 10...
      $steps = floor($max_y / $pow ); # die Zahl der Querlinien
      #return '<br>ordnung: ' .  $pow . '<br>max: ' .  $max_y . '<br>schritte: ' . $steps;
      for ($i=1; $i<= $steps; $i++)
      {
        $j = $i * $pow ;
        $jj = $hoch - $yfaktor * $j;
        if ($log < 2) $maxlab = number_format($j,2,",",".");
        else $maxlab = number_format($j,0,",",".");
        $ylabel .= '
<text text-anchor="start" dy="0.5" y="' . $jj . '">' . $maxlab . '</text>';
        #if ($i>0 and $i<$val)
        $linethick .= '
<rect y="' . $jj . '" width="1200" height="0.01" class="linethick"/>';
        $j = $j - $pow/2;
        if ($log < 2) $maxlab = number_format($j,2,",",".");
        else $maxlab = number_format($j,0,",",".");
        $jj = $hoch -  $yfaktor * $j;
        $ysmall .= '
<text text-anchor="start" dy="0.5" y="' . $jj . '">' . $maxlab . '</text>';
        $linethin .= '
<rect y="' . $jj . '" width="1200" height="0.01" class="linethin"/>';
        if ($steps < 2)
        {
          $j = $i * $pow - $pow/4;
          if ($log < 2) $maxlab = number_format($j,2,",",".");
          else $maxlab = number_format($j,0,",",".");
          $jj = $hoch -  $yfaktor * $j;
          $ysmall .= '
<text text-anchor="start" dy="0.5" y="' . $jj . '">' . $maxlab . '</text>';
        $linethin .= '
<rect y="' . $jj . '" width="1200" height="0.01" class="linethin"/>';
          $j = $i * $pow - 3 * $pow/4;
          if ($log < 2) $maxlab = number_format($j,2,",",".");
          else $maxlab = number_format($j,0,",",".");
          $jj = $hoch -  $yfaktor * $j;
          $ysmall .= '
<text text-anchor="start" dy="0.5" y="' . $jj . '">' . $maxlab . '</text>';
        $linethin .= '
<rect y="' . $jj . '" width="1200" height="0.01" class="linethin"/>';
        }
      }
      #number_format("1000000",2,",",".")
      # und jetzt noch das Maximum der Messwerte explizit dazu schreiben
      if ($log < 2) $maxlab = number_format($max_y,3,",",".");
      else $maxlab = number_format($max_y,0,",",".");
      $ylabel .= '
<text text-anchor="start" dy="0.5" y="0">' . $maxlab . '</text>
</g>';
      $ysmall .= '
</g>';
      $linethick .= '
</g>';

      $linethin .= '
</g>';
      #return $pow . ' * ' . $max_y . ' * ' . $val . ' # ' . floor(log10($max_y)) . ' + ' . $ry;

      #return '<p>' . $pfad ; <g transform="translate(50,30) rotate(180)">
      $lablen = $lablen * 10;
      $boxheight = $lablen + $hoch;
      $output  = '
<svg
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1"
viewbox="0 0 1300 ' . $boxheight . '" width="1300" height="' . $boxheight . '">
' .
$style .
'
<g transform = "translate(0,20)">
<g transform = "translate(15)">
<rect width="1200" height="' . $hoch . '" y="0"
id="paper" fill="#85c2ff" opacity="0.1" />' .
$bars .
$labels . 
$linethick . $linethin .
$pfad . '
</g>
<g transform = "translate(0)">' .
$ylabel . $ysmall . '
</g> 
</g>' .
'
</svg>';

        #$output = empty($input) ? $svgtest : $input;
        $tmppath = "/images/png/";
        $bildurl = "$wgServer$wgScriptPath$tmppath";
        $bildfolder = $_SERVER['DOCUMENT_ROOT'] . $wgScriptPath . '/images/png/';
        $mydatum = date('Y-m-d-His'); 
        $bildname = "produktionsgrafik-" . $mydatum;
        $html = "<p style='font-size:0.7em'>
<a href='$bildurl$bildname.png' target='_blank'><img src='$bildurl$bildname.png' width='50'>Grafik im PNG-Format speichern</a> 
&middot; <a href='$bildurl$bildname.svg' target='_blank'>Grafik im SVG-Format speichern</a>
</p>";
        $erzeug = $this->toPng($output, $bildfolder, $bildname);
        
        if (!empty($argv["svg"])) $output .= '<pre>' . $wgParser->recursiveTagParse($output) . '<pre>';
        #return $code;
        return '<p>' . $output . '</p>' . $html . '</p>' . $code; 
     }

    private function toPfad($daten, $xfaktor = 10)
    {
      # liefert einen Pfad als Ergebnis zurück
      # aufeinanderfolgende Elemente sollen nur ein Pfadelement ergeben
      # optional können die Daten skaliert werden

      $pfad = '';

      #return implode('+', $daten);
      if (!is_array($daten)) return; # wenn kein Array übergeben wurde, zurück

      #$faktor = 1;
      $max_y = max($daten);
      $wert = $max_y - $daten[0]; # Y-Startwert
      $pfad  = "M0," . $wert;
      $hor   = $xfaktor;

      for ($i=1; $i<count($daten); $i++)
      {
        if ($daten[$i] == $wert) ## Wiederholung des y-Wertes, wir erhöhen nur den Zähler
        {
          $hor+=$xfaktor;
        }
        else
        {
          $vert = ($daten[$i-1] - $daten[$i]);
          $pfad .= "h $hor v $vert ";
          $hor=$xfaktor;
          $wert = $daten[$i];
        }
      }
      # Abschluss des Pfades
      $pfad .= "h $hor";
      return $pfad;
    }

    private function toPng($bild, $pfad, $name, $breit=1300, $hoch=700)
    {
      # wandelt ein SVG in ein PNG um
      # der SVG-Inhalt muss in der Variablen bild gespeichert sein
      # Parameter:
      # bild ... string mit dem SVG-Text
      # pfad ... Pfad des zu speichernden Bildes
      # name ... Name unter dem das Bild gespeichert werden soll
    
       global $wgServer, $wgScriptPath, $wgUploadPath;

      if (empty($bild) or empty($pfad) or empty($name))
      {
        return 'Fehler bei Bildererzeugung, fehlender Parameter';
      }
  
      $bname = $pfad . $name . ".png";
      $im = new Imagick();
      #return "$pfad $name .png";
      
      $svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
' . $bild;
    
      $im->readImageBlob($svg);
      $geo = $im->getImageGeometry ();
      $hoch= $geo['height']*2 ;
      $breit = $geo['width']*2;
      #return $geo['width'] . $geo['height'] ;
      $svgname = "$pfad$name.svg";
      $x = file_put_contents($svgname, $svg);
      #return "svgname" . $svgname;
      #return "imageblob";
      $im->setImageBackgroundColor(new ImagickPixel('transparent'));
      $im->resizeImage($breit, $hoch, imagick::FILTER_BOX, 1);
      $im->setImageFormat("png");
      #$im->scaleImage($breit, $hoch); 
      $im->borderImage('#fff', 10, 10);
      $im->writeImage($bname);
      $im->clear();
      $im->destroy();
  
      return "Bild als $bname gespeichert";
    }

}