ソフトウェア/pukiwiki/QRコードの表示

(1d) 更新


公開メモ

目的

pukiwiki 内の重要なリンクについて、 ページを印刷したときにQRコードが表示されるようにしたかったので、 Google ChartBitly を使って make_pagelink を拡張しました。

QR コードを表示するには

Google Chart へリンクを張ればOK

例えば、

http://chart.apis.google.com/chart?cht=qr&chld=L%7c1&chs=120x120&chl=http://dora.bk.tsukuba.ac.jp/~takeuchi/

の url へアクセスすれば http://dora.bk.tsukuba.ac.jp/~takeuchi/ を表わすQRコードを画像として得られます。

&.png

詳しい使い方は他のページでいろいろ解説されています→ Google:qrコード google

本家の解説はこちら: https://developers.google.com/chart/infographics/docs/qr_codes

基本的には

LANG:php
     $qr_img = 
         '<img src="' .
         'http://chart.apis.google.com/chart?cht=qr' .
         '&chs=80x80&chld=L|1&chl=' . urlencode($url) .
         '" class="visible-print-inline">';

のようにして画像を html に埋め込めます。

class="visible-print-inline"

は bootstrap の css クラスで、印刷時のみ表示するように指定するためのものです。

pukiwiki ページへのリンク

アドレスが長いのでQRコード画像が大きくなってしまいます。

例えばこのページの URL は

http://dora.bk.tsukuba.ac.jp/~takeuchi/?%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%2Fpukiwiki%2FQR%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E8%A1%A8%E7%A4%BA

なので、QRコードは

chart?cht=qr&chld=L%7c1&chs=120x120&chl=http%3A%2F%2Fdora.bk.tsukuba.ac.jp%2F%7Etakeuchi%2F%3F%25E3%2582%25BD%25E3%2583%2595%25E3%2583%2588%25E3%2582%25A6%25E3%2582%25A7%25E3%2582%25A2%252Fpukiwiki%252FQR%25E3%2582%25B3%25E3%2583%25BC%25E3%2583%2589%25E3%2581%25AE%25E8%25A1%25A8%25E7%25A4%25BA&.png

となり、かなり大きく印刷しないと認識されません。

bitly で url を短くする

url 短縮サービスを使うことでシンプルなQRコードを作成できます。

1epCc4p&.png

これなら小さめに印刷しても認識できます。

bitly の api

こちらを参考に、bitly にアクセスして短縮urlを得るコードを書きました。

http://qiita.com/maruyam-a/items/96c8ad733c770a44117e

LANG:php
function shorten_url($url)
{
  // to create your own access token, 
  // see http://qiita.com/maruyam-a/items/96c8ad733c770a44117e
  $access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  
  $api_url = 'https://api-ssl.bitly.com/v3/shorten?' .
             'access_token=' . $access_token .
             '&longUrl=' . urlencode($url);

  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $api_url);
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
  $result = json_decode( curl_exec( $curl ), true );

  return $result['data']['url'];
}

データベースへ格納する

ページを表示する度に短縮アドレスを bitly に問い合せるのは無駄なので、 一度得た短縮アドレスをデータベースに格納しておくことにしました。

LANG:php
function fetch_short_url($url)
{
  $db = new SQLite3('cache/short_urls.sqlite3');
  $db->query('CREATE TABLE IF NOT EXISTS short_urls ' .
             '(url VARCHAR(1024) PRIMARY KEY, short VARCHAR(32));');

  $short = $db->querySingle('SELECT short FROM short_urls ' . 
                            'WHERE url=\'' . SQLite3::escapeString($url) . '\';');
  if ($short == NULL) {
    $short = shorten_url($url);
    $db->query('INSERT INTO short_urls VALUES(\'' . 
        SQLite3::escapeString($url) . '\',\'' .
        SQLite3::escapeString($short) . '\');');
  }
  $db->close();
  
  return $short;
}

pukiwiki では cache/ 以下にデータを保持できるので、 cache/short_urls.sqlite3 というデータベースを作成し、 短縮アドレスを格納しています。

こうすることで、一回目の表示では bitly にアクセスするため時間がかかりますが、 二回目以降は高速に表示が可能です。

qr プラグイン

ということで、QRコード表示用のプラグインを作成しました。

plugin/qr.inc.php

LANG:php
<?php
//
// pukiwiki用 QRコード プラグイン (qr.inc.php)
//   Copywrite 2015 Osamu Takeuchi <osamu@big.jp>
//
// [履歴]
//   2015.06.18 初期リリース
//
// [インストール]
//   ソースファイルを (pukiwiki)/plugin/qr.inc.php として保存
//
// [使い方]
//   &qr(http://www.example.com/); や &qr([[PageName]]); により、
//   印刷時のみ表示される QRコードを埋め込む
//   オプションを含めたフルセットの使い方は、
//   &qr(AnyString_or_PageName[,chs=L|1][,chld=80x80][,visible]);
//

function shorten_url($url)
{
  // to create your own access token, 
  // see http://qiita.com/maruyam-a/items/96c8ad733c770a44117e
  $access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  
  $api_url = 'https://api-ssl.bitly.com/v3/shorten?' .
             'access_token=' . $access_token .
             '&longUrl=' . urlencode($url);

  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $api_url);
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
  $result = json_decode( curl_exec( $curl ), true );

  return $result['data']['url'];
}

function fetch_short_url($url)
{
  $db = new SQLite3('cache/short_urls.sqlite3');
  $db->query('CREATE TABLE IF NOT EXISTS short_urls ' .
             '(url VARCHAR(1024) PRIMARY KEY, short VARCHAR(32));');

  $short = $db->querySingle('SELECT short FROM short_urls ' . 
                            'WHERE url=\'' . SQLite3::escapeString($url) . '\';');
  if ($short == NULL) {
    $short = shorten_url($url);
    $db->query('INSERT INTO short_urls VALUES(\'' . 
        SQLite3::escapeString($url) . '\',\'' .
        SQLite3::escapeString($short) . '\');');
  }
  $db->close();
  
  return $short;
}

function plugin_qr_inline()
{
    global $script;
    $args = func_get_args();
    $str_or_bracket = trim(array_shift($args));
    
    $chs = '80x80';
    $chld = 'L|1';
    $class = ' class="visible-print-inline"';

    if (preg_match('/^\\[\\[(.*?)(#[A-Za-z0-9]+)?\\]\\]$/', $str_or_bracket, $match)) {
      $str = $script . '?' . urlencode($match[1]) . $match[2];
    } else {
      $str = $str_or_bracket;
    }
    
    if (preg_match('/^https?\:\/\//', $str)) {
      $str = fetch_short_url($str);
      $chs = '33x33';
      $chld = 'Q|1';
    }
    
    foreach ($args as $arg) {
      $arg = trim($arg);
      if (mb_substr($arg,0,4) == 'chs=') $chs = mb_substr($arg,4);
      if (mb_substr($arg,0,5) == 'chld=') $chld = mb_substr($arg,5);
      if ($arg == 'visible') $class = '';
    }
    
    return '<img src="' .
           'http://chart.apis.google.com/chart?cht=qr' .
           '&chs=' . urlencode($chs) .
           '&chld=' . urlencode($chld) .
           '&chl=' . urlencode($str) .
           '"' . $class . '>';
}

?>

使用例

&qr(任意の文字列,chs=60x60,visible);

&qr([[ソフトウェア/pukiwiki/QRコードの表示]],visible);

&qr([[ソフトウェア/pukiwiki/QRコードの表示]]);

 ← 印刷時しか表示されません。 正しく表示されるかは印刷プレビューで確かめてください。

make_pagelink を書き換える

pukiwiki では

[[目的Page名]]

という形で文中に目的ページへのリンクを埋め込みますが、

[[@目的Page名]]

のようにページ名の前に @ を付けることにより、 印刷時にQRコードを表示するように改造しました。

LANG:php
// Make hyperlink for the page
function make_pagelink($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE)
{
  $add_qr_code = false;
  if (substr($page, 0, 1)=='@') {
    $add_qr_code = true;
    $page = substr($page, 1);
  }
  
  $link = make_pagelink_without_qrcode($page, $alias, $anchor, $refer, $isautolink);
  
  if ($add_qr_code && preg_match('/href="([^"]*)"/', $link, $match)) {
    require_once(PLUGIN_DIR . 'qr.inc.php');
    return $link . plugin_qr_inline($match[1]);
  }

 return $link;
 }

function make_pagelink_without_qrcode($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE)
{
  ...
  • 元々の make_pagelink を make_pagelink_without_qrcode と改名して、 新しく作った make_pagelink の中から呼び出します
  • ページ名の先頭に @ があれば、それを除いて make_pagelink_without_qrcode を呼びます
  • 作成された html 内の href の値を読み取って qr プラグインを呼び出す

という動作です。

使い方

例えば、

[[このページへのリンク>@ソフトウェア/pukiwiki/QRコードの表示]]

のようにページ名の前に @ を付けて書くと、一見

このページへのリンク

のように普通のリンクができるように見えますが、 印刷しようとすると、上記リンクにQRコードが付きます。

めでたしめでたし。

Bitly API v4 への対応

  • Bitly にログインして Settings から API を選択
  • Access Token の項目で Enter Password のフィールドにパスワードを入れて Generate token する
  • 生成された token をコピーしておく

https://www.meganii.com/blog/2020/04/17/bitly-api-migrating-from-v3/https://qiita.com/re-24/items/bfdd533e5dacecd21a7a#post%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E5%AE%9F%E8%A1%8C などを参考に、

LANG: php
function shorten_url($url)
{
  $access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  
  $api_url = 'https://api-ssl.bitly.com/v4/shorten';
  $header = [
    'Authorization: Bearer ' . $access_token,
    'Content-Type: application/json',
  ];
  $data = [
    'long_url'=> $url,
  ];

  $curl = curl_init($api_url);
  curl_setopt($curl, CURLOPT_POST, TRUE);                            //POSTで送信
  curl_setopt($curl, CURLOPT_HTTPHEADER, $header);                   // リクエストにヘッダーを含める
  curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));   //データをセット
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);                  //受け取ったデータを変数に
  $result = json_decode( curl_exec( $curl ), true );

  return $result['data']['url'];
}

Google Chart API が廃止になったことへの対応

https://intercord.jp/blog/2024415google-chart-api/ によれば、

http://chart.apis.google.com/chart?chs=500x500&cht=qr&chl=(URL)

の代わりに

https://api.qrserver.com/v1/create-qr-code/?data=(URL)

とすれば良いそうなのですが・・・

  • 生成に時間がかかる
  • QR コードのバージョンを指定できない

という問題があるのでちょっと使いづらいです。。。

qrcode.js を使ってブラウザ上で生成する

https://github.com/davidshimjs/qrcodejs

こちらを利用すると javascript を使ってブラウザ上で QR コードを生成可能になります。

ただこのコード 2024/04 次点で9年前から更新されておらず、pull request がたんまり溜まっていました。

  • Android 端末でのバージョンチェックに関するバグ
  • ユニコード対応部分のバグ
  • SVG をダークカラーで使う際のエッジ処理
  • 同じ要素に繰り返し生成する際の初期化処理
  • Drawing.clear() で画像を非表示
  • QRCodeLimitLength の数え間違い

あたりに対応するため

を取り込み、また、生成する画像サイズをコンテナとなる div 要素に合わせるため

LANG: js
	this._elImage.style.width = "100%";   // added by osamu@big.jp
	this._elImage.style.height = "100%";  // added by osamu@big.jp

を追加したのが https://dora.bk.tsukuba.ac.jp/~takeuchi/qrcode.js になります。

LANG: php
  $id = uniqid();
  list($width, $height)=explode("x", $chs);
  list($level, $typeNumber)=explode("|", $chld);
  return '<span id="'.$id.'" style="display: inline-block; width: '.$width.'px; height: '.$height.'px; border: 8px solid white;"'.$class.'></span><script lang="javascript">{ new QRCode("'.$id.'", {text: "'.$str.'", width: '.($width*10).', height: '.($height*10).', typeNumber: '.$typeNumber.', correctLevel: QRCode.CorrectLevel.'.$level.'}); }</script>';

とすることで QR コードを表示できるようになりました。

コメント・質問





Counter: 4700 (from 2010/06/03), today: 4, yesterday: 0