vendor/amenadiel/jpgraph/src/graph/Graph.php line 1853

  1. <?php
  2. /**
  3.  * JPGraph v4.0.3
  4.  */
  5. namespace Amenadiel\JpGraph\Graph;
  6. require_once __DIR__ '/../config.inc.php';
  7. use Amenadiel\JpGraph\Image;
  8. use Amenadiel\JpGraph\Plot;
  9. use Amenadiel\JpGraph\Text;
  10. use Amenadiel\JpGraph\Util;
  11. /**
  12.  * @class Graph
  13.  * // Description: Main class to handle graphs
  14.  */
  15. class Graph
  16. {
  17.     public $gDateLocale;
  18.     public $gJpgDateLocale;
  19.     public $cache// Cache object (singleton)
  20.     public $img// Img object (singleton)
  21.     public $plots   = []; // Array of all plot object in the graph (for Y 1 axis)
  22.     public $y2plots = []; // Array of all plot object in the graph (for Y 2 axis)
  23.     public $ynplots = [];
  24.     public $xscale// X Scale object (could be instance of LinearScale or LogScale
  25.     public $yscale;
  26.     public $y2scale;
  27.     public $ynscale = [];
  28.     public $iIcons  = []; // Array of Icons to add to
  29.     public $cache_name// File name to be used for the current graph in the cache directory
  30.     public $xgrid// X Grid object (linear or logarithmic)
  31.     public $ygrid;
  32.     public $y2grid//dito for Y
  33.     public $doframe;
  34.     public $frame_color;
  35.     public $frame_weight// Frame around graph
  36.     public $boxed        false;
  37.     public $box_color    'black';
  38.     public $box_weight   1// Box around plot area
  39.     public $doshadow     false;
  40.     public $shadow_width 4;
  41.     public $shadow_color 'gray@0.5'// Shadow for graph
  42.     public $xaxis// X-axis (instane of Axis class)
  43.     public $yaxis;
  44.     public $y2axis;
  45.     public $ynaxis = []; // Y axis (instance of Axis class)
  46.     public $margin_color// Margin color of graph
  47.     public $plotarea_color = [255255255]; // Plot area color
  48.     public $title;
  49.     public $subtitle;
  50.     public $subsubtitle// Title and subtitle(s) text object
  51.     public $axtype 'linlin'// Type of axis
  52.     public $xtick_factor;
  53.     public $ytick_factor// Factor to determine the maximum number of ticks depending on the plot width
  54.     public $texts;
  55.     public $y2texts// Text object to ge shown in the graph
  56.     public $lines;
  57.     public $y2lines;
  58.     public $bands;
  59.     public $y2bands;
  60.     public $text_scale_off          0;
  61.     public $text_scale_abscenteroff = -1// Text scale in fractions and for centering bars
  62.     public $background_image        '';
  63.     public $background_image_type   = -1;
  64.     public $background_image_format 'png';
  65.     public $background_image_bright 0;
  66.     public $background_image_contr  0;
  67.     public $background_image_sat    0;
  68.     public $background_image_xpos   0;
  69.     public $background_image_ypos   0;
  70.     public $image_bright            0;
  71.     public $image_contr             0;
  72.     public $image_sat               0;
  73.     public $inline;
  74.     public $showcsim     0;
  75.     public $csimcolor    'red'//debug stuff, draw the csim boundaris on the image if <>0
  76.     public $grid_depth   DEPTH_BACK// Draw grid under all plots as default
  77.     public $iAxisStyle   AXSTYLE_SIMPLE;
  78.     public $iCSIMdisplay false;
  79.     public $iHasStroked  false;
  80.     public $footer;
  81.     public $csimcachename    '';
  82.     public $csimcachetimeout 0;
  83.     public $iCSIMImgAlt      '';
  84.     public $iDoClipping      false;
  85.     public $y2orderback      true;
  86.     public $tabtitle;
  87.     public $bkg_gradtype   = -1;
  88.     public $bkg_gradstyle  BGRAD_MARGIN;
  89.     public $bkg_gradfrom   'navy';
  90.     public $bkg_gradto     'silver';
  91.     public $plot_gradtype  = -1;
  92.     public $plot_gradstyle BGRAD_MARGIN;
  93.     public $plot_gradfrom  'silver';
  94.     public $plot_gradto    'navy';
  95.     public $titlebackground       false;
  96.     public $titlebackground_color 'lightblue';
  97.     public $titlebackground_style 1;
  98.     public $titlebackground_framecolor;
  99.     public $titlebackground_framestyle;
  100.     public $titlebackground_frameweight;
  101.     public $titlebackground_bevelheight;
  102.     public $titlebkg_fillstyle TITLEBKG_FILLSTYLE_SOLID;
  103.     public $titlebkg_scolor1   'black';
  104.     public $titlebkg_scolor2   'white';
  105.     public $framebevel;
  106.     public $framebeveldepth;
  107.     public $framebevelborder;
  108.     public $framebevelbordercolor;
  109.     public $framebevelcolor1;
  110.     public $framebevelcolor2;
  111.     public $background_image_mix  100;
  112.     public $background_cflag      '';
  113.     public $background_cflag_type BGIMG_FILLPLOT;
  114.     public $background_cflag_mix  100;
  115.     public $iImgTrans             false;
  116.     public $iImgTransHorizon      100;
  117.     public $iImgTransSkewDist     150;
  118.     public $iImgTransDirection    1;
  119.     public $iImgTransMinSize      true;
  120.     public $iImgTransFillColor    'white';
  121.     public $iImgTransHighQ        false;
  122.     public $iImgTransBorder       false;
  123.     public $iImgTransHorizonPos   0.5;
  124.     public $legend;
  125.     public $graph_theme;
  126.     protected $iYAxisDeltaPos       50;
  127.     protected $iIconDepth           DEPTH_BACK;
  128.     protected $iAxisLblBgType       0;
  129.     protected $iXAxisLblBgFillColor 'lightgray';
  130.     protected $iXAxisLblBgColor     'black';
  131.     protected $iYAxisLblBgFillColor 'lightgray';
  132.     protected $iYAxisLblBgColor     'black';
  133.     protected $iTables;
  134.     protected $isRunningClear false;
  135.     protected $inputValues;
  136.     protected $isAfterSetScale false;
  137.     // aWIdth   Width in pixels of image
  138.     // aHeight   Height in pixels of image
  139.     // aCachedName Name for image file in cache directory
  140.     // aTimeOut  Timeout in minutes for image in cache
  141.     // aInline  If true the image is streamed back in the call to Stroke()
  142.     //   If false the image is just created in the cache
  143.     public function __construct($aWidth 300$aHeight 200$aCachedName ''$aTimeout 0$aInline true)
  144.     {
  145.         $this->gDateLocale    = new Util\DateLocale();
  146.         $this->gJpgDateLocale = new Util\DateLocale();
  147.         if (!is_numeric($aWidth) || !is_numeric($aHeight)) {
  148.             Util\JpGraphError::RaiseL(25008); //('Image width/height argument in Graph::Graph() must be numeric');
  149.         }
  150.         // Initialize frame and margin
  151.         $this->InitializeFrameAndMargin();
  152.         // Automatically generate the image file name based on the name of the script that
  153.         // generates the graph
  154.         if ($aCachedName == 'auto') {
  155.             $aCachedName Util\Helper::GenImgName();
  156.         }
  157.         // Should the image be streamed back to the browser or only to the cache?
  158.         $this->inline $aInline;
  159.         $this->img   = new Image\RotImage($aWidth$aHeight);
  160.         $this->cache = new Image\ImgStreamCache();
  161.         // Window doesn't like '?' in the file name so replace it with an '_'
  162.         $aCachedName str_replace('?''_'$aCachedName);
  163.         $this->SetupCache($aCachedName$aTimeout);
  164.         $this->title = new Text\Text();
  165.         $this->title->ParagraphAlign('center');
  166.         $this->title->SetFont(FF_DEFAULTFS_NORMAL); //FF_FONT2, FS_BOLD
  167.         $this->title->SetMargin(5);
  168.         $this->title->SetAlign('center');
  169.         $this->subtitle = new Text\Text();
  170.         $this->subtitle->ParagraphAlign('center');
  171.         $this->subtitle->SetMargin(3);
  172.         $this->subtitle->SetAlign('center');
  173.         $this->subsubtitle = new Text\Text();
  174.         $this->subsubtitle->ParagraphAlign('center');
  175.         $this->subsubtitle->SetMargin(3);
  176.         $this->subsubtitle->SetAlign('center');
  177.         $this->legend = new Legend();
  178.         $this->footer = new Image\Footer();
  179.         // If the cached version exist just read it directly from the
  180.         // cache, stream it back to browser and exit
  181.         if ($aCachedName != '' && READ_CACHE && $aInline) {
  182.             if ($this->cache->GetAndStream($this->img$aCachedName)) {
  183.                 exit();
  184.             }
  185.         }
  186.         $this->SetTickDensity(); // Normal density
  187.         $this->tabtitle = new Text\GraphTabTitle();
  188.         if (!$this->isRunningClear) {
  189.             $this->inputValues                = [];
  190.             $this->inputValues['aWidth']      = $aWidth;
  191.             $this->inputValues['aHeight']     = $aHeight;
  192.             $this->inputValues['aCachedName'] = $aCachedName;
  193.             $this->inputValues['aTimeout']    = $aTimeout;
  194.             $this->inputValues['aInline']     = $aInline;
  195.             $theme_class '\Amenadiel\JpGraph\Themes\\' DEFAULT_THEME_CLASS;
  196.             if (class_exists($theme_class)) {
  197.                 $this->graph_theme = new $theme_class();
  198.             }
  199.         }
  200.     }
  201.     public function InitializeFrameAndMargin()
  202.     {
  203.         $this->doframe      true;
  204.         $this->frame_color  'black';
  205.         $this->frame_weight 1;
  206.         $this->titlebackground_framecolor  'blue';
  207.         $this->titlebackground_framestyle  2;
  208.         $this->titlebackground_frameweight 1;
  209.         $this->titlebackground_bevelheight 3;
  210.         $this->titlebkg_fillstyle          TITLEBKG_FILLSTYLE_SOLID;
  211.         $this->titlebkg_scolor1            'black';
  212.         $this->titlebkg_scolor2            'white';
  213.         $this->framebevel                  false;
  214.         $this->framebeveldepth             2;
  215.         $this->framebevelborder            false;
  216.         $this->framebevelbordercolor       'black';
  217.         $this->framebevelcolor1            'white@0.4';
  218.         $this->framebevelcolor2            'black@0.4';
  219.         $this->margin_color = [250250250];
  220.     }
  221.     public function SetupCache($aFilename$aTimeout 60)
  222.     {
  223.         $this->cache_name $aFilename;
  224.         $this->cache->SetTimeOut($aTimeout);
  225.     }
  226.     // Enable final image perspective transformation
  227.     public function Set3DPerspective($aDir 1$aHorizon 100$aSkewDist 120$aQuality false$aFillColor '#FFFFFF'$aBorder false$aMinSize true$aHorizonPos 0.5)
  228.     {
  229.         $this->iImgTrans           true;
  230.         $this->iImgTransHorizon    $aHorizon;
  231.         $this->iImgTransSkewDist   $aSkewDist;
  232.         $this->iImgTransDirection  $aDir;
  233.         $this->iImgTransMinSize    $aMinSize;
  234.         $this->iImgTransFillColor  $aFillColor;
  235.         $this->iImgTransHighQ      $aQuality;
  236.         $this->iImgTransBorder     $aBorder;
  237.         $this->iImgTransHorizonPos $aHorizonPos;
  238.     }
  239.     public function SetUserFont($aNormal$aBold ''$aItalic ''$aBoldIt '')
  240.     {
  241.         $this->img->ttf->SetUserFont($aNormal$aBold$aItalic$aBoldIt);
  242.     }
  243.     public function SetUserFont1($aNormal$aBold ''$aItalic ''$aBoldIt '')
  244.     {
  245.         $this->img->ttf->SetUserFont1($aNormal$aBold$aItalic$aBoldIt);
  246.     }
  247.     public function SetUserFont2($aNormal$aBold ''$aItalic ''$aBoldIt '')
  248.     {
  249.         $this->img->ttf->SetUserFont2($aNormal$aBold$aItalic$aBoldIt);
  250.     }
  251.     public function SetUserFont3($aNormal$aBold ''$aItalic ''$aBoldIt '')
  252.     {
  253.         $this->img->ttf->SetUserFont3($aNormal$aBold$aItalic$aBoldIt);
  254.     }
  255.     // Set Image format and optional quality
  256.     public function SetImgFormat($aFormat$aQuality 75)
  257.     {
  258.         $this->img->SetImgFormat($aFormat$aQuality);
  259.     }
  260.     // Should the grid be in front or back of the plot?
  261.     public function SetGridDepth($aDepth)
  262.     {
  263.         $this->grid_depth $aDepth;
  264.     }
  265.     public function SetIconDepth($aDepth)
  266.     {
  267.         $this->iIconDepth $aDepth;
  268.     }
  269.     // Specify graph angle 0-360 degrees.
  270.     public function SetAngle($aAngle)
  271.     {
  272.         $this->img->SetAngle($aAngle);
  273.     }
  274.     public function SetAlphaBlending($aFlg true)
  275.     {
  276.         $this->img->SetAlphaBlending($aFlg);
  277.     }
  278.     // Shortcut to image margin
  279.     public function SetMargin($lm$rm$tm$bm)
  280.     {
  281.         $this->img->SetMargin($lm$rm$tm$bm);
  282.     }
  283.     public function SetY2OrderBack($aBack true)
  284.     {
  285.         $this->y2orderback $aBack;
  286.     }
  287.     // Rotate the graph 90 degrees and set the margin
  288.     // when we have done a 90 degree rotation
  289.     public function Set90AndMargin($lm 0$rm 0$tm 0$bm 0)
  290.     {
  291.         $lm $lm == floor(0.2 $this->img->width) : $lm;
  292.         $rm $rm == floor(0.1 $this->img->width) : $rm;
  293.         $tm $tm == floor(0.2 $this->img->height) : $tm;
  294.         $bm $bm == floor(0.1 $this->img->height) : $bm;
  295.         $adj = ($this->img->height $this->img->width) / 2;
  296.         $this->img->SetMargin($tm $adj$bm $adj$rm $adj$lm $adj);
  297.         $this->img->SetCenter(floor($this->img->width 2), floor($this->img->height 2));
  298.         $this->SetAngle(90);
  299.         if (empty($this->yaxis) || empty($this->xaxis)) {
  300.             Util\JpGraphError::RaiseL(25009); //('You must specify what scale to use with a call to Graph::SetScale()');
  301.         }
  302.         $this->xaxis->SetLabelAlign('right''center');
  303.         $this->yaxis->SetLabelAlign('center''bottom');
  304.     }
  305.     public function SetClipping($aFlg true)
  306.     {
  307.         $this->iDoClipping $aFlg;
  308.     }
  309.     // Add a plot object to the graph
  310.     public function Add($aPlot)
  311.     {
  312.         if ($aPlot == null) {
  313.             Util\JpGraphError::RaiseL(25010); //("Graph::Add() You tried to add a null plot to the graph.");
  314.         }
  315.         if (is_array($aPlot) && safe_count($aPlot) > 0) {
  316.             $cl $aPlot[0];
  317.         } else {
  318.             $cl $aPlot;
  319.         }
  320.         if ($cl instanceof Text\Text) {
  321.             $this->AddText($aPlot);
  322.         } elseif (($cl instanceof Plot\PlotLine)) {
  323.             $this->AddLine($aPlot);
  324.         } elseif (($cl instanceof Plot\PlotBand)) {
  325.             $this->AddBand($aPlot);
  326.         } elseif (($cl instanceof Plot\IconPlot)) {
  327.             $this->AddIcon($aPlot);
  328.         } elseif (($cl instanceof Text\GTextTable)) {
  329.             $this->AddTable($aPlot);
  330.         } else {
  331.             if (is_array($aPlot)) {
  332.                 $this->plots array_merge($this->plots$aPlot);
  333.             } else {
  334.                 $this->plots[] = $aPlot;
  335.             }
  336.         }
  337.         if ($this->graph_theme) {
  338.             $this->graph_theme->SetupPlot($aPlot);
  339.         }
  340.     }
  341.     public function AddTable($aTable)
  342.     {
  343.         if (is_array($aTable)) {
  344.             for ($i 0$i safe_count($aTable); ++$i) {
  345.                 $this->iTables[] = $aTable[$i];
  346.             }
  347.         } else {
  348.             $this->iTables[] = $aTable;
  349.         }
  350.     }
  351.     public function AddIcon($aIcon)
  352.     {
  353.         if (is_array($aIcon)) {
  354.             for ($i 0$i safe_count($aIcon); ++$i) {
  355.                 $this->iIcons[] = $aIcon[$i];
  356.             }
  357.         } else {
  358.             $this->iIcons[] = $aIcon;
  359.         }
  360.     }
  361.     // Add plot to second Y-scale
  362.     public function AddY2($aPlot)
  363.     {
  364.         if ($aPlot == null) {
  365.             Util\JpGraphError::RaiseL(25011); //("Graph::AddY2() You tried to add a null plot to the graph.");
  366.         }
  367.         if (is_array($aPlot) && safe_count($aPlot) > 0) {
  368.             $cl $aPlot[0];
  369.         } else {
  370.             $cl $aPlot;
  371.         }
  372.         if ($cl instanceof Text\Text) {
  373.             $this->AddText($aPlottrue);
  374.         } elseif (($cl instanceof Plot\PlotLine)) {
  375.             $this->AddLine($aPlottrue);
  376.         } elseif (($cl instanceof Plot\PlotBand)) {
  377.             $this->AddBand($aPlottrue);
  378.         } else {
  379.             $this->y2plots[] = $aPlot;
  380.         }
  381.         if ($this->graph_theme) {
  382.             $this->graph_theme->SetupPlot($aPlot);
  383.         }
  384.     }
  385.     // Add plot to the extra Y-axises
  386.     public function AddY($aN$aPlot)
  387.     {
  388.         if ($aPlot == null) {
  389.             Util\JpGraphError::RaiseL(25012); //("Graph::AddYN() You tried to add a null plot to the graph.");
  390.         }
  391.         if (is_array($aPlot) && safe_count($aPlot) > 0) {
  392.             $cl $aPlot[0];
  393.         } else {
  394.             $cl $aPlot;
  395.         }
  396.         if (($cl instanceof Text\Text) ||
  397.             ($cl instanceof Plot\PlotLine) ||
  398.             ($cl instanceof Plot\PlotBand)) {
  399.             Util\JpGraphError::RaiseL(25013); //('You can only add standard plots to multiple Y-axis');
  400.         } else {
  401.             $this->ynplots[$aN][] = $aPlot;
  402.         }
  403.         if ($this->graph_theme) {
  404.             $this->graph_theme->SetupPlot($aPlot);
  405.         }
  406.     }
  407.     // Add text object to the graph
  408.     public function AddText($aTxt$aToY2 false)
  409.     {
  410.         if ($aTxt == null) {
  411.             Util\JpGraphError::RaiseL(25014); //("Graph::AddText() You tried to add a null text to the graph.");
  412.         }
  413.         if ($aToY2) {
  414.             if (is_array($aTxt)) {
  415.                 for ($i 0$i safe_count($aTxt); ++$i) {
  416.                     $this->y2texts[] = $aTxt[$i];
  417.                 }
  418.             } else {
  419.                 $this->y2texts[] = $aTxt;
  420.             }
  421.         } else {
  422.             if (is_array($aTxt)) {
  423.                 for ($i 0$i safe_count($aTxt); ++$i) {
  424.                     $this->texts[] = $aTxt[$i];
  425.                 }
  426.             } else {
  427.                 $this->texts[] = $aTxt;
  428.             }
  429.         }
  430.     }
  431.     // Add a line object (class PlotLine) to the graph
  432.     public function AddLine($aLine$aToY2 false)
  433.     {
  434.         if ($aLine == null) {
  435.             Util\JpGraphError::RaiseL(25015); //("Graph::AddLine() You tried to add a null line to the graph.");
  436.         }
  437.         if ($aToY2) {
  438.             if (is_array($aLine)) {
  439.                 for ($i 0$i safe_count($aLine); ++$i) {
  440.                     //$this->y2lines[]=$aLine[$i];
  441.                     $this->y2plots[] = $aLine[$i];
  442.                 }
  443.             } else {
  444.                 //$this->y2lines[] = $aLine;
  445.                 $this->y2plots[] = $aLine;
  446.             }
  447.         } else {
  448.             if (is_array($aLine)) {
  449.                 for ($i 0$i safe_count($aLine); ++$i) {
  450.                     //$this->lines[]=$aLine[$i];
  451.                     $this->plots[] = $aLine[$i];
  452.                 }
  453.             } else {
  454.                 //$this->lines[] = $aLine;
  455.                 $this->plots[] = $aLine;
  456.             }
  457.         }
  458.     }
  459.     // Add vertical or horizontal band
  460.     public function AddBand($aBand$aToY2 false)
  461.     {
  462.         if ($aBand == null) {
  463.             Util\JpGraphError::RaiseL(25016); //(" Graph::AddBand() You tried to add a null band to the graph.");
  464.         }
  465.         if ($aToY2) {
  466.             if (is_array($aBand)) {
  467.                 for ($i 0$i safe_count($aBand); ++$i) {
  468.                     $this->y2bands[] = $aBand[$i];
  469.                 }
  470.             } else {
  471.                 $this->y2bands[] = $aBand;
  472.             }
  473.         } else {
  474.             if (is_array($aBand)) {
  475.                 for ($i 0$i safe_count($aBand); ++$i) {
  476.                     $this->bands[] = $aBand[$i];
  477.                 }
  478.             } else {
  479.                 $this->bands[] = $aBand;
  480.             }
  481.         }
  482.     }
  483.     public function SetPlotGradient($aFrom 'navy'$aTo 'silver'$aGradType 2)
  484.     {
  485.         $this->plot_gradtype $aGradType;
  486.         $this->plot_gradfrom $aFrom;
  487.         $this->plot_gradto   $aTo;
  488.     }
  489.     public function SetBackgroundGradient($aFrom 'navy'$aTo 'silver'$aGradType 2$aStyle BGRAD_FRAME)
  490.     {
  491.         $this->bkg_gradtype  $aGradType;
  492.         $this->bkg_gradstyle $aStyle;
  493.         $this->bkg_gradfrom  $aFrom;
  494.         $this->bkg_gradto    $aTo;
  495.     }
  496.     // Set a country flag in the background
  497.     public function SetBackgroundCFlag($aName$aBgType BGIMG_FILLPLOT$aMix 100)
  498.     {
  499.         $this->background_cflag      $aName;
  500.         $this->background_cflag_type $aBgType;
  501.         $this->background_cflag_mix  $aMix;
  502.     }
  503.     // Alias for the above method
  504.     public function SetBackgroundCountryFlag($aName$aBgType BGIMG_FILLPLOT$aMix 100)
  505.     {
  506.         $this->background_cflag      $aName;
  507.         $this->background_cflag_type $aBgType;
  508.         $this->background_cflag_mix  $aMix;
  509.     }
  510.     // Specify a background image
  511.     public function SetBackgroundImage($aFileName$aBgType BGIMG_FILLPLOT$aImgFormat 'auto')
  512.     {
  513.         // Get extension to determine image type
  514.         if ($aImgFormat == 'auto') {
  515.             $e explode('.'$aFileName);
  516.             if (empty($e)) {
  517.                 Util\JpGraphError::RaiseL(25018$aFileName); //('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
  518.             }
  519.             $valid_formats = ['png''jpg''gif'];
  520.             $aImgFormat    strtolower($e[count($e) - 1]);
  521.             if ($aImgFormat == 'jpeg') {
  522.                 $aImgFormat 'jpg';
  523.             } elseif (!in_array($aImgFormat$valid_formatstrue)) {
  524.                 Util\JpGraphError::RaiseL(25019$aImgFormat); //('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
  525.             }
  526.         }
  527.         $this->background_image        $aFileName;
  528.         $this->background_image_type   $aBgType;
  529.         $this->background_image_format $aImgFormat;
  530.     }
  531.     public function SetBackgroundImageMix($aMix)
  532.     {
  533.         $this->background_image_mix $aMix;
  534.     }
  535.     // Adjust background image position
  536.     public function SetBackgroundImagePos($aXpos$aYpos)
  537.     {
  538.         $this->background_image_xpos $aXpos;
  539.         $this->background_image_ypos $aYpos;
  540.     }
  541.     // Specify axis style (boxed or single)
  542.     public function SetAxisStyle($aStyle)
  543.     {
  544.         $this->iAxisStyle $aStyle;
  545.     }
  546.     // Set a frame around the plot area
  547.     public function SetBox($aDrawPlotFrame true$aPlotFrameColor = [000], $aPlotFrameWeight 1)
  548.     {
  549.         $this->boxed      $aDrawPlotFrame;
  550.         $this->box_weight $aPlotFrameWeight;
  551.         $this->box_color  $aPlotFrameColor;
  552.     }
  553.     // Specify color for the plotarea (not the margins)
  554.     public function SetColor($aColor)
  555.     {
  556.         $this->plotarea_color $aColor;
  557.     }
  558.     // Specify color for the margins (all areas outside the plotarea)
  559.     public function SetMarginColor($aColor)
  560.     {
  561.         $this->margin_color $aColor;
  562.     }
  563.     // Set a frame around the entire image
  564.     public function SetFrame($aDrawImgFrame true$aImgFrameColor = [000], $aImgFrameWeight 1)
  565.     {
  566.         $this->doframe      $aDrawImgFrame;
  567.         $this->frame_color  $aImgFrameColor;
  568.         $this->frame_weight $aImgFrameWeight;
  569.     }
  570.     public function SetFrameBevel($aDepth 3$aBorder false$aBorderColor 'black'$aColor1 'white@0.4'$aColor2 'darkgray@0.4'$aFlg true)
  571.     {
  572.         $this->framebevel            $aFlg;
  573.         $this->framebeveldepth       $aDepth;
  574.         $this->framebevelborder      $aBorder;
  575.         $this->framebevelbordercolor $aBorderColor;
  576.         $this->framebevelcolor1      $aColor1;
  577.         $this->framebevelcolor2      $aColor2;
  578.         $this->doshadow false;
  579.     }
  580.     // Set the shadow around the whole image
  581.     public function SetShadow($aShowShadow true$aShadowWidth 5$aShadowColor 'darkgray')
  582.     {
  583.         $this->doshadow     $aShowShadow;
  584.         $this->shadow_color $aShadowColor;
  585.         $this->shadow_width $aShadowWidth;
  586.         $this->footer->iBottomMargin += $aShadowWidth;
  587.         $this->footer->iRightMargin += $aShadowWidth;
  588.     }
  589.     // Specify x,y scale. Note that if you manually specify the scale
  590.     // you must also specify the tick distance with a call to Ticks::Set()
  591.     public function SetScale($aAxisType$aYMin 1$aYMax 1$aXMin 1$aXMax 1)
  592.     {
  593.         $this->axtype $aAxisType;
  594.         if ($aYMax $aYMin || $aXMax $aXMin) {
  595.             Util\JpGraphError::RaiseL(25020); //('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
  596.         }
  597.         $yt substr($aAxisType, -33);
  598.         if ($yt == 'lin') {
  599.             $this->yscale = new LinearScale($aYMin$aYMax);
  600.         } elseif ($yt == 'int') {
  601.             $this->yscale = new LinearScale($aYMin$aYMax);
  602.             $this->yscale->SetIntScale();
  603.         } elseif ($yt == 'log') {
  604.             $this->yscale = new LogScale($aYMin$aYMax);
  605.         } else {
  606.             Util\JpGraphError::RaiseL(25021$aAxisType); //("Unknown scale specification for Y-scale. ($aAxisType)");
  607.         }
  608.         $xt substr($aAxisType03);
  609.         if ($xt == 'lin' || $xt == 'tex') {
  610.             $this->xscale            = new LinearScale($aXMin$aXMax'x');
  611.             $this->xscale->textscale = ($xt == 'tex');
  612.         } elseif ($xt == 'int') {
  613.             $this->xscale = new LinearScale($aXMin$aXMax'x');
  614.             $this->xscale->SetIntScale();
  615.         } elseif ($xt == 'dat') {
  616.             $this->xscale = new DateScale($aXMin$aXMax'x');
  617.         } elseif ($xt == 'log') {
  618.             $this->xscale = new LogScale($aXMin$aXMax'x');
  619.         } else {
  620.             Util\JpGraphError::RaiseL(25022$aAxisType); //(" Unknown scale specification for X-scale. ($aAxisType)");
  621.         }
  622.         $this->xaxis = new Axis($this->img$this->xscale);
  623.         $this->yaxis = new Axis($this->img$this->yscale);
  624.         $this->xgrid = new Grid($this->xaxis);
  625.         $this->ygrid = new Grid($this->yaxis);
  626.         $this->ygrid->Show();
  627.         if (!$this->isRunningClear) {
  628.             $this->inputValues['aAxisType'] = $aAxisType;
  629.             $this->inputValues['aYMin']     = $aYMin;
  630.             $this->inputValues['aYMax']     = $aYMax;
  631.             $this->inputValues['aXMin']     = $aXMin;
  632.             $this->inputValues['aXMax']     = $aXMax;
  633.             if ($this->graph_theme) {
  634.                 $this->graph_theme->ApplyGraph($this);
  635.             }
  636.         }
  637.         $this->isAfterSetScale true;
  638.     }
  639.     // Specify secondary Y scale
  640.     public function SetY2Scale($aAxisType 'lin'$aY2Min 1$aY2Max 1)
  641.     {
  642.         if ($aAxisType == 'lin') {
  643.             $this->y2scale = new LinearScale($aY2Min$aY2Max);
  644.         } elseif ($aAxisType == 'int') {
  645.             $this->y2scale = new LinearScale($aY2Min$aY2Max);
  646.             $this->y2scale->SetIntScale();
  647.         } elseif ($aAxisType == 'log') {
  648.             $this->y2scale = new LogScale($aY2Min$aY2Max);
  649.         } else {
  650.             Util\JpGraphError::RaiseL(25023$aAxisType); //("JpGraph: Unsupported Y2 axis type: $aAxisType\nMust be one of (lin,log,int)");
  651.         }
  652.         $this->y2axis = new Axis($this->img$this->y2scale);
  653.         $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
  654.         $this->y2axis->SetLabelSide(SIDE_RIGHT);
  655.         $this->y2axis->SetPos('max');
  656.         $this->y2axis->SetTitleSide(SIDE_RIGHT);
  657.         // Deafult position is the max x-value
  658.         $this->y2grid = new Grid($this->y2axis);
  659.         if ($this->graph_theme) {
  660.             $this->graph_theme->ApplyGraph($this);
  661.         }
  662.     }
  663.     // Set the delta position (in pixels) between the multiple Y-axis
  664.     public function SetYDeltaDist($aDist)
  665.     {
  666.         $this->iYAxisDeltaPos $aDist;
  667.     }
  668.     // Specify secondary Y scale
  669.     public function SetYScale($aN$aAxisType 'lin'$aYMin 1$aYMax 1)
  670.     {
  671.         if ($aAxisType == 'lin') {
  672.             $this->ynscale[$aN] = new LinearScale($aYMin$aYMax);
  673.         } elseif ($aAxisType == 'int') {
  674.             $this->ynscale[$aN] = new LinearScale($aYMin$aYMax);
  675.             $this->ynscale[$aN]->SetIntScale();
  676.         } elseif ($aAxisType == 'log') {
  677.             $this->ynscale[$aN] = new LogScale($aYMin$aYMax);
  678.         } else {
  679.             Util\JpGraphError::RaiseL(25024$aAxisType); //("JpGraph: Unsupported Y axis type: $aAxisType\nMust be one of (lin,log,int)");
  680.         }
  681.         $this->ynaxis[$aN] = new Axis($this->img$this->ynscale[$aN]);
  682.         $this->ynaxis[$aN]->scale->ticks->SetDirection(SIDE_LEFT);
  683.         $this->ynaxis[$aN]->SetLabelSide(SIDE_RIGHT);
  684.         if ($this->graph_theme) {
  685.             $this->graph_theme->ApplyGraph($this);
  686.         }
  687.     }
  688.     // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
  689.     // The dividing factor have been determined heuristically according to my aesthetic
  690.     // sense (or lack off) y.m.m.v !
  691.     public function SetTickDensity($aYDensity TICKD_NORMAL$aXDensity TICKD_NORMAL)
  692.     {
  693.         $this->xtick_factor 30;
  694.         $this->ytick_factor 25;
  695.         switch ($aYDensity) {
  696.             case TICKD_DENSE:
  697.                 $this->ytick_factor 12;
  698.                 break;
  699.             case TICKD_NORMAL:
  700.                 $this->ytick_factor 25;
  701.                 break;
  702.             case TICKD_SPARSE:
  703.                 $this->ytick_factor 40;
  704.                 break;
  705.             case TICKD_VERYSPARSE:
  706.                 $this->ytick_factor 100;
  707.                 break;
  708.             default:
  709.                 Util\JpGraphError::RaiseL(25025$densy); //("JpGraph: Unsupported Tick density: $densy");
  710.         }
  711.         switch ($aXDensity) {
  712.             case TICKD_DENSE:
  713.                 $this->xtick_factor 15;
  714.                 break;
  715.             case TICKD_NORMAL:
  716.                 $this->xtick_factor 30;
  717.                 break;
  718.             case TICKD_SPARSE:
  719.                 $this->xtick_factor 45;
  720.                 break;
  721.             case TICKD_VERYSPARSE:
  722.                 $this->xtick_factor 60;
  723.                 break;
  724.             default:
  725.                 Util\JpGraphError::RaiseL(25025$densx); //("JpGraph: Unsupported Tick density: $densx");
  726.         }
  727.     }
  728.     // Get a string of all image map areas
  729.     public function GetCSIMareas()
  730.     {
  731.         if (!$this->iHasStroked) {
  732.             $this->Stroke(_CSIM_SPECIALFILE);
  733.         }
  734.         $csim $this->title->GetCSIMAreas();
  735.         $csim .= $this->subtitle->GetCSIMAreas();
  736.         $csim .= $this->subsubtitle->GetCSIMAreas();
  737.         $csim .= $this->legend->GetCSIMAreas();
  738.         if ($this->y2axis != null) {
  739.             $csim .= $this->y2axis->title->GetCSIMAreas();
  740.         }
  741.         if ($this->texts != null) {
  742.             $n safe_count($this->texts);
  743.             for ($i 0$i $n; ++$i) {
  744.                 $csim .= $this->texts[$i]->GetCSIMAreas();
  745.             }
  746.         }
  747.         if ($this->y2texts != null && $this->y2scale != null) {
  748.             $n safe_count($this->y2texts);
  749.             for ($i 0$i $n; ++$i) {
  750.                 $csim .= $this->y2texts[$i]->GetCSIMAreas();
  751.             }
  752.         }
  753.         if ($this->yaxis != null && $this->xaxis != null) {
  754.             $csim .= $this->yaxis->title->GetCSIMAreas();
  755.             $csim .= $this->xaxis->title->GetCSIMAreas();
  756.         }
  757.         $n safe_count($this->plots);
  758.         for ($i 0$i $n; ++$i) {
  759.             $csim .= $this->plots[$i]->GetCSIMareas();
  760.         }
  761.         $n safe_count($this->y2plots);
  762.         for ($i 0$i $n; ++$i) {
  763.             $csim .= $this->y2plots[$i]->GetCSIMareas();
  764.         }
  765.         $n safe_count($this->ynaxis);
  766.         for ($i 0$i $n; ++$i) {
  767.             $m safe_count($this->ynplots[$i]);
  768.             for ($j 0$j $m; ++$j) {
  769.                 $csim .= $this->ynplots[$i][$j]->GetCSIMareas();
  770.             }
  771.         }
  772.         $n safe_count($this->iTables);
  773.         for ($i 0$i $n; ++$i) {
  774.             $csim .= $this->iTables[$i]->GetCSIMareas();
  775.         }
  776.         return $csim;
  777.     }
  778.     // Get a complete <MAP>..</MAP> tag for the final image map
  779.     public function GetHTMLImageMap($aMapName)
  780.     {
  781.         $im "<map name=\"${aMapName}\" id=\"${aMapName}\" >\n";
  782.         $im .= $this->GetCSIMareas();
  783.         $im .= '</map>';
  784.         return $im;
  785.     }
  786.     public function CheckCSIMCache($aCacheName$aTimeOut 60)
  787.     {
  788.         global $_SERVER;
  789.         if ($aCacheName == 'auto') {
  790.             $aCacheName basename($_SERVER['PHP_SELF']);
  791.         }
  792.         $urlarg                 $this->GetURLArguments();
  793.         $this->csimcachename    CSIMCACHE_DIR $aCacheName $urlarg;
  794.         $this->csimcachetimeout $aTimeOut;
  795.         // First determine if we need to check for a cached version
  796.         // This differs from the standard cache in the sense that the
  797.         // image and CSIM map HTML file is written relative to the directory
  798.         // the script executes in and not the specified cache directory.
  799.         // The reason for this is that the cache directory is not necessarily
  800.         // accessible from the HTTP server.
  801.         if ($this->csimcachename != '') {
  802.             $dir      dirname($this->csimcachename);
  803.             $base     basename($this->csimcachename);
  804.             $base     strtok($base'.');
  805.             $suffix   strtok('.');
  806.             $basecsim $dir '/' $base '?' $urlarg '_csim_.html';
  807.             $baseimg  $dir '/' $base '?' $urlarg '.' $this->img->img_format;
  808.             $timedout false;
  809.             // Does it exist at all ?
  810.             if (file_exists($basecsim) && file_exists($baseimg)) {
  811.                 // Check that it hasn't timed out
  812.                 $diff time() - filemtime($basecsim);
  813.                 if ($this->csimcachetimeout && ($diff $this->csimcachetimeout 60)) {
  814.                     $timedout true;
  815.                     @unlink($basecsim);
  816.                     @unlink($baseimg);
  817.                 } else {
  818.                     if ($fh = @fopen($basecsim'r')) {
  819.                         fpassthru($fh);
  820.                         return true;
  821.                     }
  822.                     Util\JpGraphError::RaiseL(25027$basecsim); //(" Can't open cached CSIM \"$basecsim\" for reading.");
  823.                 }
  824.             }
  825.         }
  826.         return false;
  827.     }
  828.     // Build the argument string to be used with the csim images
  829.     public static function GetURLArguments($aAddRecursiveBlocker false)
  830.     {
  831.         if ($aAddRecursiveBlocker) {
  832.             // This is a JPGRAPH internal defined that prevents
  833.             // us from recursively coming here again
  834.             $urlarg _CSIM_DISPLAY '=1';
  835.         }
  836.         // Now reconstruct any user URL argument
  837.         reset($_GET);
  838.         foreach ($_GET as $key => $value) {
  839.             if (is_array($value)) {
  840.                 foreach ($value as $k => $v) {
  841.                     $urlarg .= '&amp;' $key '%5B' $k '%5D=' urlencode($v);
  842.                 }
  843.             } else {
  844.                 $urlarg .= '&amp;' $key '=' urlencode($value);
  845.             }
  846.         }
  847.         // It's not ideal to convert POST argument to GET arguments
  848.         // but there is little else we can do. One idea for the
  849.         // future might be recreate the POST header in case.
  850.         reset($_POST);
  851.         foreach ($_POST as $key => $value) {
  852.             if (is_array($value)) {
  853.                 foreach ($value as $k => $v) {
  854.                     $urlarg .= '&amp;' $key '%5B' $k '%5D=' urlencode($v);
  855.                 }
  856.             } else {
  857.                 $urlarg .= '&amp;' $key '=' urlencode($value);
  858.             }
  859.         }
  860.         return $urlarg;
  861.     }
  862.     public function SetCSIMImgAlt($aAlt)
  863.     {
  864.         $this->iCSIMImgAlt $aAlt;
  865.     }
  866.     public function StrokeCSIM($aScriptName 'auto'$aCSIMName ''$aBorder 0)
  867.     {
  868.         if ($aCSIMName == '') {
  869.             // create a random map name
  870.             srand((int) (microtime() * 1000000));
  871.             $r         rand(0100000);
  872.             $aCSIMName '__mapname' $r '__';
  873.         }
  874.         if ($aScriptName == 'auto') {
  875.             $aScriptName basename($_SERVER['PHP_SELF']);
  876.         }
  877.         $urlarg $this->GetURLArguments(true);
  878.         if (empty($_GET[_CSIM_DISPLAY])) {
  879.             // First determine if we need to check for a cached version
  880.             // This differs from the standard cache in the sense that the
  881.             // image and CSIM map HTML file is written relative to the directory
  882.             // the script executes in and not the specified cache directory.
  883.             // The reason for this is that the cache directory is not necessarily
  884.             // accessible from the HTTP server.
  885.             if ($this->csimcachename != '') {
  886.                 $dir      dirname($this->csimcachename);
  887.                 $base     basename($this->csimcachename);
  888.                 $base     strtok($base'.');
  889.                 $suffix   strtok('.');
  890.                 $basecsim $dir '/' $base '?' $urlarg '_csim_.html';
  891.                 $baseimg  $base '?' $urlarg '.' $this->img->img_format;
  892.                 // Check that apache can write to directory specified
  893.                 if (file_exists($dir) && !is_writeable($dir)) {
  894.                     Util\JpGraphError::RaiseL(25028$dir); //('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
  895.                 }
  896.                 // Make sure directory exists
  897.                 $this->cache->MakeDirs($dir);
  898.                 // Write the image file
  899.                 $this->Stroke(CSIMCACHE_DIR $baseimg);
  900.                 // Construct wrapper HTML and write to file and send it back to browser
  901.                 // In the src URL we must replace the '?' with its encoding to prevent the arguments
  902.                 // to be converted to real arguments.
  903.                 $tmp      str_replace('?''%3f'$baseimg);
  904.                 $htmlwrap $this->GetHTMLImageMap($aCSIMName) . "\n" .
  905.                 '<img src="' CSIMCACHE_HTTP_DIR $tmp '" ismap="ismap" usemap="#' $aCSIMName ' width="' $this->img->width '" height="' $this->img->height '" alt="' $this->iCSIMImgAlt "\" />\n";
  906.                 if ($fh = @fopen($basecsim'w')) {
  907.                     fwrite($fh$htmlwrap);
  908.                     fclose($fh);
  909.                     echo $htmlwrap;
  910.                 } else {
  911.                     Util\JpGraphError::RaiseL(25029$basecsim); //(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
  912.                 }
  913.             } else {
  914.                 if ($aScriptName == '') {
  915.                     Util\JpGraphError::RaiseL(25030); //('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
  916.                 }
  917.                 echo $this->GetHTMLImageMap($aCSIMName) . $this->GetCSIMImgHTML($aCSIMName$aScriptName$aBorder);
  918.             }
  919.         } else {
  920.             $this->Stroke();
  921.         }
  922.     }
  923.     public function StrokeCSIMImage()
  924.     {
  925.         if (@$_GET[_CSIM_DISPLAY] == 1) {
  926.             $this->Stroke();
  927.         }
  928.     }
  929.     public function GetCSIMImgHTML($aCSIMName$aScriptName 'auto'$aBorder 0)
  930.     {
  931.         if ($aScriptName == 'auto') {
  932.             $aScriptName basename($_SERVER['PHP_SELF']);
  933.         }
  934.         $urlarg $this->GetURLArguments(true);
  935.         return '<img src="' $aScriptName '?' $urlarg '" ismap="ismap" usemap="#' $aCSIMName '" height="' $this->img->height '" alt="' $this->iCSIMImgAlt "\" />\n";
  936.     }
  937.     public function GetTextsYMinMax($aY2 false)
  938.     {
  939.         if ($aY2) {
  940.             $txts $this->y2texts;
  941.         } else {
  942.             $txts $this->texts;
  943.         }
  944.         $n   safe_count($txts);
  945.         $min null;
  946.         $max null;
  947.         for ($i 0$i $n; ++$i) {
  948.             if ($txts[$i]->iScalePosY !== null && $txts[$i]->iScalePosX !== null) {
  949.                 if ($min === null) {
  950.                     $min $max $txts[$i]->iScalePosY;
  951.                 } else {
  952.                     $min min($min$txts[$i]->iScalePosY);
  953.                     $max max($max$txts[$i]->iScalePosY);
  954.                 }
  955.             }
  956.         }
  957.         if ($min !== null) {
  958.             return [$min$max];
  959.         }
  960.         return null;
  961.     }
  962.     public function GetTextsXMinMax($aY2 false)
  963.     {
  964.         if ($aY2) {
  965.             $txts $this->y2texts;
  966.         } else {
  967.             $txts $this->texts;
  968.         }
  969.         $n   safe_count($txts);
  970.         $min null;
  971.         $max null;
  972.         for ($i 0$i $n; ++$i) {
  973.             if ($txts[$i]->iScalePosY !== null && $txts[$i]->iScalePosX !== null) {
  974.                 if ($min === null) {
  975.                     $min $max $txts[$i]->iScalePosX;
  976.                 } else {
  977.                     $min min($min$txts[$i]->iScalePosX);
  978.                     $max max($max$txts[$i]->iScalePosX);
  979.                 }
  980.             }
  981.         }
  982.         if ($min !== null) {
  983.             return [$min$max];
  984.         }
  985.         return null;
  986.     }
  987.     public function GetXMinMax()
  988.     {
  989.         list($min$ymin) = $this->plots[0]->Min();
  990.         list($max$ymax) = $this->plots[0]->Max();
  991.         $i 0;
  992.         // Some plots, e.g. PlotLine should not affect the scale
  993.         // and will return (null,null). We should ignore those
  994.         // values.
  995.         while (($min === null || $max === null) && ($i safe_count($this->plots) - 1)) {
  996.             ++$i;
  997.             list($min$ymin) = $this->plots[$i]->Min();
  998.             list($max$ymax) = $this->plots[$i]->Max();
  999.         }
  1000.         foreach ($this->plots as $p) {
  1001.             list($xmin$ymin) = $p->Min();
  1002.             list($xmax$ymax) = $p->Max();
  1003.             if ($xmin !== null && $xmax !== null) {
  1004.                 $min min($xmin$min);
  1005.                 $max max($xmax$max);
  1006.             }
  1007.         }
  1008.         if ($this->y2axis != null) {
  1009.             foreach ($this->y2plots as $p) {
  1010.                 list($xmin$ymin) = $p->Min();
  1011.                 list($xmax$ymax) = $p->Max();
  1012.                 $min               min($xmin$min);
  1013.                 $max               max($xmax$max);
  1014.             }
  1015.         }
  1016.         $n safe_count($this->ynaxis);
  1017.         for ($i 0$i $n; ++$i) {
  1018.             if ($this->ynaxis[$i] != null) {
  1019.                 foreach ($this->ynplots[$i] as $p) {
  1020.                     list($xmin$ymin) = $p->Min();
  1021.                     list($xmax$ymax) = $p->Max();
  1022.                     $min               min($xmin$min);
  1023.                     $max               max($xmax$max);
  1024.                 }
  1025.             }
  1026.         }
  1027.         return [$min$max];
  1028.     }
  1029.     public function AdjustMarginsForTitles()
  1030.     {
  1031.         $totrequired =
  1032.             ($this->title->!= ''
  1033.             $this->title->GetTextHeight($this->img) + $this->title->margin SUPERSAMPLING_SCALE
  1034.             0) +
  1035.             ($this->subtitle->!= ''
  1036.             $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin SUPERSAMPLING_SCALE
  1037.             0) +
  1038.             ($this->subsubtitle->!= ''
  1039.             $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin SUPERSAMPLING_SCALE
  1040.             0);
  1041.         $btotrequired 0;
  1042.         if ($this->xaxis != null && !$this->xaxis->hide && !$this->xaxis->hide_labels) {
  1043.             // Minimum bottom margin
  1044.             if ($this->xaxis->title->!= '') {
  1045.                 if ($this->img->== 90) {
  1046.                     $btotrequired $this->yaxis->title->GetTextHeight($this->img) + 7;
  1047.                 } else {
  1048.                     $btotrequired $this->xaxis->title->GetTextHeight($this->img) + 7;
  1049.                 }
  1050.             } else {
  1051.                 $btotrequired 0;
  1052.             }
  1053.             if ($this->img->== 90) {
  1054.                 $this->img->SetFont(
  1055.                     $this->yaxis->font_family,
  1056.                     $this->yaxis->font_style,
  1057.                     $this->yaxis->font_size
  1058.                 );
  1059.                 $lh $this->img->GetTextHeight('Mg'$this->yaxis->label_angle);
  1060.             } else {
  1061.                 $this->img->SetFont(
  1062.                     $this->xaxis->font_family,
  1063.                     $this->xaxis->font_style,
  1064.                     $this->xaxis->font_size
  1065.                 );
  1066.                 $lh $this->img->GetTextHeight('Mg'$this->xaxis->label_angle);
  1067.             }
  1068.             $btotrequired += $lh 6;
  1069.         }
  1070.         if ($this->img->== 90) {
  1071.             // DO Nothing. It gets too messy to do this properly for 90 deg...
  1072.         } else {
  1073.             // need more top margin
  1074.             if ($this->img->top_margin $totrequired) {
  1075.                 $this->SetMargin(
  1076.                     $this->img->raw_left_margin,
  1077.                     $this->img->raw_right_margin,
  1078.                     $totrequired SUPERSAMPLING_SCALE,
  1079.                     $this->img->raw_bottom_margin
  1080.                 );
  1081.             }
  1082.             // need more bottom margin
  1083.             if ($this->img->bottom_margin $btotrequired) {
  1084.                 $this->SetMargin(
  1085.                     $this->img->raw_left_margin,
  1086.                     $this->img->raw_right_margin,
  1087.                     $this->img->raw_top_margin,
  1088.                     $btotrequired SUPERSAMPLING_SCALE
  1089.                 );
  1090.             }
  1091.         }
  1092.     }
  1093.     public function StrokeStore($aStrokeFileName)
  1094.     {
  1095.         // Get the handler to prevent the library from sending the
  1096.         // image to the browser
  1097.         $ih $this->Stroke(_IMG_HANDLER);
  1098.         // Stroke it to a file
  1099.         $this->img->Stream($aStrokeFileName);
  1100.         // Send it back to browser
  1101.         $this->img->Headers();
  1102.         $this->img->Stream();
  1103.     }
  1104.     public function doAutoscaleXAxis()
  1105.     {
  1106.         $aPlots array_filter($this->plots, function ($plot) {
  1107.             //\Kint::dump($plot, $plot instanceof Plot\Plot);
  1108.             return $plot instanceof Plot\Plot;
  1109.         });
  1110.         //Check if we should autoscale x-axis
  1111.         if (!$this->xscale->IsSpecified()) {
  1112.             if (substr($this->axtype04) == 'text') {
  1113.                 $max 0;
  1114.                 $n   safe_count($aPlots);
  1115.                 for ($i 0$i $n; ++$i) {
  1116.                     $p $aPlots[$i];
  1117.                     // We need some unfortunate sub class knowledge here in order
  1118.                     // to increase number of data points in case it is a line plot
  1119.                     // which has the barcenter set. If not it could mean that the
  1120.                     // last point of the data is outside the scale since the barcenter
  1121.                     // settings means that we will shift the entire plot half a tick step
  1122.                     // to the right in oder to align with the center of the bars.
  1123.                     $cl strtolower(get_class($p));
  1124.                     if (empty($p->barcenter)) {
  1125.                         $max max($max$p->numpoints 1);
  1126.                     } else {
  1127.                         $max max($max$p->numpoints);
  1128.                     }
  1129.                 }
  1130.                 $min 0;
  1131.                 if ($this->y2axis != null) {
  1132.                     foreach ($this->y2plots as $p) {
  1133.                         $max max($max$p->numpoints 1);
  1134.                     }
  1135.                 }
  1136.                 $n safe_count($this->ynaxis);
  1137.                 for ($i 0$i $n; ++$i) {
  1138.                     if ($this->ynaxis[$i] != null) {
  1139.                         foreach ($this->ynplots[$i] as $p) {
  1140.                             $max max($max$p->numpoints 1);
  1141.                         }
  1142.                     }
  1143.                 }
  1144.                 $this->xscale->Update($this->img$min$max);
  1145.                 $this->xscale->ticks->Set($this->xaxis->tick_step1);
  1146.                 $this->xscale->ticks->SupressMinorTickMarks();
  1147.             } else {
  1148.                 list($min$max) = $this->GetXMinMax();
  1149.                 $lres $this->GetLinesXMinMax($this->lines);
  1150.                 if ($lres) {
  1151.                     list($linmin$linmax) = $lres;
  1152.                     $min                   min($min$linmin);
  1153.                     $max                   max($max$linmax);
  1154.                 }
  1155.                 $lres $this->GetLinesXMinMax($this->y2lines);
  1156.                 if ($lres) {
  1157.                     list($linmin$linmax) = $lres;
  1158.                     $min                   min($min$linmin);
  1159.                     $max                   max($max$linmax);
  1160.                 }
  1161.                 $tres $this->GetTextsXMinMax();
  1162.                 if ($tres) {
  1163.                     list($tmin$tmax) = $tres;
  1164.                     $min               min($min$tmin);
  1165.                     $max               max($max$tmax);
  1166.                 }
  1167.                 $tres $this->GetTextsXMinMax(true);
  1168.                 if ($tres) {
  1169.                     list($tmin$tmax) = $tres;
  1170.                     $min               min($min$tmin);
  1171.                     $max               max($max$tmax);
  1172.                 }
  1173.                 $this->xscale->AutoScale($this->img$min$maxround($this->img->plotwidth $this->xtick_factor));
  1174.             }
  1175.             //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
  1176.             if (!is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos)) {
  1177.                 $this->yaxis->SetPos($this->xscale->GetMinVal());
  1178.             }
  1179.         } elseif ($this->xscale->IsSpecified() &&
  1180.             ($this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified())) {
  1181.             // The tick calculation will use the user suplied min/max values to determine
  1182.             // the ticks. If auto_ticks is false the exact user specifed min and max
  1183.             // values will be used for the scale.
  1184.             // If auto_ticks is true then the scale might be slightly adjusted
  1185.             // so that the min and max values falls on an even major step.
  1186.             $min $this->xscale->scale[0];
  1187.             $max $this->xscale->scale[1];
  1188.             $this->xscale->AutoScale($this->img$min$maxround($this->img->plotwidth $this->xtick_factor), false);
  1189.             // Now make sure we show enough precision to accurate display the
  1190.             // labels. If this is not done then the user might end up with
  1191.             // a scale that might actually start with, say 13.5, butdue to rounding
  1192.             // the scale label will ony show 14.
  1193.             if (abs(floor($min) - $min) > 0) {
  1194.                 // If the user has set a format then we bail out
  1195.                 if ($this->xscale->ticks->label_formatstr == '' && $this->xscale->ticks->label_dateformatstr == '') {
  1196.                     $this->xscale->ticks->precision abs(floor(log10(abs(floor($min) - $min)))) + 1;
  1197.                 }
  1198.             }
  1199.         }
  1200.         // Position the optional Y2 and Yn axis to the rightmost position of the x-axis
  1201.         if ($this->y2axis != null) {
  1202.             if (!is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos)) {
  1203.                 $this->y2axis->SetPos($this->xscale->GetMaxVal());
  1204.             }
  1205.             $this->y2axis->SetTitleSide(SIDE_RIGHT);
  1206.         }
  1207.         $n      safe_count($this->ynaxis);
  1208.         $nY2adj $this->y2axis != null $this->iYAxisDeltaPos 0;
  1209.         for ($i 0$i $n; ++$i) {
  1210.             if ($this->ynaxis[$i] != null) {
  1211.                 if (!is_numeric($this->ynaxis[$i]->pos) && !is_string($this->ynaxis[$i]->pos)) {
  1212.                     $this->ynaxis[$i]->SetPos($this->xscale->GetMaxVal());
  1213.                     $this->ynaxis[$i]->SetPosAbsDelta($i $this->iYAxisDeltaPos $nY2adj);
  1214.                 }
  1215.                 $this->ynaxis[$i]->SetTitleSide(SIDE_RIGHT);
  1216.             }
  1217.         }
  1218.     }
  1219.     public function doAutoScaleYnAxis()
  1220.     {
  1221.         if ($this->y2scale != null) {
  1222.             if (!$this->y2scale->IsSpecified() && safe_count($this->y2plots) > 0) {
  1223.                 list($min$max) = $this->GetPlotsYMinMax($this->y2plots);
  1224.                 $lres $this->GetLinesYMinMax($this->y2lines);
  1225.                 if (is_array($lres)) {
  1226.                     list($linmin$linmax) = $lres;
  1227.                     $min                   min($min$linmin);
  1228.                     $max                   max($max$linmax);
  1229.                 }
  1230.                 $tres $this->GetTextsYMinMax(true);
  1231.                 if (is_array($tres)) {
  1232.                     list($tmin$tmax) = $tres;
  1233.                     $min               min($min$tmin);
  1234.                     $max               max($max$tmax);
  1235.                 }
  1236.                 $this->y2scale->AutoScale($this->img$min$max$this->img->plotheight $this->ytick_factor);
  1237.             } elseif ($this->y2scale->IsSpecified() && ($this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified())) {
  1238.                 // The tick calculation will use the user suplied min/max values to determine
  1239.                 // the ticks. If auto_ticks is false the exact user specifed min and max
  1240.                 // values will be used for the scale.
  1241.                 // If auto_ticks is true then the scale might be slightly adjusted
  1242.                 // so that the min and max values falls on an even major step.
  1243.                 $min $this->y2scale->scale[0];
  1244.                 $max $this->y2scale->scale[1];
  1245.                 $this->y2scale->AutoScale(
  1246.                     $this->img,
  1247.                     $min,
  1248.                     $max,
  1249.                     $this->img->plotheight $this->ytick_factor,
  1250.                     $this->y2scale->auto_ticks
  1251.                 );
  1252.                 // Now make sure we show enough precision to accurate display the
  1253.                 // labels. If this is not done then the user might end up with
  1254.                 // a scale that might actually start with, say 13.5, butdue to rounding
  1255.                 // the scale label will ony show 14.
  1256.                 if (abs(floor($min) - $min) > 0) {
  1257.                     // If the user has set a format then we bail out
  1258.                     if ($this->y2scale->ticks->label_formatstr == '' && $this->y2scale->ticks->label_dateformatstr == '') {
  1259.                         $this->y2scale->ticks->precision abs(floor(log10(abs(floor($min) - $min)))) + 1;
  1260.                     }
  1261.                 }
  1262.             }
  1263.         }
  1264.         //
  1265.         // Autoscale the extra Y-axises
  1266.         //
  1267.         $n safe_count($this->ynaxis);
  1268.         for ($i 0$i $n; ++$i) {
  1269.             if ($this->ynscale[$i] != null) {
  1270.                 if (!$this->ynscale[$i]->IsSpecified() && safe_count($this->ynplots[$i]) > 0) {
  1271.                     list($min$max) = $this->GetPlotsYMinMax($this->ynplots[$i]);
  1272.                     $this->ynscale[$i]->AutoScale($this->img$min$max$this->img->plotheight $this->ytick_factor);
  1273.                 } elseif ($this->ynscale[$i]->IsSpecified() && ($this->ynscale[$i]->auto_ticks || !$this->ynscale[$i]->ticks->IsSpecified())) {
  1274.                     // The tick calculation will use the user suplied min/max values to determine
  1275.                     // the ticks. If auto_ticks is false the exact user specifed min and max
  1276.                     // values will be used for the scale.
  1277.                     // If auto_ticks is true then the scale might be slightly adjusted
  1278.                     // so that the min and max values falls on an even major step.
  1279.                     $min $this->ynscale[$i]->scale[0];
  1280.                     $max $this->ynscale[$i]->scale[1];
  1281.                     $this->ynscale[$i]->AutoScale(
  1282.                         $this->img,
  1283.                         $min,
  1284.                         $max,
  1285.                         $this->img->plotheight $this->ytick_factor,
  1286.                         $this->ynscale[$i]->auto_ticks
  1287.                     );
  1288.                     // Now make sure we show enough precision to accurate display the
  1289.                     // labels. If this is not done then the user might end up with
  1290.                     // a scale that might actually start with, say 13.5, butdue to rounding
  1291.                     // the scale label will ony show 14.
  1292.                     if (abs(floor($min) - $min) > 0) {
  1293.                         // If the user has set a format then we bail out
  1294.                         if ($this->ynscale[$i]->ticks->label_formatstr == '' && $this->ynscale[$i]->ticks->label_dateformatstr == '') {
  1295.                             $this->ynscale[$i]->ticks->precision abs(floor(log10(abs(floor($min) - $min)))) + 1;
  1296.                         }
  1297.                     }
  1298.                 }
  1299.             }
  1300.         }
  1301.     }
  1302.     public function doAutoScaleYAxis()
  1303.     {
  1304.         //Check if we should autoscale y-axis
  1305.         if (!$this->yscale->IsSpecified() && safe_count($this->plots) > 0) {
  1306.             list($min$max) = $this->GetPlotsYMinMax($this->plots);
  1307.             $lres            $this->GetLinesYMinMax($this->lines);
  1308.             if (is_array($lres)) {
  1309.                 list($linmin$linmax) = $lres;
  1310.                 $min                   min($min$linmin);
  1311.                 $max                   max($max$linmax);
  1312.             }
  1313.             $tres $this->GetTextsYMinMax();
  1314.             if (is_array($tres)) {
  1315.                 list($tmin$tmax) = $tres;
  1316.                 $min               min($min$tmin);
  1317.                 $max               max($max$tmax);
  1318.             }
  1319.             $this->yscale->AutoScale(
  1320.                 $this->img,
  1321.                 $min,
  1322.                 $max,
  1323.                 $this->img->plotheight $this->ytick_factor
  1324.             );
  1325.         } elseif ($this->yscale->IsSpecified() && ($this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified())) {
  1326.             // The tick calculation will use the user suplied min/max values to determine
  1327.             // the ticks. If auto_ticks is false the exact user specifed min and max
  1328.             // values will be used for the scale.
  1329.             // If auto_ticks is true then the scale might be slightly adjusted
  1330.             // so that the min and max values falls on an even major step.
  1331.             $min $this->yscale->scale[0];
  1332.             $max $this->yscale->scale[1];
  1333.             $this->yscale->AutoScale(
  1334.                 $this->img,
  1335.                 $min,
  1336.                 $max,
  1337.                 $this->img->plotheight $this->ytick_factor,
  1338.                 $this->yscale->auto_ticks
  1339.             );
  1340.             // Now make sure we show enough precision to accurate display the
  1341.             // labels. If this is not done then the user might end up with
  1342.             // a scale that might actually start with, say 13.5, butdue to rounding
  1343.             // the scale label will ony show 14.
  1344.             if (abs(floor($min) - $min) > 0) {
  1345.                 // If the user has set a format then we bail out
  1346.                 if ($this->yscale->ticks->label_formatstr == '' && $this->yscale->ticks->label_dateformatstr == '') {
  1347.                     $this->yscale->ticks->precision abs(floor(log10(abs(floor($min) - $min)))) + 1;
  1348.                 }
  1349.             }
  1350.         }
  1351.     }
  1352.     public function InitScaleConstants()
  1353.     {
  1354.         // Setup scale constants
  1355.         if ($this->yscale) {
  1356.             $this->yscale->InitConstants($this->img);
  1357.         }
  1358.         if ($this->xscale) {
  1359.             $this->xscale->InitConstants($this->img);
  1360.         }
  1361.         if ($this->y2scale) {
  1362.             $this->y2scale->InitConstants($this->img);
  1363.         }
  1364.         $n safe_count($this->ynscale);
  1365.         for ($i 0$i $n; ++$i) {
  1366.             if ($this->ynscale[$i]) {
  1367.                 $this->ynscale[$i]->InitConstants($this->img);
  1368.             }
  1369.         }
  1370.     }
  1371.     public function doPrestrokeAdjustments()
  1372.     {
  1373.         // Do any pre-stroke adjustment that is needed by the different plot types
  1374.         // (i.e bar plots want's to add an offset to the x-labels etc)
  1375.         for ($i 0$i safe_count($this->plots); ++$i) {
  1376.             if ($this->plots[$i] instanceof Plot\Plot) {
  1377.                 $this->plots[$i]->PreStrokeAdjust($this);
  1378.                 $this->plots[$i]->DoLegend($this);
  1379.             }
  1380.         }
  1381.         // Any plots on the second Y scale?
  1382.         if ($this->y2scale != null) {
  1383.             for ($i 0$i safe_count($this->y2plots); ++$i) {
  1384.                 if ($this->plots[$i] instanceof Plot\Plot) {
  1385.                     $this->y2plots[$i]->PreStrokeAdjust($this);
  1386.                     $this->y2plots[$i]->DoLegend($this);
  1387.                 }
  1388.             }
  1389.         }
  1390.         // Any plots on the extra Y axises?
  1391.         $n safe_count($this->ynaxis);
  1392.         for ($i 0$i $n; ++$i) {
  1393.             if ($this->ynplots == null || $this->ynplots[$i] == null) {
  1394.                 Util\JpGraphError::RaiseL(25032$i); //("No plots for Y-axis nbr:$i");
  1395.             }
  1396.             $m safe_count($this->ynplots[$i]);
  1397.             for ($j 0$j $m; ++$j) {
  1398.                 $this->ynplots[$i][$j]->PreStrokeAdjust($this);
  1399.                 $this->ynplots[$i][$j]->DoLegend($this);
  1400.             }
  1401.         }
  1402.     }
  1403.     public function StrokeBands($aDepth$aCSIM)
  1404.     {
  1405.         // Stroke bands
  1406.         if ($this->bands != null && !$aCSIM) {
  1407.             for ($i 0$i safe_count($this->bands); ++$i) {
  1408.                 // Stroke all bands that asks to be in the background
  1409.                 if ($this->bands[$i]->depth == $aDepth) {
  1410.                     $this->bands[$i]->Stroke($this->img$this->xscale$this->yscale);
  1411.                 }
  1412.             }
  1413.         }
  1414.         if ($this->y2bands != null && $this->y2scale != null && !$aCSIM) {
  1415.             for ($i 0$i safe_count($this->y2bands); ++$i) {
  1416.                 // Stroke all bands that asks to be in the foreground
  1417.                 if ($this->y2bands[$i]->depth == $aDepth) {
  1418.                     $this->y2bands[$i]->Stroke($this->img$this->xscale$this->y2scale);
  1419.                 }
  1420.             }
  1421.         }
  1422.     }
  1423.     // Stroke the graph
  1424.     // $aStrokeFileName If != "" the image will be written to this file and NOT
  1425.     // streamed back to the browser
  1426.     public function Stroke($aStrokeFileName '')
  1427.     {
  1428.         // Fist make a sanity check that user has specified a scale
  1429.         if (empty($this->yscale)) {
  1430.             Util\JpGraphError::RaiseL(25031); //('You must specify what scale to use with a call to Graph::SetScale().');
  1431.         }
  1432.         // Start by adjusting the margin so that potential titles will fit.
  1433.         $this->AdjustMarginsForTitles();
  1434.         // Give the plot a chance to do any scale adjuments the individual plots
  1435.         // wants to do. Right now this is only used by the contour plot to set scale
  1436.         // limits
  1437.         for ($i 0$i safe_count($this->plots); ++$i) {
  1438.             if ($this->plots[$i] instanceof Plot\Plot) {
  1439.                 $this->plots[$i]->PreScaleSetup($this);
  1440.             }
  1441.             //\Kint::dump($this->plots[$i]);
  1442.         }
  1443.         // Init scale constants that are used to calculate the transformation from
  1444.         // world to pixel coordinates
  1445.         $this->InitScaleConstants();
  1446.         // If the filename is the predefined value = '_csim_special_'
  1447.         // we assume that the call to stroke only needs to do enough
  1448.         // to correctly generate the CSIM maps.
  1449.         // We use this variable to skip things we don't strictly need
  1450.         // to do to generate the image map to improve performance
  1451.         // a best we can. Therefor you will see a lot of tests !$_csim in the
  1452.         // code below.
  1453.         $_csim = ($aStrokeFileName === _CSIM_SPECIALFILE);
  1454.         // If we are called the second time (perhaps the user has called GetHTMLImageMap()
  1455.         // himself then the legends have alsready been populated once in order to get the
  1456.         // CSIM coordinats. Since we do not want the legends to be populated a second time
  1457.         // we clear the legends
  1458.         $this->legend->Clear();
  1459.         // We need to know if we have stroked the plot in the
  1460.         // GetCSIMareas. Otherwise the CSIM hasn't been generated
  1461.         // and in the case of GetCSIM called before stroke to generate
  1462.         // CSIM without storing an image to disk GetCSIM must call Stroke.
  1463.         $this->iHasStroked true;
  1464.         // Setup pre-stroked adjustments and Legends
  1465.         $this->doPrestrokeAdjustments();
  1466.         if ($this->graph_theme) {
  1467.             $this->graph_theme->PreStrokeApply($this);
  1468.         }
  1469.         // Bail out if any of the Y-axis not been specified and
  1470.         // has no plots. (This means it is impossible to do autoscaling and
  1471.         // no other scale was given so we can't possible draw anything). If you use manual
  1472.         // scaling you also have to supply the tick steps as well.
  1473.         if ((!$this->yscale->IsSpecified() && safe_count($this->plots) == 0) ||
  1474.             ($this->y2scale != null && !$this->y2scale->IsSpecified() && safe_count($this->y2plots) == 0)) {
  1475.             //$e = "n=". safe_count($this->y2plots)."\n";
  1476.             // $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
  1477.             // $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
  1478.             // $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
  1479.             Util\JpGraphError::RaiseL(25026);
  1480.         }
  1481.         // Bail out if no plots and no specified X-scale
  1482.         if ((!$this->xscale->IsSpecified() && safe_count($this->plots) == && safe_count($this->y2plots) == 0)) {
  1483.             Util\JpGraphError::RaiseL(25034); //("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
  1484.         }
  1485.         // Autoscale the normal Y-axis
  1486.         $this->doAutoScaleYAxis();
  1487.         // Autoscale all additiopnal y-axis
  1488.         $this->doAutoScaleYnAxis();
  1489.         // Autoscale the regular x-axis and position the y-axis properly
  1490.         $this->doAutoScaleXAxis();
  1491.         // If we have a negative values and x-axis position is at 0
  1492.         // we need to supress the first and possible the last tick since
  1493.         // they will be drawn on top of the y-axis (and possible y2 axis)
  1494.         // The test below might seem strange the reasone being that if
  1495.         // the user hasn't specified a value for position this will not
  1496.         // be set until we do the stroke for the axis so as of now it
  1497.         // is undefined.
  1498.         // For X-text scale we ignore all this since the tick are usually
  1499.         // much further in and not close to the Y-axis. Hence the test
  1500.         // for 'text'
  1501.         if (($this->yaxis->pos == $this->xscale->GetMinVal() || (is_string($this->yaxis->pos) && $this->yaxis->pos == 'min')) &&
  1502.             !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < &&
  1503.             substr($this->axtype04) != 'text' && $this->xaxis->pos != 'min') {
  1504.             //$this->yscale->ticks->SupressZeroLabel(false);
  1505.             $this->xscale->ticks->SupressFirst();
  1506.             if ($this->y2axis != null) {
  1507.                 $this->xscale->ticks->SupressLast();
  1508.             }
  1509.         } elseif (!is_numeric($this->yaxis->pos) && $this->yaxis->pos == 'max') {
  1510.             $this->xscale->ticks->SupressLast();
  1511.         }
  1512.         if (!$_csim) {
  1513.             $this->StrokePlotArea();
  1514.             if ($this->iIconDepth == DEPTH_BACK) {
  1515.                 $this->StrokeIcons();
  1516.             }
  1517.         }
  1518.         $this->StrokeAxis(false);
  1519.         // Stroke colored bands
  1520.         $this->StrokeBands(DEPTH_BACK$_csim);
  1521.         if ($this->grid_depth == DEPTH_BACK && !$_csim) {
  1522.             $this->ygrid->Stroke();
  1523.             $this->xgrid->Stroke();
  1524.         }
  1525.         // Stroke Y2-axis
  1526.         if ($this->y2axis != null && !$_csim) {
  1527.             $this->y2axis->Stroke($this->xscale);
  1528.             $this->y2grid->Stroke();
  1529.         }
  1530.         // Stroke yn-axis
  1531.         $n safe_count($this->ynaxis);
  1532.         for ($i 0$i $n; ++$i) {
  1533.             $this->ynaxis[$i]->Stroke($this->xscale);
  1534.         }
  1535.         $oldoff $this->xscale->off;
  1536.         if (substr($this->axtype04) == 'text') {
  1537.             if ($this->text_scale_abscenteroff > -1) {
  1538.                 // For a text scale the scale factor is the number of pixel per step.
  1539.                 // Hence we can use the scale factor as a substitute for number of pixels
  1540.                 // per major scale step and use that in order to adjust the offset so that
  1541.                 // an object of width "abscenteroff" becomes centered.
  1542.                 $this->xscale->off += round($this->xscale->scale_factor 2) - round($this->text_scale_abscenteroff 2);
  1543.             } else {
  1544.                 $this->xscale->off += ceil($this->xscale->scale_factor $this->text_scale_off $this->xscale->ticks->minor_step);
  1545.             }
  1546.         }
  1547.         if ($this->iDoClipping) {
  1548.             $oldimage $this->img->CloneCanvasH();
  1549.         }
  1550.         if (!$this->y2orderback) {
  1551.             // Stroke all plots for Y1 axis
  1552.             for ($i 0$i safe_count($this->plots); ++$i) {
  1553.                 $this->plots[$i]->Stroke($this->img$this->xscale$this->yscale);
  1554.                 if ($this->plots[$i] instanceof Plot\Plot) {
  1555.                     $this->plots[$i]->StrokeMargin($this->img);
  1556.                 }
  1557.             }
  1558.         }
  1559.         // Stroke all plots for Y2 axis
  1560.         if ($this->y2scale != null) {
  1561.             for ($i 0$i safe_count($this->y2plots); ++$i) {
  1562.                 $this->y2plots[$i]->Stroke($this->img$this->xscale$this->y2scale);
  1563.             }
  1564.         }
  1565.         if ($this->y2orderback) {
  1566.             // Stroke all plots for Y1 axis
  1567.             for ($i 0$i safe_count($this->plots); ++$i) {
  1568.                 $this->plots[$i]->Stroke($this->img$this->xscale$this->yscale);
  1569.                 if ($this->plots[$i] instanceof Plot\Plot) {
  1570.                     $this->plots[$i]->StrokeMargin($this->img);
  1571.                 }
  1572.             }
  1573.         }
  1574.         $n safe_count($this->ynaxis);
  1575.         for ($i 0$i $n; ++$i) {
  1576.             $m safe_count($this->ynplots[$i]);
  1577.             for ($j 0$j $m; ++$j) {
  1578.                 $this->ynplots[$i][$j]->Stroke($this->img$this->xscale$this->ynscale[$i]);
  1579.                 if ($this->ynplots[$i][$j] instanceof Plot\Plot) {
  1580.                     $this->ynplots[$i][$j]->StrokeMargin($this->img);
  1581.                 }
  1582.             }
  1583.         }
  1584.         if ($this->iIconDepth == DEPTH_FRONT) {
  1585.             $this->StrokeIcons();
  1586.         }
  1587.         if ($this->iDoClipping) {
  1588.             // Clipping only supports graphs at 0 and 90 degrees
  1589.             if ($this->img->== 0) {
  1590.                 $this->img->CopyCanvasH(
  1591.                     $oldimage,
  1592.                     $this->img->img,
  1593.                     $this->img->left_margin,
  1594.                     $this->img->top_margin,
  1595.                     $this->img->left_margin,
  1596.                     $this->img->top_margin,
  1597.                     $this->img->plotwidth 1,
  1598.                     $this->img->plotheight
  1599.                 );
  1600.             } elseif ($this->img->== 90) {
  1601.                 $adj = ($this->img->height $this->img->width) / 2;
  1602.                 $this->img->CopyCanvasH(
  1603.                     $oldimage,
  1604.                     $this->img->img,
  1605.                     $this->img->bottom_margin $adj,
  1606.                     $this->img->left_margin $adj,
  1607.                     $this->img->bottom_margin $adj,
  1608.                     $this->img->left_margin $adj,
  1609.                     $this->img->plotheight 1,
  1610.                     $this->img->plotwidth
  1611.                 );
  1612.             } else {
  1613.                 Util\JpGraphError::RaiseL(25035$this->img->a); //('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
  1614.             }
  1615.             $this->img->Destroy();
  1616.             $this->img->SetCanvasH($oldimage);
  1617.         }
  1618.         $this->xscale->off $oldoff;
  1619.         if ($this->grid_depth == DEPTH_FRONT && !$_csim) {
  1620.             $this->ygrid->Stroke();
  1621.             $this->xgrid->Stroke();
  1622.         }
  1623.         // Stroke colored bands
  1624.         $this->StrokeBands(DEPTH_FRONT$_csim);
  1625.         // Finally draw the axis again since some plots may have nagged
  1626.         // the axis in the edges.
  1627.         if (!$_csim) {
  1628.             $this->StrokeAxis();
  1629.         }
  1630.         if ($this->y2scale != null && !$_csim) {
  1631.             $this->y2axis->Stroke($this->xscalefalse);
  1632.         }
  1633.         if (!$_csim) {
  1634.             $this->StrokePlotBox();
  1635.         }
  1636.         // The titles and legends never gets rotated so make sure
  1637.         // that the angle is 0 before stroking them
  1638.         $aa $this->img->SetAngle(0);
  1639.         $this->StrokeTitles();
  1640.         $this->footer->Stroke($this->img);
  1641.         $this->legend->Stroke($this->img);
  1642.         $this->img->SetAngle($aa);
  1643.         $this->StrokeTexts();
  1644.         $this->StrokeTables();
  1645.         if (!$_csim) {
  1646.             $this->img->SetAngle($aa);
  1647.             // Draw an outline around the image map
  1648.             if (_JPG_DEBUG) {
  1649.                 $this->DisplayClientSideaImageMapAreas();
  1650.             }
  1651.             // Should we do any final image transformation
  1652.             if ($this->iImgTrans) {
  1653.                 $tform          = new Image\ImgTrans($this->img->img);
  1654.                 $this->img->img $tform->Skew3D(
  1655.                     $this->iImgTransHorizon,
  1656.                     $this->iImgTransSkewDist,
  1657.                     $this->iImgTransDirection,
  1658.                     $this->iImgTransHighQ,
  1659.                     $this->iImgTransMinSize,
  1660.                     $this->iImgTransFillColor,
  1661.                     $this->iImgTransBorder
  1662.                 );
  1663.             }
  1664.             // If the filename is given as the special "__handle"
  1665.             // then the image handler is returned and the image is NOT
  1666.             // streamed back
  1667.             if ($aStrokeFileName == _IMG_HANDLER) {
  1668.                 return $this->img->img;
  1669.             }
  1670.             // Finally stream the generated picture
  1671.             $this->cache->PutAndStream($this->img$this->cache_name$this->inline$aStrokeFileName);
  1672.         }
  1673.     }
  1674.     public function SetAxisLabelBackground($aType$aXFColor 'lightgray'$aXColor 'black'$aYFColor 'lightgray'$aYColor 'black')
  1675.     {
  1676.         $this->iAxisLblBgType       $aType;
  1677.         $this->iXAxisLblBgFillColor $aXFColor;
  1678.         $this->iXAxisLblBgColor     $aXColor;
  1679.         $this->iYAxisLblBgFillColor $aYFColor;
  1680.         $this->iYAxisLblBgColor     $aYColor;
  1681.     }
  1682.     public function StrokeAxisLabelBackground()
  1683.     {
  1684.         // Types
  1685.         // 0 = No background
  1686.         // 1 = Only X-labels, length of axis
  1687.         // 2 = Only Y-labels, length of axis
  1688.         // 3 = As 1 but extends to width of graph
  1689.         // 4 = As 2 but extends to height of graph
  1690.         // 5 = Combination of 3 & 4
  1691.         // 6 = Combination of 1 & 2
  1692.         $t $this->iAxisLblBgType;
  1693.         if ($t 1) {
  1694.             return;
  1695.         }
  1696.         // Stroke optional X-axis label background color
  1697.         if ($t == || $t == || $t == || $t == 6) {
  1698.             $this->img->PushColor($this->iXAxisLblBgFillColor);
  1699.             if ($t == || $t == 6) {
  1700.                 $xl $this->img->left_margin;
  1701.                 $yu $this->img->height $this->img->bottom_margin 1;
  1702.                 $xr $this->img->width $this->img->right_margin;
  1703.                 $yl $this->img->height $this->frame_weight;
  1704.             } else {
  1705.                 // t==3 || t==5
  1706.                 $xl $this->frame_weight;
  1707.                 $yu $this->img->height $this->img->bottom_margin 1;
  1708.                 $xr $this->img->width $this->frame_weight;
  1709.                 $yl $this->img->height $this->frame_weight;
  1710.             }
  1711.             $this->img->FilledRectangle($xl$yu$xr$yl);
  1712.             $this->img->PopColor();
  1713.             // Check if we should add the vertical lines at left and right edge
  1714.             if ($this->iXAxisLblBgColor !== '') {
  1715.                 // Hardcode to one pixel wide
  1716.                 $this->img->SetLineWeight(1);
  1717.                 $this->img->PushColor($this->iXAxisLblBgColor);
  1718.                 if ($t == || $t == 6) {
  1719.                     $this->img->Line($xl$yu$xl$yl);
  1720.                     $this->img->Line($xr$yu$xr$yl);
  1721.                 } else {
  1722.                     $xl $this->img->width $this->img->right_margin;
  1723.                     $this->img->Line($xl$yu 1$xr$yu 1);
  1724.                 }
  1725.                 $this->img->PopColor();
  1726.             }
  1727.         }
  1728.         if ($t == || $t == || $t == || $t == 6) {
  1729.             $this->img->PushColor($this->iYAxisLblBgFillColor);
  1730.             if ($t == || $t == 6) {
  1731.                 $xl $this->frame_weight;
  1732.                 $yu $this->frame_weight $this->img->top_margin;
  1733.                 $xr $this->img->left_margin 1;
  1734.                 $yl $this->img->height $this->img->bottom_margin 1;
  1735.             } else {
  1736.                 $xl $this->frame_weight;
  1737.                 $yu $this->frame_weight;
  1738.                 $xr $this->img->left_margin 1;
  1739.                 $yl $this->img->height $this->frame_weight;
  1740.             }
  1741.             $this->img->FilledRectangle($xl$yu$xr$yl);
  1742.             $this->img->PopColor();
  1743.             // Check if we should add the vertical lines at left and right edge
  1744.             if ($this->iXAxisLblBgColor !== '') {
  1745.                 $this->img->PushColor($this->iXAxisLblBgColor);
  1746.                 if ($t == || $t == 6) {
  1747.                     $this->img->Line($xl$yu 1$xr$yu 1);
  1748.                     $this->img->Line($xl$yl 1$xr$yl 1);
  1749.                 } else {
  1750.                     $this->img->Line($xr 1$yu$xr 1$this->img->top_margin);
  1751.                 }
  1752.                 $this->img->PopColor();
  1753.             }
  1754.         }
  1755.     }
  1756.     public function StrokeAxis($aStrokeLabels true)
  1757.     {
  1758.         if ($aStrokeLabels) {
  1759.             $this->StrokeAxisLabelBackground();
  1760.         }
  1761.         // Stroke axis
  1762.         if ($this->iAxisStyle != AXSTYLE_SIMPLE) {
  1763.             switch ($this->iAxisStyle) {
  1764.                 case AXSTYLE_BOXIN:
  1765.                     $toppos    SIDE_DOWN;
  1766.                     $bottompos SIDE_UP;
  1767.                     $leftpos   SIDE_RIGHT;
  1768.                     $rightpos  SIDE_LEFT;
  1769.                     break;
  1770.                 case AXSTYLE_BOXOUT:
  1771.                     $toppos    SIDE_UP;
  1772.                     $bottompos SIDE_DOWN;
  1773.                     $leftpos   SIDE_LEFT;
  1774.                     $rightpos  SIDE_RIGHT;
  1775.                     break;
  1776.                 case AXSTYLE_YBOXIN:
  1777.                     $toppos    false;
  1778.                     $bottompos SIDE_UP;
  1779.                     $leftpos   SIDE_RIGHT;
  1780.                     $rightpos  SIDE_LEFT;
  1781.                     break;
  1782.                 case AXSTYLE_YBOXOUT:
  1783.                     $toppos    false;
  1784.                     $bottompos SIDE_DOWN;
  1785.                     $leftpos   SIDE_LEFT;
  1786.                     $rightpos  SIDE_RIGHT;
  1787.                     break;
  1788.                 default:
  1789.                     Util\JpGraphError::RaiseL(25036$this->iAxisStyle); //('Unknown AxisStyle() : '.$this->iAxisStyle);
  1790.                     break;
  1791.             }
  1792.             // By default we hide the first label so it doesn't cross the
  1793.             // Y-axis in case the positon hasn't been set by the user.
  1794.             // However, if we use a box we always want the first value
  1795.             // displayed so we make sure it will be displayed.
  1796.             $this->xscale->ticks->SupressFirst(false);
  1797.             // Now draw the bottom X-axis
  1798.             $this->xaxis->SetPos('min');
  1799.             $this->xaxis->SetLabelSide(SIDE_DOWN);
  1800.             $this->xaxis->scale->ticks->SetSide($bottompos);
  1801.             $this->xaxis->Stroke($this->yscale$aStrokeLabels);
  1802.             if ($toppos !== false) {
  1803.                 // We also want a top X-axis
  1804.                 $this->xaxis $this->xaxis;
  1805.                 $this->xaxis->SetPos('max');
  1806.                 $this->xaxis->SetLabelSide(SIDE_UP);
  1807.                 // No title for the top X-axis
  1808.                 if ($aStrokeLabels) {
  1809.                     $this->xaxis->title->Set('');
  1810.                 }
  1811.                 $this->xaxis->scale->ticks->SetSide($toppos);
  1812.                 $this->xaxis->Stroke($this->yscale$aStrokeLabels);
  1813.             }
  1814.             // Stroke the left Y-axis
  1815.             $this->yaxis->SetPos('min');
  1816.             $this->yaxis->SetLabelSide(SIDE_LEFT);
  1817.             $this->yaxis->scale->ticks->SetSide($leftpos);
  1818.             $this->yaxis->Stroke($this->xscale$aStrokeLabels);
  1819.             // Stroke the  right Y-axis
  1820.             $this->yaxis->SetPos('max');
  1821.             // No title for the right side
  1822.             if ($aStrokeLabels) {
  1823.                 $this->yaxis->title->Set('');
  1824.             }
  1825.             $this->yaxis->SetLabelSide(SIDE_RIGHT);
  1826.             $this->yaxis->scale->ticks->SetSide($rightpos);
  1827.             $this->yaxis->Stroke($this->xscale$aStrokeLabels);
  1828.         } else {
  1829.             $this->xaxis->Stroke($this->yscale$aStrokeLabels);
  1830.             $this->yaxis->Stroke($this->xscale$aStrokeLabels);
  1831.         }
  1832.     }
  1833.     // Private helper function for backgound image
  1834.     public static function LoadBkgImage($aImgFormat ''$aFile ''$aImgStr '')
  1835.     {
  1836.         if ($aImgStr != '') {
  1837.             return Image::CreateFromString($aImgStr);
  1838.         }
  1839.         // Remove case sensitivity and setup appropriate function to create image
  1840.         // Get file extension. This should be the LAST '.' separated part of the filename
  1841.         $e   explode('.'$aFile);
  1842.         $ext strtolower($e[count($e) - 1]);
  1843.         if ($ext == 'jpeg') {
  1844.             $ext 'jpg';
  1845.         }
  1846.         if (trim($ext) == '') {
  1847.             $ext 'png'// Assume PNG if no extension specified
  1848.         }
  1849.         if ($aImgFormat == '') {
  1850.             $imgtag $ext;
  1851.         } else {
  1852.             $imgtag $aImgFormat;
  1853.         }
  1854.         $supported imagetypes();
  1855.         if (($ext == 'jpg' && !($supported IMG_JPG)) ||
  1856.             ($ext == 'gif' && !($supported IMG_GIF)) ||
  1857.             ($ext == 'png' && !($supported IMG_PNG)) ||
  1858.             ($ext == 'bmp' && !($supported IMG_WBMP)) ||
  1859.             ($ext == 'xpm' && !($supported IMG_XPM))) {
  1860.             Util\JpGraphError::RaiseL(25037$aFile); //('The image format of your background image ('.$aFile.') is not supported in your system configuration. ');
  1861.         }
  1862.         if ($imgtag == 'jpg' || $imgtag == 'jpeg') {
  1863.             $f      'imagecreatefromjpeg';
  1864.             $imgtag 'jpg';
  1865.         } else {
  1866.             $f 'imagecreatefrom' $imgtag;
  1867.         }
  1868.         // Compare specified image type and file extension
  1869.         if ($imgtag != $ext) {
  1870.             //$t = "Background image seems to be of different type (has different file extension) than specified imagetype. Specified: '".$aImgFormat."'File: '".$aFile."'";
  1871.             Util\JpGraphError::RaiseL(25038$aImgFormat$aFile);
  1872.         }
  1873.         $img = @$f($aFile);
  1874.         if (!$img) {
  1875.             Util\JpGraphError::RaiseL(25039$aFile); //(" Can't read background image: '".$aFile."'");
  1876.         }
  1877.         return $img;
  1878.     }
  1879.     public function StrokePlotGrad()
  1880.     {
  1881.         if ($this->plot_gradtype 0) {
  1882.             return;
  1883.         }
  1884.         $grad = new Plot\Gradient($this->img);
  1885.         $xl   $this->img->left_margin;
  1886.         $yt   $this->img->top_margin;
  1887.         $xr   $xl $this->img->plotwidth 1;
  1888.         $yb   $yt $this->img->plotheight;
  1889.         $grad->FilledRectangle($xl$yt$xr$yb$this->plot_gradfrom$this->plot_gradto$this->plot_gradtype);
  1890.     }
  1891.     public function StrokeBackgroundGrad()
  1892.     {
  1893.         if ($this->bkg_gradtype 0) {
  1894.             return;
  1895.         }
  1896.         $grad = new Plot\Gradient($this->img);
  1897.         if ($this->bkg_gradstyle == BGRAD_PLOT) {
  1898.             $xl $this->img->left_margin;
  1899.             $yt $this->img->top_margin;
  1900.             $xr $xl $this->img->plotwidth 1;
  1901.             $yb $yt $this->img->plotheight;
  1902.             $grad->FilledRectangle($xl$yt$xr$yb$this->bkg_gradfrom$this->bkg_gradto$this->bkg_gradtype);
  1903.         } else {
  1904.             $xl 0;
  1905.             $yt 0;
  1906.             $xr $xl $this->img->width 1;
  1907.             $yb $yt $this->img->height 1;
  1908.             if ($this->doshadow) {
  1909.                 $xr -= $this->shadow_width;
  1910.                 $yb -= $this->shadow_width;
  1911.             }
  1912.             if ($this->doframe) {
  1913.                 $yt += $this->frame_weight;
  1914.                 $yb -= $this->frame_weight;
  1915.                 $xl += $this->frame_weight;
  1916.                 $xr -= $this->frame_weight;
  1917.             }
  1918.             $aa $this->img->SetAngle(0);
  1919.             $grad->FilledRectangle($xl$yt$xr$yb$this->bkg_gradfrom$this->bkg_gradto$this->bkg_gradtype);
  1920.             $aa $this->img->SetAngle($aa);
  1921.         }
  1922.     }
  1923.     public function StrokeFrameBackground()
  1924.     {
  1925.         if ($this->background_image != '' && $this->background_cflag != '') {
  1926.             Util\JpGraphError::RaiseL(25040); //('It is not possible to specify both a background image and a background country flag.');
  1927.         }
  1928.         if ($this->background_image != '') {
  1929.             $bkgimg $this->LoadBkgImage($this->background_image_format$this->background_image);
  1930.         } elseif ($this->background_cflag != '') {
  1931.             $fobj                        = new Image\FlagImages(FLAGSIZE4);
  1932.             $dummy                       '';
  1933.             $bkgimg                      $fobj->GetImgByName($this->background_cflag$dummy);
  1934.             $this->background_image_mix  $this->background_cflag_mix;
  1935.             $this->background_image_type $this->background_cflag_type;
  1936.         } else {
  1937.             return;
  1938.         }
  1939.         $bw imagesx($bkgimg);
  1940.         $bh imagesy($bkgimg);
  1941.         // No matter what the angle is we always stroke the image and frame
  1942.         // assuming it is 0 degree
  1943.         $aa $this->img->SetAngle(0);
  1944.         switch ($this->background_image_type) {
  1945.             case BGIMG_FILLPLOT// Resize to just fill the plotarea
  1946.                 $this->FillMarginArea();
  1947.                 $this->StrokeFrame();
  1948.                 // Special case to hande 90 degree rotated graph corectly
  1949.                 if ($aa == 90) {
  1950.                     $this->img->SetAngle(90);
  1951.                     $this->FillPlotArea();
  1952.                     $aa  $this->img->SetAngle(0);
  1953.                     $adj = ($this->img->height $this->img->width) / 2;
  1954.                     $this->img->CopyMerge(
  1955.                         $bkgimg,
  1956.                         $this->img->bottom_margin $adj,
  1957.                         $this->img->left_margin $adj,
  1958.                         0,
  1959.                         0,
  1960.                         $this->img->plotheight 1,
  1961.                         $this->img->plotwidth,
  1962.                         $bw,
  1963.                         $bh,
  1964.                         $this->background_image_mix
  1965.                     );
  1966.                 } else {
  1967.                     $this->FillPlotArea();
  1968.                     $this->img->CopyMerge(
  1969.                         $bkgimg,
  1970.                         $this->img->left_margin,
  1971.                         $this->img->top_margin 1,
  1972.                         0,
  1973.                         0,
  1974.                         $this->img->plotwidth 1,
  1975.                         $this->img->plotheight,
  1976.                         $bw,
  1977.                         $bh,
  1978.                         $this->background_image_mix
  1979.                     );
  1980.                 }
  1981.                 break;
  1982.             case BGIMG_FILLFRAME// Fill the whole area from upper left corner, resize to just fit
  1983.                 $hadj 0;
  1984.                 $vadj 0;
  1985.                 if ($this->doshadow) {
  1986.                     $hadj $this->shadow_width;
  1987.                     $vadj $this->shadow_width;
  1988.                 }
  1989.                 $this->FillMarginArea();
  1990.                 $this->FillPlotArea();
  1991.                 $this->img->CopyMerge(
  1992.                     $bkgimg,
  1993.                     0,
  1994.                     0,
  1995.                     0,
  1996.                     0,
  1997.                     $this->img->width $hadj,
  1998.                     $this->img->height $vadj,
  1999.                     $bw,
  2000.                     $bh,
  2001.                     $this->background_image_mix
  2002.                 );
  2003.                 $this->StrokeFrame();
  2004.                 break;
  2005.             case BGIMG_COPY// Just copy the image from left corner, no resizing
  2006.                 $this->FillMarginArea();
  2007.                 $this->FillPlotArea();
  2008.                 $this->img->CopyMerge(
  2009.                     $bkgimg,
  2010.                     0,
  2011.                     0,
  2012.                     0,
  2013.                     0,
  2014.                     $bw,
  2015.                     $bh,
  2016.                     $bw,
  2017.                     $bh,
  2018.                     $this->background_image_mix
  2019.                 );
  2020.                 $this->StrokeFrame();
  2021.                 break;
  2022.             case BGIMG_CENTER// Center original image in the plot area
  2023.                 $this->FillMarginArea();
  2024.                 $this->FillPlotArea();
  2025.                 $centerx round($this->img->plotwidth $this->img->left_margin $bw 2);
  2026.                 $centery round($this->img->plotheight $this->img->top_margin $bh 2);
  2027.                 $this->img->CopyMerge(
  2028.                     $bkgimg,
  2029.                     $centerx,
  2030.                     $centery,
  2031.                     0,
  2032.                     0,
  2033.                     $bw,
  2034.                     $bh,
  2035.                     $bw,
  2036.                     $bh,
  2037.                     $this->background_image_mix
  2038.                 );
  2039.                 $this->StrokeFrame();
  2040.                 break;
  2041.             case BGIMG_FREE// Just copy the image to the specified location
  2042.                 $this->img->CopyMerge(
  2043.                     $bkgimg,
  2044.                     $this->background_image_xpos,
  2045.                     $this->background_image_ypos,
  2046.                     0,
  2047.                     0,
  2048.                     $bw,
  2049.                     $bh,
  2050.                     $bw,
  2051.                     $bh,
  2052.                     $this->background_image_mix
  2053.                 );
  2054.                 $this->StrokeFrame(); // New
  2055.                 break;
  2056.             default:
  2057.                 Util\JpGraphError::RaiseL(25042); //(" Unknown background image layout");
  2058.         }
  2059.         $this->img->SetAngle($aa);
  2060.     }
  2061.     // Private
  2062.     // Draw a frame around the image
  2063.     public function StrokeFrame()
  2064.     {
  2065.         if (!$this->doframe) {
  2066.             return;
  2067.         }
  2068.         if ($this->background_image_type <= && ($this->bkg_gradtype || ($this->bkg_gradtype && $this->bkg_gradstyle == BGRAD_PLOT))) {
  2069.             $c $this->margin_color;
  2070.         } else {
  2071.             $c false;
  2072.         }
  2073.         if ($this->doshadow) {
  2074.             $this->img->SetColor($this->frame_color);
  2075.             $this->img->ShadowRectangle(
  2076.                 0,
  2077.                 0,
  2078.                 $this->img->width,
  2079.                 $this->img->height,
  2080.                 $c,
  2081.                 $this->shadow_width,
  2082.                 $this->shadow_color
  2083.             );
  2084.         } elseif ($this->framebevel) {
  2085.             if ($c) {
  2086.                 $this->img->SetColor($this->margin_color);
  2087.                 $this->img->FilledRectangle(00$this->img->width 1$this->img->height 1);
  2088.             }
  2089.             $this->img->Bevel(
  2090.                 1,
  2091.                 1,
  2092.                 $this->img->width 2,
  2093.                 $this->img->height 2,
  2094.                 $this->framebeveldepth,
  2095.                 $this->framebevelcolor1,
  2096.                 $this->framebevelcolor2
  2097.             );
  2098.             if ($this->framebevelborder) {
  2099.                 $this->img->SetColor($this->framebevelbordercolor);
  2100.                 $this->img->Rectangle(00$this->img->width 1$this->img->height 1);
  2101.             }
  2102.         } else {
  2103.             $this->img->SetLineWeight($this->frame_weight);
  2104.             if ($c) {
  2105.                 $this->img->SetColor($this->margin_color);
  2106.                 $this->img->FilledRectangle(00$this->img->width 1$this->img->height 1);
  2107.             }
  2108.             $this->img->SetColor($this->frame_color);
  2109.             $this->img->Rectangle(00$this->img->width 1$this->img->height 1);
  2110.         }
  2111.     }
  2112.     public function FillMarginArea()
  2113.     {
  2114.         $hadj 0;
  2115.         $vadj 0;
  2116.         if ($this->doshadow) {
  2117.             $hadj $this->shadow_width;
  2118.             $vadj $this->shadow_width;
  2119.         }
  2120.         $this->img->SetColor($this->margin_color);
  2121.         $this->img->FilledRectangle(00$this->img->width $hadj$this->img->height $vadj);
  2122.         $this->img->FilledRectangle(00$this->img->width $hadj$this->img->top_margin);
  2123.         $this->img->FilledRectangle(0$this->img->top_margin$this->img->left_margin$this->img->height $hadj);
  2124.         $this->img->FilledRectangle(
  2125.             $this->img->left_margin 1,
  2126.             $this->img->height $this->img->bottom_margin,
  2127.             $this->img->width $hadj,
  2128.             $this->img->height $hadj
  2129.         );
  2130.         $this->img->FilledRectangle(
  2131.             $this->img->width $this->img->right_margin,
  2132.             $this->img->top_margin 1,
  2133.             $this->img->width $hadj,
  2134.             $this->img->height $this->img->bottom_margin 1
  2135.         );
  2136.     }
  2137.     public function FillPlotArea()
  2138.     {
  2139.         $this->img->PushColor($this->plotarea_color);
  2140.         $this->img->FilledRectangle(
  2141.             $this->img->left_margin,
  2142.             $this->img->top_margin,
  2143.             $this->img->width $this->img->right_margin,
  2144.             $this->img->height $this->img->bottom_margin
  2145.         );
  2146.         $this->img->PopColor();
  2147.     }
  2148.     // Stroke the plot area with either a solid color or a background image
  2149.     public function StrokePlotArea()
  2150.     {
  2151.         // Note: To be consistent we really should take a possible shadow
  2152.         // into account. However, that causes some problem for the LinearScale class
  2153.         // since in the current design it does not have any links to class Graph which
  2154.         // means it has no way of compensating for the adjusted plotarea in case of a
  2155.         // shadow. So, until I redesign LinearScale we can't compensate for this.
  2156.         // So just set the two adjustment parameters to zero for now.
  2157.         $boxadj 0//$this->doframe ? $this->frame_weight : 0 ;
  2158.         $adj    0//$this->doshadow ? $this->shadow_width : 0 ;
  2159.         if ($this->background_image != '' || $this->background_cflag != '') {
  2160.             $this->StrokeFrameBackground();
  2161.         } else {
  2162.             $aa $this->img->SetAngle(0);
  2163.             $this->StrokeFrame();
  2164.             $aa $this->img->SetAngle($aa);
  2165.             $this->StrokeBackgroundGrad();
  2166.             if ($this->bkg_gradtype || ($this->bkg_gradtype && $this->bkg_gradstyle == BGRAD_MARGIN)) {
  2167.                 $this->FillPlotArea();
  2168.             }
  2169.             $this->StrokePlotGrad();
  2170.         }
  2171.     }
  2172.     public function StrokeIcons()
  2173.     {
  2174.         $n safe_count($this->iIcons);
  2175.         for ($i 0$i $n; ++$i) {
  2176.             $this->iIcons[$i]->StrokeWithScale($this->img$this->xscale$this->yscale);
  2177.         }
  2178.     }
  2179.     public function StrokePlotBox()
  2180.     {
  2181.         // Should we draw a box around the plot area?
  2182.         if ($this->boxed) {
  2183.             $this->img->SetLineWeight(1);
  2184.             $this->img->SetLineStyle('solid');
  2185.             $this->img->SetColor($this->box_color);
  2186.             for ($i 0$i $this->box_weight; ++$i) {
  2187.                 $this->img->Rectangle(
  2188.                     $this->img->left_margin $i,
  2189.                     $this->img->top_margin $i,
  2190.                     $this->img->width $this->img->right_margin $i,
  2191.                     $this->img->height $this->img->bottom_margin $i
  2192.                 );
  2193.             }
  2194.         }
  2195.     }
  2196.     public function SetTitleBackgroundFillStyle($aStyle$aColor1 'black'$aColor2 'white')
  2197.     {
  2198.         $this->titlebkg_fillstyle $aStyle;
  2199.         $this->titlebkg_scolor1   $aColor1;
  2200.         $this->titlebkg_scolor2   $aColor2;
  2201.     }
  2202.     public function SetTitleBackground($aBackColor 'gray'$aStyle TITLEBKG_STYLE1$aFrameStyle TITLEBKG_FRAME_NONE$aFrameColor 'black'$aFrameWeight 1$aBevelHeight 3$aEnable true)
  2203.     {
  2204.         $this->titlebackground             $aEnable;
  2205.         $this->titlebackground_color       $aBackColor;
  2206.         $this->titlebackground_style       $aStyle;
  2207.         $this->titlebackground_framecolor  $aFrameColor;
  2208.         $this->titlebackground_framestyle  $aFrameStyle;
  2209.         $this->titlebackground_frameweight $aFrameWeight;
  2210.         $this->titlebackground_bevelheight $aBevelHeight;
  2211.     }
  2212.     public function StrokeTitles()
  2213.     {
  2214.         $margin 3;
  2215.         if ($this->titlebackground) {
  2216.             // Find out height
  2217.             $this->title->margin += 2;
  2218.             $h $this->title->GetTextHeight($this->img) + $this->title->margin $margin;
  2219.             if ($this->subtitle->!= '' && !$this->subtitle->hide) {
  2220.                 $h += $this->subtitle->GetTextHeight($this->img) + $margin +
  2221.                 $this->subtitle->margin;
  2222.                 $h += 2;
  2223.             }
  2224.             if ($this->subsubtitle->!= '' && !$this->subsubtitle->hide) {
  2225.                 $h += $this->subsubtitle->GetTextHeight($this->img) + $margin +
  2226.                 $this->subsubtitle->margin;
  2227.                 $h += 2;
  2228.             }
  2229.             $this->img->PushColor($this->titlebackground_color);
  2230.             if ($this->titlebackground_style === TITLEBKG_STYLE1) {
  2231.                 // Inside the frame
  2232.                 if ($this->framebevel) {
  2233.                     $x1 $y1 $this->framebeveldepth 1;
  2234.                     $x2 $this->img->width $this->framebeveldepth 2;
  2235.                     $this->title->margin += $this->framebeveldepth 1;
  2236.                     $h += $y1;
  2237.                     $h += 2;
  2238.                 } else {
  2239.                     $x1 $y1 $this->frame_weight;
  2240.                     $x2 $this->img->width $this->frame_weight 1;
  2241.                 }
  2242.             } elseif ($this->titlebackground_style === TITLEBKG_STYLE2) {
  2243.                 // Cover the frame as well
  2244.                 $x1 $y1 0;
  2245.                 $x2 $this->img->width 1;
  2246.             } elseif ($this->titlebackground_style === TITLEBKG_STYLE3) {
  2247.                 // Cover the frame as well (the difference is that
  2248.                 // for style==3 a bevel frame border is on top
  2249.                 // of the title background)
  2250.                 $x1 $y1 0;
  2251.                 $x2 $this->img->width 1;
  2252.                 $h += $this->framebeveldepth;
  2253.                 $this->title->margin += $this->framebeveldepth;
  2254.             } else {
  2255.                 Util\JpGraphError::RaiseL(25043); //('Unknown title background style.');
  2256.             }
  2257.             if ($this->titlebackground_framestyle === 3) {
  2258.                 $h += $this->titlebackground_bevelheight 1;
  2259.                 $this->title->margin += $this->titlebackground_bevelheight;
  2260.             }
  2261.             if ($this->doshadow) {
  2262.                 $x2 -= $this->shadow_width;
  2263.             }
  2264.             $indent 0;
  2265.             if ($this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL) {
  2266.                 $indent $this->titlebackground_bevelheight;
  2267.             }
  2268.             if ($this->titlebkg_fillstyle == TITLEBKG_FILLSTYLE_HSTRIPED) {
  2269.                 $this->img->FilledRectangle2(
  2270.                     $x1 $indent,
  2271.                     $y1 $indent,
  2272.                     $x2 $indent,
  2273.                     $h $indent,
  2274.                     $this->titlebkg_scolor1,
  2275.                     $this->titlebkg_scolor2
  2276.                 );
  2277.             } elseif ($this->titlebkg_fillstyle == TITLEBKG_FILLSTYLE_VSTRIPED) {
  2278.                 $this->img->FilledRectangle2(
  2279.                     $x1 $indent,
  2280.                     $y1 $indent,
  2281.                     $x2 $indent,
  2282.                     $h $indent,
  2283.                     $this->titlebkg_scolor1,
  2284.                     $this->titlebkg_scolor2,
  2285.                     2
  2286.                 );
  2287.             } else {
  2288.                 // Solid fill
  2289.                 $this->img->FilledRectangle($x1$y1$x2$h);
  2290.             }
  2291.             $this->img->PopColor();
  2292.             $this->img->PushColor($this->titlebackground_framecolor);
  2293.             $this->img->SetLineWeight($this->titlebackground_frameweight);
  2294.             if ($this->titlebackground_framestyle == TITLEBKG_FRAME_FULL) {
  2295.                 // Frame background
  2296.                 $this->img->Rectangle($x1$y1$x2$h);
  2297.             } elseif ($this->titlebackground_framestyle == TITLEBKG_FRAME_BOTTOM) {
  2298.                 // Bottom line only
  2299.                 $this->img->Line($x1$h$x2$h);
  2300.             } elseif ($this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL) {
  2301.                 $this->img->Bevel($x1$y1$x2$h$this->titlebackground_bevelheight);
  2302.             }
  2303.             $this->img->PopColor();
  2304.             // This is clumsy. But we neeed to stroke the whole graph frame if it is
  2305.             // set to bevel to get the bevel shading on top of the text background
  2306.             if ($this->framebevel && $this->doframe && $this->titlebackground_style === 3) {
  2307.                 $this->img->Bevel(
  2308.                     1,
  2309.                     1,
  2310.                     $this->img->width 2,
  2311.                     $this->img->height 2,
  2312.                     $this->framebeveldepth,
  2313.                     $this->framebevelcolor1,
  2314.                     $this->framebevelcolor2
  2315.                 );
  2316.                 if ($this->framebevelborder) {
  2317.                     $this->img->SetColor($this->framebevelbordercolor);
  2318.                     $this->img->Rectangle(00$this->img->width 1$this->img->height 1);
  2319.                 }
  2320.             }
  2321.         }
  2322.         // Stroke title
  2323.         $y $this->title->margin;
  2324.         if ($this->title->halign == 'center') {
  2325.             $this->title->Center(0$this->img->width$y);
  2326.         } elseif ($this->title->halign == 'left') {
  2327.             $this->title->SetPos($this->title->margin 2$y);
  2328.         } elseif ($this->title->halign == 'right') {
  2329.             $indent 0;
  2330.             if ($this->doshadow) {
  2331.                 $indent $this->shadow_width 2;
  2332.             }
  2333.             $this->title->SetPos($this->img->width $this->title->margin $indent$y'right');
  2334.         }
  2335.         $this->title->Stroke($this->img);
  2336.         // ... and subtitle
  2337.         $y += $this->title->GetTextHeight($this->img) + $margin $this->subtitle->margin;
  2338.         if ($this->subtitle->halign == 'center') {
  2339.             $this->subtitle->Center(0$this->img->width$y);
  2340.         } elseif ($this->subtitle->halign == 'left') {
  2341.             $this->subtitle->SetPos($this->subtitle->margin 2$y);
  2342.         } elseif ($this->subtitle->halign == 'right') {
  2343.             $indent 0;
  2344.             if ($this->doshadow) {
  2345.                 $indent $this->shadow_width 2;
  2346.             }
  2347.             $this->subtitle->SetPos($this->img->width $this->subtitle->margin $indent$y'right');
  2348.         }
  2349.         $this->subtitle->Stroke($this->img);
  2350.         // ... and subsubtitle
  2351.         $y += $this->subtitle->GetTextHeight($this->img) + $margin $this->subsubtitle->margin;
  2352.         if ($this->subsubtitle->halign == 'center') {
  2353.             $this->subsubtitle->Center(0$this->img->width$y);
  2354.         } elseif ($this->subsubtitle->halign == 'left') {
  2355.             $this->subsubtitle->SetPos($this->subsubtitle->margin 2$y);
  2356.         } elseif ($this->subsubtitle->halign == 'right') {
  2357.             $indent 0;
  2358.             if ($this->doshadow) {
  2359.                 $indent $this->shadow_width 2;
  2360.             }
  2361.             $this->subsubtitle->SetPos($this->img->width $this->subsubtitle->margin $indent$y'right');
  2362.         }
  2363.         $this->subsubtitle->Stroke($this->img);
  2364.         // ... and fancy title
  2365.         $this->tabtitle->Stroke($this->img);
  2366.     }
  2367.     public function StrokeTexts()
  2368.     {
  2369.         // Stroke any user added text objects
  2370.         if ($this->texts != null) {
  2371.             for ($i 0$i safe_count($this->texts); ++$i) {
  2372.                 $this->texts[$i]->StrokeWithScale($this->img$this->xscale$this->yscale);
  2373.             }
  2374.         }
  2375.         if ($this->y2texts != null && $this->y2scale != null) {
  2376.             for ($i 0$i safe_count($this->y2texts); ++$i) {
  2377.                 $this->y2texts[$i]->StrokeWithScale($this->img$this->xscale$this->y2scale);
  2378.             }
  2379.         }
  2380.     }
  2381.     public function StrokeTables()
  2382.     {
  2383.         if ($this->iTables != null) {
  2384.             $n safe_count($this->iTables);
  2385.             for ($i 0$i $n; ++$i) {
  2386.                 $this->iTables[$i]->StrokeWithScale($this->img$this->xscale$this->yscale);
  2387.             }
  2388.         }
  2389.     }
  2390.     public function DisplayClientSideaImageMapAreas()
  2391.     {
  2392.         // Debug stuff - display the outline of the image map areas
  2393.         $csim '';
  2394.         foreach ($this->plots as $p) {
  2395.             $csim .= $p->GetCSIMareas();
  2396.         }
  2397.         $csim .= $this->legend->GetCSIMareas();
  2398.         if (preg_match_all('/area shape="(\\w+)" coords="([0-9\\, ]+)"/'$csim$coords)) {
  2399.             $this->img->SetColor($this->csimcolor);
  2400.             $n safe_count($coords[0]);
  2401.             for ($i 0$i $n; ++$i) {
  2402.                 if ($coords[1][$i] == 'poly') {
  2403.                     preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/'$coords[2][$i], $pts);
  2404.                     $this->img->SetStartPoint($pts[1][count($pts[0]) - 1], $pts[2][count($pts[0]) - 1]);
  2405.                     $m safe_count($pts[0]);
  2406.                     for ($j 0$j $m; ++$j) {
  2407.                         $this->img->LineTo($pts[1][$j], $pts[2][$j]);
  2408.                     }
  2409.                 } elseif ($coords[1][$i] == 'rect') {
  2410.                     $pts preg_split('/,/'$coords[2][$i]);
  2411.                     $this->img->SetStartPoint($pts[0], $pts[1]);
  2412.                     $this->img->LineTo($pts[2], $pts[1]);
  2413.                     $this->img->LineTo($pts[2], $pts[3]);
  2414.                     $this->img->LineTo($pts[0], $pts[3]);
  2415.                     $this->img->LineTo($pts[0], $pts[1]);
  2416.                 }
  2417.             }
  2418.         }
  2419.     }
  2420.     // Text scale offset in world coordinates
  2421.     public function SetTextScaleOff($aOff)
  2422.     {
  2423.         $this->text_scale_off         $aOff;
  2424.         $this->xscale->text_scale_off $aOff;
  2425.     }
  2426.     // Text width of bar to be centered in absolute pixels
  2427.     public function SetTextScaleAbsCenterOff($aOff)
  2428.     {
  2429.         $this->text_scale_abscenteroff $aOff;
  2430.     }
  2431.     // Get Y min and max values for added lines
  2432.     public function GetLinesYMinMax($aLines)
  2433.     {
  2434.         if (is_null($aLines)) {
  2435.             return false;
  2436.         }
  2437.         $n safe_count($aLines);
  2438.         if ($n == 0) {
  2439.             return false;
  2440.         }
  2441.         $min $aLines[0]->scaleposition;
  2442.         $max $min;
  2443.         $flg false;
  2444.         for ($i 0$i $n; ++$i) {
  2445.             if ($aLines[$i]->direction == HORIZONTAL) {
  2446.                 $flg true;
  2447.                 $v   $aLines[$i]->scaleposition;
  2448.                 if ($min $v) {
  2449.                     $min $v;
  2450.                 }
  2451.                 if ($max $v) {
  2452.                     $max $v;
  2453.                 }
  2454.             }
  2455.         }
  2456.         return $flg ? [$min$max] : false;
  2457.     }
  2458.     // Get X min and max values for added lines
  2459.     public function GetLinesXMinMax($aLines)
  2460.     {
  2461.         $n safe_count($aLines);
  2462.         if ($n == 0) {
  2463.             return false;
  2464.         }
  2465.         $min $aLines[0]->scaleposition;
  2466.         $max $min;
  2467.         $flg false;
  2468.         for ($i 0$i $n; ++$i) {
  2469.             if ($aLines[$i]->direction == VERTICAL) {
  2470.                 $flg true;
  2471.                 $v   $aLines[$i]->scaleposition;
  2472.                 if ($min $v) {
  2473.                     $min $v;
  2474.                 }
  2475.                 if ($max $v) {
  2476.                     $max $v;
  2477.                 }
  2478.             }
  2479.         }
  2480.         return $flg ? [$min$max] : false;
  2481.     }
  2482.     // Get min and max values for all included plots
  2483.     public function GetPlotsYMinMax($bPlots)
  2484.     {
  2485.         $aPlots array_filter($bPlots, function ($plot) {
  2486.             //\Kint::dump($plot, $plot instanceof Plot\Plot);
  2487.             return $plot instanceof Plot\Plot;
  2488.         });
  2489.         reset($aPlots);
  2490.         $n safe_count($aPlots);
  2491.         $i 0;
  2492.         //\Kint::dump($n, $aPlots);
  2493.         do {
  2494.             //\Kint::dump($i, $aPlots[$i]);
  2495.             list($xmax$max) = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [nullnull];
  2496.         } while (++$i $n && !is_numeric($max));
  2497.         $i 0;
  2498.         do {
  2499.             list($xmin$min) = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [nullnull];
  2500.         } while (++$i $n && !is_numeric($min));
  2501.         if (!is_numeric($min) || !is_numeric($max)) {
  2502.             Util\JpGraphError::RaiseL(25044); //('Cannot use autoscaling since it is impossible to determine a valid min/max value  of the Y-axis (only null values).');
  2503.         }
  2504.         for ($i 0$i $n; ++$i) {
  2505.             list($xmax$ymax) = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [nullnull];
  2506.             list($xmin$ymin) = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [nullnull];
  2507.             if (is_numeric($ymax)) {
  2508.                 $max max($max$ymax);
  2509.             }
  2510.             if (is_numeric($ymin)) {
  2511.                 $min min($min$ymin);
  2512.             }
  2513.         }
  2514.         if ($min == '') {
  2515.             $min 0;
  2516.         }
  2517.         if ($max == '') {
  2518.             $max 0;
  2519.         }
  2520.         if ($min == && $max == 0) {
  2521.             // Special case if all values are 0
  2522.             $min 0;
  2523.             $max 1;
  2524.         }
  2525.         return [$min$max];
  2526.     }
  2527.     public function hasLinePlotAndBarPlot()
  2528.     {
  2529.         $has_line false;
  2530.         $has_bar  false;
  2531.         foreach ($this->plots as $plot) {
  2532.             if ($plot instanceof Plot\LinePlot) {
  2533.                 $has_line true;
  2534.             }
  2535.             if ($plot instanceof Plot\BarPlot) {
  2536.                 $has_bar true;
  2537.             }
  2538.         }
  2539.         if ($has_line && $has_bar) {
  2540.             return true;
  2541.         }
  2542.         return false;
  2543.     }
  2544.     public function SetTheme($graph_theme)
  2545.     {
  2546.         if (!($this instanceof PieGraph)) {
  2547.             if (!$this->isAfterSetScale) {
  2548.                 Util\JpGraphError::RaiseL(25133); //('Use Graph::SetTheme() after Graph::SetScale().');
  2549.             }
  2550.         }
  2551.         if ($this->graph_theme) {
  2552.             $this->ClearTheme();
  2553.         }
  2554.         $this->graph_theme $graph_theme;
  2555.         $this->graph_theme->ApplyGraph($this);
  2556.     }
  2557.     public function ClearTheme()
  2558.     {
  2559.         $this->graph_theme null;
  2560.         $this->isRunningClear true;
  2561.         $this->__construct(
  2562.             $this->inputValues['aWidth'],
  2563.             $this->inputValues['aHeight'],
  2564.             $this->inputValues['aCachedName'],
  2565.             $this->inputValues['aTimeout'],
  2566.             $this->inputValues['aInline']
  2567.         );
  2568.         if (!($this instanceof PieGraph)) {
  2569.             if ($this->isAfterSetScale) {
  2570.                 $this->SetScale(
  2571.                     $this->inputValues['aAxisType'],
  2572.                     $this->inputValues['aYMin'],
  2573.                     $this->inputValues['aYMax'],
  2574.                     $this->inputValues['aXMin'],
  2575.                     $this->inputValues['aXMax']
  2576.                 );
  2577.             }
  2578.         }
  2579.         $this->isRunningClear false;
  2580.     }
  2581.     public function SetSupersampling($do false$scale 2)
  2582.     {
  2583.         if ($do) {
  2584.             define('SUPERSAMPLING_SCALE'$scale);
  2585.         // $this->img->scale = $scale;
  2586.         } else {
  2587.             define('SUPERSAMPLING_SCALE'1);
  2588.             //$this->img->scale = 0;
  2589.         }
  2590.     }
  2591. // @class