index.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. require_once 'telemetry_db.php';
  3. error_reporting(0);
  4. putenv('GDFONTPATH='.realpath('.'));
  5. /**
  6. * @param string $name
  7. *
  8. * @return string|null
  9. */
  10. function tryFont($name)
  11. {
  12. if (is_array(imageftbbox(12, 0, $name, 'M'))) {
  13. return $name;
  14. }
  15. $fullPathToFont = realpath('.').'/'.$name.'.ttf';
  16. if (is_array(imageftbbox(12, 0, $fullPathToFont, 'M'))) {
  17. return $fullPathToFont;
  18. }
  19. return null;
  20. }
  21. /**
  22. * @param int|float $d
  23. *
  24. * @return string
  25. */
  26. function format($d)
  27. {
  28. if ($d < 10) {
  29. return number_format($d, 2, '.', '');
  30. }
  31. if ($d < 100) {
  32. return number_format($d, 1, '.', '');
  33. }
  34. return number_format($d, 0, '.', '');
  35. }
  36. /**
  37. * Drops the fractional seconds a database may keep on the timestamp column.
  38. *
  39. * How much precision that column carries is decided by the backend, and the
  40. * three schemas shipped here disagree: MySQL declares `timestamp`, which keeps
  41. * none, PostgreSQL uses `timestamp without time zone DEFAULT now()`, which
  42. * keeps microseconds, and MSSQL uses `datetime`, which keeps milliseconds. The
  43. * value was drawn exactly as the database returned it, so the same result reads
  44. * as "2026-08-10 02:04:13" on one deployment and "2026-08-10 02:04:13.957789"
  45. * on another.
  46. *
  47. * The fraction is matched against the seconds field it belongs to rather than
  48. * the end of the string, so a value carrying a zone offset after it, such as
  49. * "2026-08-10 02:04:13.957789+00", is handled as well.
  50. *
  51. * @param string $timestamp
  52. *
  53. * @return string
  54. */
  55. function formatTimestamp($timestamp)
  56. {
  57. return preg_replace('/(:\d{2})\.\d+/', '$1', (string) $timestamp);
  58. }
  59. /**
  60. * @param array $speedtest
  61. *
  62. * @return array
  63. */
  64. function formatSpeedtestDataForImage($speedtest)
  65. {
  66. // format values for the image
  67. $speedtest['dl'] = format($speedtest['dl']);
  68. $speedtest['ul'] = format($speedtest['ul']);
  69. $speedtest['ping'] = format($speedtest['ping']);
  70. $speedtest['jitter'] = format($speedtest['jitter']);
  71. $speedtest['timestamp'] = formatTimestamp($speedtest['timestamp']);
  72. $ispinfo = json_decode($speedtest['ispinfo'], true)['processedString'];
  73. $dash = strpos($ispinfo, '-');
  74. if ($dash !== false) {
  75. $ispinfo = substr($ispinfo, $dash + 2);
  76. $par = strrpos($ispinfo, '(');
  77. if ($par !== false) {
  78. $ispinfo = substr($ispinfo, 0, $par);
  79. }
  80. } else {
  81. $ispinfo = '';
  82. }
  83. $speedtest['ispinfo'] = $ispinfo;
  84. return $speedtest;
  85. }
  86. /**
  87. * @param array $speedtest
  88. *
  89. * @return void
  90. */
  91. function drawImage($speedtest)
  92. {
  93. // format values for the image
  94. $data = formatSpeedtestDataForImage($speedtest);
  95. $dl = $data['dl'];
  96. $ul = $data['ul'];
  97. $ping = $data['ping'];
  98. $jit = $data['jitter'];
  99. $ispinfo = $data['ispinfo'];
  100. $timestamp = $data['timestamp'];
  101. // initialize the image
  102. $SCALE = 1.25;
  103. $SMALL_SEP = 8 * $SCALE;
  104. $WIDTH = 400 * $SCALE;
  105. $HEIGHT = 229 * $SCALE;
  106. $im = imagecreatetruecolor($WIDTH, $HEIGHT);
  107. $BACKGROUND_COLOR = imagecolorallocate($im, 255, 255, 255);
  108. // configure fonts
  109. $FONT_LABEL = tryFont('OpenSans-Semibold');
  110. $FONT_LABEL_SIZE = 14 * $SCALE;
  111. $FONT_LABEL_SIZE_BIG = 16 * $SCALE;
  112. $FONT_METER = tryFont('OpenSans-Light');
  113. $FONT_METER_SIZE = 20 * $SCALE;
  114. $FONT_METER_SIZE_BIG = 22 * $SCALE;
  115. $FONT_MEASURE = tryFont('OpenSans-Semibold');
  116. $FONT_MEASURE_SIZE = 12 * $SCALE;
  117. $FONT_MEASURE_SIZE_BIG = 12 * $SCALE;
  118. $FONT_ISP = tryFont('OpenSans-Semibold');
  119. $FONT_ISP_SIZE = 9 * $SCALE;
  120. $FONT_TIMESTAMP = tryFont("OpenSans-Light");
  121. $FONT_TIMESTAMP_SIZE = 8 * $SCALE;
  122. $FONT_WATERMARK = tryFont('OpenSans-Light');
  123. $FONT_WATERMARK_SIZE = 8 * $SCALE;
  124. // configure text colors
  125. $TEXT_COLOR_LABEL = imagecolorallocate($im, 40, 40, 40);
  126. $TEXT_COLOR_PING_METER = imagecolorallocate($im, 170, 96, 96);
  127. $TEXT_COLOR_JIT_METER = imagecolorallocate($im, 170, 96, 96);
  128. $TEXT_COLOR_DL_METER = imagecolorallocate($im, 96, 96, 170);
  129. $TEXT_COLOR_UL_METER = imagecolorallocate($im, 96, 96, 96);
  130. $TEXT_COLOR_MEASURE = imagecolorallocate($im, 40, 40, 40);
  131. $TEXT_COLOR_ISP = imagecolorallocate($im, 40, 40, 40);
  132. $SEPARATOR_COLOR = imagecolorallocate($im, 192, 192, 192);
  133. $TEXT_COLOR_TIMESTAMP = imagecolorallocate($im, 160, 160, 160);
  134. $TEXT_COLOR_WATERMARK = imagecolorallocate($im, 160, 160, 160);
  135. // configure positioning or the different parts on the image
  136. $POSITION_X_PING = 125 * $SCALE;
  137. $POSITION_Y_PING_LABEL = 24 * $SCALE;
  138. $POSITION_Y_PING_METER = 60 * $SCALE;
  139. $POSITION_Y_PING_MEASURE = 60 * $SCALE;
  140. $POSITION_X_JIT = 275 * $SCALE;
  141. $POSITION_Y_JIT_LABEL = 24 * $SCALE;
  142. $POSITION_Y_JIT_METER = 60 * $SCALE;
  143. $POSITION_Y_JIT_MEASURE = 60 * $SCALE;
  144. $POSITION_X_DL = 120 * $SCALE;
  145. $POSITION_Y_DL_LABEL = 105 * $SCALE;
  146. $POSITION_Y_DL_METER = 143 * $SCALE;
  147. $POSITION_Y_DL_MEASURE = 169 * $SCALE;
  148. $POSITION_X_UL = 280 * $SCALE;
  149. $POSITION_Y_UL_LABEL = 105 * $SCALE;
  150. $POSITION_Y_UL_METER = 143 * $SCALE;
  151. $POSITION_Y_UL_MEASURE = 169 * $SCALE;
  152. $POSITION_X_ISP = 4 * $SCALE;
  153. $POSITION_Y_ISP = 205 * $SCALE;
  154. $SEPARATOR_Y = 211 * $SCALE;
  155. $POSITION_X_TIMESTAMP= 4 * $SCALE;
  156. $POSITION_Y_TIMESTAMP = 223 * $SCALE;
  157. $POSITION_Y_WATERMARK = 223 * $SCALE;
  158. // configure labels
  159. $MBPS_TEXT = 'Mbit/s';
  160. $MS_TEXT = 'ms';
  161. $PING_TEXT = 'Ping';
  162. $JIT_TEXT = 'Jitter';
  163. $DL_TEXT = 'Download';
  164. $UL_TEXT = 'Upload';
  165. $WATERMARK_TEXT = 'LibreSpeed';
  166. // create text boxes for each part of the image
  167. $mbpsBbox = imageftbbox($FONT_MEASURE_SIZE_BIG, 0, $FONT_MEASURE, $MBPS_TEXT);
  168. $msBbox = imageftbbox($FONT_MEASURE_SIZE, 0, $FONT_MEASURE, $MS_TEXT);
  169. $pingBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $PING_TEXT);
  170. $pingMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $ping);
  171. $jitBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $JIT_TEXT);
  172. $jitMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $jit);
  173. $dlBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $DL_TEXT);
  174. $dlMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $dl);
  175. $ulBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $UL_TEXT);
  176. $ulMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $ul);
  177. $watermarkBbox = imageftbbox($FONT_WATERMARK_SIZE, 0, $FONT_WATERMARK, $WATERMARK_TEXT);
  178. $POSITION_X_WATERMARK = $WIDTH - $watermarkBbox[4] - 4 * $SCALE;
  179. // put the parts together to draw the image
  180. imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR);
  181. // ping
  182. imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_PING - $pingBbox[4] / 2, $POSITION_Y_PING_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $PING_TEXT);
  183. imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_PING - $pingMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_PING_METER, $TEXT_COLOR_PING_METER, $FONT_METER, $ping);
  184. imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_PING + $pingMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_PING_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
  185. // jitter
  186. imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_JIT - $jitBbox[4] / 2, $POSITION_Y_JIT_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $JIT_TEXT);
  187. imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_JIT - $jitMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_JIT_METER, $TEXT_COLOR_JIT_METER, $FONT_METER, $jit);
  188. imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_JIT + $jitMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_JIT_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
  189. // dl
  190. imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_DL - $dlBbox[4] / 2, $POSITION_Y_DL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $DL_TEXT);
  191. imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_DL - $dlMeterBbox[4] / 2, $POSITION_Y_DL_METER, $TEXT_COLOR_DL_METER, $FONT_METER, $dl);
  192. imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_DL - $mbpsBbox[4] / 2, $POSITION_Y_DL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT);
  193. // ul
  194. imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_UL - $ulBbox[4] / 2, $POSITION_Y_UL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $UL_TEXT);
  195. imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_UL - $ulMeterBbox[4] / 2, $POSITION_Y_UL_METER, $TEXT_COLOR_UL_METER, $FONT_METER, $ul);
  196. imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_UL - $mbpsBbox[4] / 2, $POSITION_Y_UL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT);
  197. // isp
  198. imagefttext($im, $FONT_ISP_SIZE, 0, $POSITION_X_ISP, $POSITION_Y_ISP, $TEXT_COLOR_ISP, $FONT_ISP, $ispinfo);
  199. // separator
  200. imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR);
  201. // timestamp
  202. imagefttext($im, $FONT_TIMESTAMP_SIZE, 0, $POSITION_X_TIMESTAMP, $POSITION_Y_TIMESTAMP, $TEXT_COLOR_TIMESTAMP, $FONT_TIMESTAMP, $timestamp);
  203. // watermark
  204. imagefttext($im, $FONT_WATERMARK_SIZE, 0, $POSITION_X_WATERMARK, $POSITION_Y_WATERMARK, $TEXT_COLOR_WATERMARK, $FONT_WATERMARK, $WATERMARK_TEXT);
  205. // send the image to the browser
  206. header('Content-Type: image/png');
  207. imagepng($im);
  208. }
  209. $speedtest = getSpeedtestUserById($_GET['id']);
  210. if (!is_array($speedtest)) {
  211. exit(1);
  212. }
  213. drawImage($speedtest);