SQLの窓

2016年11月29日


Android Studio で Twitter4J で画像付きツイート

Twitter API は使用回数制限があるので、現在それほど魅力的なものではありませんが、BOT 的な使い道や、ログとして使うのならば、Java のサンプルとしては魅力的ではあると思います。特に、Twitter4J を使うと、いとも簡単にツイートは可能なのです。

ダウンロードしてインストール

ダウンロードリンクは Twitter4J にあります。解凍して Android Studio のプロジェクトの libs に twitter4j-core-4.0.4.jar と twitter4j-async-4.0.4.jar をコピーします。

この後、メニューから add as library でもかまいませんが、app の gradle が以下のようになればいいです
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile files('libs/twitter4j-core-4.0.4.jar')

}

twitter4j-async-4.0.4.jar は非同期処理に必要ですが、gradle に記述する必要はありません。

ちなみに、他のライブラリを全てコピーするとエラーになります(たぶん内容が重複してるとかかもしれません)

設定ファイル

twitter4j.properties が必要です。場所は app\src\main\resources です。

エクスプローラから作成してテキストファイルを作成して、自分の Twitter から、右下の『開発』をクリックして、My Apps で アプリを作るか既存のアプリからトークンをコピペして twitter4j.properties に書き込むだけです

このへんは、Twitter の API を使った人なら常識の範疇ですが、初めてでもインターネットでたくさん紹介されているので問題は無いはずです。
debug=true
oauth.consumerKey=*********************
oauth.consumerSecret=******************************************
oauth.accessToken=**************************************************
oauth.accessTokenSecret=******************************************

実行コード

画像は、あらかじめ用意しておいてパスを取得しておきます。カメラを使用するなら、ギャラリーに登録までした後のパスをそのまま使えばいいし、一時的に使うだけなら後から削除でいいですね(もちろん終了イベント後に削除)

カメラを起点とした画像データの処理 / Android

配布ページのサンプルコードは、タイプミスがあるので注意

▼ 実行
				// 入力テキストを取得
				EditText editText = (EditText) rootView.findViewById(R.id.tweetText);
				String text = editText.getText().toString();

				// 入力なしなら、テキストを作成
				if ( text.trim().equals("") ) {
					Calendar cal = Calendar.getInstance();
					SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
					text = String.format( "%s %s", sf.format(cal.getTime()), "に Android からツイートしました");
				}
				// デバッグ用
				Log.i("lightbox",text);

				// 非同期のリスナーを作成
				TwitterListener listener = new TwitterAdapter() {
					@Override
					public void updatedStatus(Status status) {
						System.out.println("Successfully updated the status to [" +
							status.getText() + "].");
					}

					@Override
					public void onException(TwitterException te, TwitterMethod method) {
						if (method == TwitterMethod.UPDATE_STATUS) {
							te.printStackTrace();
						} else {
							throw new AssertionError("Should not happen");
						}
					}

				};

				// 非同期の準備
				AsyncTwitterFactory factory = new AsyncTwitterFactory();
				AsyncTwitter asyncTwitter = factory.getInstance();
				// リスナーを登録
				asyncTwitter.addListener(listener);

				// StatusUpdate ノインスタンスを作成
				// ※ 画像ツイートに必要
				StatusUpdate statusUpdate = new StatusUpdate(text);
				// 画像のファイルオブジェクト
				File file = new File( mainActivity.getimagePath() );
				// 画像を設定
				statusUpdate.media(file);

				// ツイート
				asyncTwitter.updateStatus(statusUpdate);


何故か非同期のサンプルが世の中に無かったですが、Android でツイートしようなんて思う人がたぶんもういないのでしょう( 殆どメリットや魅力が無いので )

updatedStatus イベント内の注意

この中は UI スレッドでは無いようなので、成功の後画面処理する場合は、適宜必要な UI スレッド呼び出しを実装します。

runOnUiThread( android.app.Activity )
UIスレッドで指定されたアクションを実行します。 現在のスレッドがUIスレッドの場合、アクションは直ちに実行されます。 現在のスレッドがUIスレッドでない場合、アクションはUIスレッドのイベントキューにポストされます。
posted by lightbox at 2016-11-29 20:10 | Comment(0) | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月26日


Google Chrome のコンソールが、『いらすとや』のイラストでいっぱいになった日

Google Plus でデベロッパーツールを開けてコンソールを見たら、大きな文字が色つきで表示されていたので、console.log で css が使えると解りました。

で、画像を使えるならサイトのメッセージとして強力だなぁと思い試してみると・・・

かなり強力です。

最大表示 

console.log("%c%s",
"background: url('https://lh3.googleusercontent.com/-lpfmi9vuwOg/WDVegEds_SI/AAAAAAAAhxc/0DzXoBQQlJw_L7KXSIi2UY0hEJwjiNcHwCHM/s128/internet_kanki_woman1.png'); font-size: 1240px;"," ")
※ 表示エリアを確保するのに、漢字スペース使ってます。半角スペースだと、思ったようになりませんでした。

一夜あけて、さらに調べてみると

Facebook の出力方法だけ特殊だったので、JavaScript のコードを拾ってみると、通常では右上に実行したスクリプトと行が出るようになってるのですが、それを消す実行方法が以下になります
setTimeout(console.log.bind(console,"%c%s",
"background: url('https://lh3.googleusercontent.com/-lpfmi9vuwOg/WDVegEds_SI/AAAAAAAAhxc/0DzXoBQQlJw_L7KXSIi2UY0hEJwjiNcHwCHM/s128/internet_kanki_woman1.png'); font-size: 1240px;"," "))

このコードが入っていたのが以下になります
'Chromedome',
['fbt'],
(function a(b, c, d, e, f, g, h) {
    if (c.__markCompiled) c.__markCompiled();
    g.start = function(i) {
        if (i.off || top !== window || !/(^|\.)facebook\.(com|sg)$/.test(document.domain)) return;
        var j = i.stop || h._("STOP\uff01"),
        k = i.text || h._("\u3053\u308c\u306f\u958b\u767a\u8005\u5411\u3051\u306e\u30d6\u30e9\u30a6\u30b6\u6a5f\u80fd\u3067\u3059\u3002Facebook\u6a5f\u80fd\u3092\u6709\u52b9\u306b\u3059\u308b\u305f\u3081\u307e\u305f\u306f\u8ab0\u304b\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30cf\u30c3\u30ad\u30f3\u30b0\u3059\u308b\u305f\u3081\u306b\u3001\u3053\u3053\u306b\u4f55\u304b\u3092\u30b3\u30d4\u30fc\u30fb\u8cbc\u308a\u4ed8\u3051\u3059\u308b\u3088\u3046\u306b\u8a00\u308f\u308c\u305f\u5834\u5408\u3001\u305d\u308c\u306f\u7b2c\u4e09\u8005\u304c\u3042\u306a\u305f\u306eFacebook\u30a2\u30ab\u30a6\u30f3\u30c8\u3078\u306e\u30a2\u30af\u30bb\u30b9\u3092\u5f97\u308b\u305f\u3081\u306e\u8a50\u6b3a\u30fb\u4e0d\u6b63\u884c\u70ba\u3067\u3059\u3002"),
        l = i.more || h._("\u8a73\u7d30\u306f{url}\u3092\u3054\u89a7\u304f\u3060\u3055\u3044\u3002", [h.param('url', 'https://www.facebook.com/selfxss')]);
        if ((window.chrome || window.safari) && !i.textonly) {
            var m = 'font-family:helvetica; font-size:20px; '; [[j, i.c1 || m + 'font-size:50px; font-weight:bold; ' + 'color:red; -webkit-text-stroke:1px black;'], [k, i.c2 || m], [l, i.c3 || m], ['', '']].map(function(s) {
                setTimeout(console.log.bind(console, '\n%c' + s[0], s[1]));
            });
        } else {
            var n = ['', ' .d8888b.  888                       888', 'd88P  Y88b 888                       888', 'Y88b.      888                       888', ' "Y888b.   888888  .d88b.  88888b.   888', '    "Y88b. 888    d88""88b 888 "88b  888', '      "888 888    888  888 888  888  Y8P', 'Y88b  d88P Y88b.  Y88..88P 888 d88P', ' "Y8888P"   "Y888  "Y88P"  88888P"   888', '                           888', '                           888', '                           888'],
            o = ('' + k).match(/.{35}.+?\s+|.+$/g),
            p = Math.floor(Math.max(0, (n.length - o.length) / 2));
            for (var q = 0; q < n.length || q < o.length; q++) {
                var r = n[q];
                n[q] = r + new Array(45 - r.length).join(' ') + (o[q - p] || '');
            }
            console.log('\n\n\n' + n.join('\n') + '\n\n' + l + '\n');
            return;
        }
    };
})
※ 後半の 8 が並んでる文字列は、Chrome と Safari 以外で使用されます。(要するにアスキーアートですね。)

IE は無視されてました。Firefox は、出てます。



※ Google Chrome で表示中ならば、F12 で見てみて下さい。出してみました

posted by lightbox at 2016-11-26 12:08 | Comment(0) | Google Chrome | このブログの読者になる | 更新情報をチェックする

汎用型 : Firebase に置いたデータで履歴書(作りかけ)のフォームを出力するサンプル



デモページ

Firebase データ

ごく一般的なデータから罫線(含連続)・BOX・テキスト・画像を出力して単票のフォームを作成します。特別な内容ではありませんが、Firebase 使うと結構汎用性も出るのでは無いかと思います。

印字部分
<?php
// ************************************************
// セッションとキャッシュなし
// ************************************************
session_cache_limiter('nocache');
session_start();

require_once("print.php");

// ************************************************
// フォーマットデータ
// ************************************************
$url = "https://freebase-654b7.firebaseio.com/tcpdf.json";
$file = @file_get_contents($url);
if ( $file !== false ) {
	// 連想配列形式で返す
	$result = json_decode( $file, true );

	if ( $result == null ) {
		// テキストのサイズ変更
		$pdf->SetFont('meiryo001', '', 20);
		// テキストの色
		$pdf->SetTextColor(255, 0, 0);
		user_text( $pdf, 10, 10, 'データが存在しません' );
		$pdf->Output("test_output.pdf", "I");
		exit();
	}

}


log_out(null);

$pdf->SetFont('meiryo001', '', 14);

$pdf->AddPage();

// ************************************************
// 印字コマンド実行
// ************************************************
foreach( $result['format'] as $obj ) {

	if ( $obj['type'] == 'image' ) {
		$pdf->Image($obj['f'], $obj['x'],$obj['y'],$obj['w'],$obj['h']);
		continue;
	}

	if ( $obj['type'] == 'text' ) {
		$pdf->SetFont('meiryo001', '', $obj['s']);
		user_text( $pdf, $obj['x'],$obj['y'], $obj['t'] );
		continue;
	}

	if ( $obj['type'] == 'rect' ) {
		$pdf->Rect($obj['x'],$obj['y'],$obj['w'],$obj['h']);
		continue;
	}

	if ( $obj['type'] == 'line' ) {
		$pdf->Line($obj['x'],$obj['y'],$obj['x']+$obj['w'],$obj['y']);
		continue;
	}

	if ( $obj['type'] == 'vline' ) {
		$pdf->Line($obj['x'],$obj['y'],$obj['x'], $obj['y']+$obj['h']);
		continue;
	}

	if ( $obj['type'] == 'lines' ) {

		$row = $obj['y'];
		
		for( $i = 0; $i < $obj['n']; $i++ ) {
			$pdf->Line($obj['x'], $row, $obj['x']+$obj['w'], $row);
			$row += $obj['p'];
		}

		continue;
	}

}


// ブラウザへ PDF を出力します
$pdf->Output("test_output.pdf", "I");
?>


TCPDF 依存部分(print.php)
<?php
define ('K_PATH_FONTS', "tcpdf/");
require_once('tcpdf/tcpdf.php');

// ************************************************
//   P or PORTRAIT(縦:既定)
//   L or LANDSCAPE(横))
// ---------------------------
//   pt: ポイント
//   mm: mm(既定)
//   cm: cm
//   in: インチ
// ---------------------------
//   用紙サイズ
// ---------------------------
// boolean $unicode = true
// ---------------------------
// String $encoding = 'UTF-8'
// ---------------------------
// boolean $diskcache = false
// ---------------------------
// PDF/A モード
// ---------------------------
// 
// 全てデフォルトなので $pdf = new TCPDF("L") でもOK
// ************************************************
$pdf = new TCPDF("P");
/*
$pdf = new TCPDF(
	"L",
	"mm",
	"A4",
	true,
	"UTF-8",
	false,
	false
);
*/

// ************************************************
// 設定
// ************************************************
$pdf->setFontSubsetting(false);
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetAutoPageBreak(false);


function log_out( $text ) {

	if ( $text == null ) {
		file_put_contents("log.txt","");
		return;
	}

	file_put_contents("log.txt", $text . "\n", FILE_APPEND );

}

# **********************************************************
#  テキスト印字
# **********************************************************
function user_text( $pdf, $x, $y, $text, $w=1, $h=0, $p="L" ) {

	text( $pdf, $x, $y, $text, $w, $h, $p );

	return $y;

}

# **********************************************************
# 位置指定印字
# ※ 改行コードで自動改行
# ※ ページあふれで自動改ページ
# ※ 内部印字位置は保存( 元に戻す )
# **********************************************************
function text( $pdf, $x=0, &$y=0, $txt='', $w=1, $h=0, $p="L" ) {

	$a = $pdf->GetX();
	$b = $pdf->GetY();

	$hm = $pdf->getPageHeight( );
	$dm = $pdf->getPageDimensions();
	$tm = $dm['tm'];
	$bm = $dm['bm'];

	$txt = str_replace( "\r","", $txt );
	$data = explode("\n", $txt );
	if ( count( $data ) > 1 ) {
		for( $i = 0; $i < count($data); $i++ ) {
			if ( $i == 0 ) {
				$pdf->SetXY( $x, $y );
			}
			else {
				$y += $pdf->getLastH();
				if ( $y >= ( $hm - $tm - $bm ) ) {
					$pdf->AddPage();
					$y = $tm;
				}
				$pdf->SetXY( $x, $y );
			}
			$pdf->Cell($w, $h, $data[$i], 0, 0, $p);
		}
	}
	else {
		$pdf->SetXY( $x, $y );
		$pdf->Cell($w, $h, $txt, 0, 0, $p);
	}
	$y += $pdf->getLastH();

	$pdf->SetXY($a,$b);

}


?>


関連する記事

1会話・IFRAME(TCPDF)テンプレート : jQuery + Bootstrap(css) + mmenu + Firebase + TCPDF + PHP




タグ:Firebase TCPDF PHP PDF
posted by lightbox at 2016-11-26 02:26 | Comment(0) | PHP + PDF | このブログの読者になる | 更新情報をチェックする

2016年11月22日


1会話・IFRAME(TCPDF)テンプレート : jQuery + Bootstrap(css) + mmenu + Firebase + TCPDF + PHP

デモページ




▼ このプラグインを使用して、ヘッダ部分で条件入力して PDF をその下の IFRAME へ表示するテンプレートです。
かつての FRAME のように画面下部に張り付く IFRAME を実装する jQuery プラグイン

全体の表示部分のテンプレート部分は、jQuery + Bootstrap(css) + mmenu で構成されており、独自の仕様ではありますが、見ればだいたい解るようには作成してあります。

アプリケーション部分

データ部分 : Firebase
PDF部分 : TCPDF
処理部分 : PHP

となっており、Firebase は PHP から直接読み込む必要があるので、Firebase Database REST API を使用しています。

TCPDF は文字列印字部分をユーザ関数化して、昔ながらの印刷用アルゴリズムで出力しています。

TCPDF 関数部分 : print.php
<?php
define ('K_PATH_FONTS', "tcpdf/");
require_once('tcpdf/tcpdf.php');

// ************************************************
//   P or PORTRAIT(縦:既定)
//   L or LANDSCAPE(横))
// ---------------------------
//   pt: ポイント
//   mm: mm(既定)
//   cm: cm
//   in: インチ
// ---------------------------
//   用紙サイズ
// ---------------------------
// boolean $unicode = true
// ---------------------------
// String $encoding = 'UTF-8'
// ---------------------------
// boolean $diskcache = false
// ---------------------------
// PDF/A モード
// ---------------------------
// 
// 全てデフォルトなので $pdf = new TCPDF("L") でもOK
// ************************************************
$pdf = new TCPDF("L");
/*
$pdf = new TCPDF(
	"L",
	"mm",
	"A4",
	true,
	"UTF-8",
	false,
	false
);
*/

// ************************************************
// 設定
// ************************************************
$pdf->setFontSubsetting(false);
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetAutoPageBreak(false);


// ************************************************
// デバッグ用ログ
// ************************************************
function log_out( $text ) {

	// クリア
	if ( $text == null ) {
		file_put_contents("log.txt","");
		return;
	}

	// 書き込み
	file_put_contents("log.txt", $text . "\n", FILE_APPEND );

}

// ************************************************
//  テキスト印字
// $p は L で左揃え、R で右揃え
// $w : 矩形領域の幅(1だと最低幅) : 右揃えに必要
// $h : 矩形領域の高さ
// http://tcpdf.penlabo.net/method/c/Cell.html
// ************************************************
function user_text( $pdf, $x, $y, $text, $w=1, $h=0, $p="L" ) {

	text( $pdf, $x, $y, $text, $w, $h, $p );

	return $y;

}

// ************************************************
// 位置指定印字
// ※ 改行コードで自動改行
// ※ ページあふれで自動改ページ
// ※ 内部印字位置は保存( 元に戻す )
// ************************************************
function text( $pdf, $x=0, $y=0, $txt='', $w=1, $h=0, $p="L" ) {

	$a = $pdf->GetX();
	$b = $pdf->GetY();

	$hm = $pdf->getPageHeight( );
	$dm = $pdf->getPageDimensions();
	$tm = $dm['tm'];
	$bm = $dm['bm'];

	$txt = str_replace( "\r","", $txt );
	$data = explode("\n", $txt );
	if ( count( $data ) > 1 ) {
		for( $i = 0; $i < count($data); $i++ ) {
			if ( $i == 0 ) {
				$pdf->SetXY( $x, $y );
			}
			else {
				$y += $pdf->getLastH();
				if ( $y >= ( $hm - $tm - $bm ) ) {
					$pdf->AddPage();
					$y = $tm;
				}
				$pdf->SetXY( $x, $y );
			}
			$pdf->Cell($w, $h, $data[$i], 0, 0, $p);
		}
	}
	else {
		$pdf->SetXY( $x, $y );
		$pdf->Cell($w, $h, $txt, 0, 0, $p);
	}
	$y += $pdf->getLastH();

	$pdf->SetXY($a,$b);

}


?>

フォントには、メイリオを使用しています。以下を参照して下さい
TCPDF で非埋め込み型として『メイリオ』を使う手順

フォントファイルは、define ('K_PATH_FONTS', "tcpdf/"); で指定した場所です。
※ MSゴシックとMS明朝も同梱しています。

印刷用アルゴリズム部分
<?php
// ************************************************
// セッションとキャッシュなし
// ************************************************
session_cache_limiter('nocache');
session_start();

// ************************************************
// 印字関数
// ************************************************
require_once("print.php");

log_out(null);
$rh		= 8;	// 行間の距離
$hh		= 40;	// ヘッダーの下の次の印字位置
$rmax	= 15;	// ページ内の最大明細行数
// ************************************************
// フォント
// 第二引数は フォント・スタイル(空文字で標準)
// http://tcpdf.penlabo.net/method/s/SetFont.html
// ************************************************
$pdf->SetFont('meiryo001', '', 14);

$pdf->AddPage();

// ************************************************
// データベース
// ※ パスの先にデータが無い事は考慮していない
// ************************************************
$url = "https://freebase-654b7.firebaseio.com/class.json";
$file = @file_get_contents($url);
if ( $file !== false ) {
	// 連想配列形式で返す
	$result = json_decode( $file, true );

	if ( $result == null ) {
		// テキストのサイズ変更
		$pdf->SetFont('meiryo001', '', 20);
		// テキストの色
		$pdf->SetTextColor(255, 0, 0);
		user_text( $pdf, 10, 10, 'データが存在しません' );
		$pdf->Output("test_output.pdf", "I");
		exit();
	}

}

$url = "https://freebase-654b7.firebaseio.com/refdata/syozoku.json";
$file = @file_get_contents($url);
if ( $file !== false ) {
	$syozoku = json_decode( $file, true );

	// log_out(print_r($syozoku,true));

	if ( $syozoku == null ) {
		$syozoku = json_decode( '{"0001":"営業部第一","0002":"技術開発","0003":"人事部"}', true );
	}
}

// ************************************************
// 印刷用変数
// ************************************************
$counter = 0;	// ページ用カウンタ
$lcount = 0;	// 行数カウンタ
$sum = 0;		// ページ毎合計
$sumall = 0;	// 総合計

// 最初のヘッダーを出力して現在位置(y)を取得
$cur_position = print_header( $pdf );

// ************************************************
// 処理ループ
// ************************************************
foreach( $result as  $key => $obj ) {

	if ( $_GET['fld1'] != '' ) {
		if ( mb_strpos($obj["name"],$_GET['fld1'],0,"utf-8") === false ) {
			continue;
		}
	}

	$lcount++;
	if ( $lcount > $rmax ) {
		$counter++;
		$lcount = 0;
		user_text( $pdf, 108, $cur_position, "ページ計" );
		user_text( $pdf, 138, $cur_position, number_format($sum), 20, 0, "R" );
		$sum = 0;

		// 新しいページ
		$pdf->AddPage();
		$cur_position = print_header( $pdf );
	}

	user_text( $pdf, 10, $cur_position, $obj["code"] );
	user_text( $pdf, 28, $cur_position, $obj["name"] );
	user_text( $pdf, 51, $cur_position, $obj["furi"] );
	user_text( $pdf, 95, $cur_position, $syozoku[$obj["syozoku"]] );
	if ( $obj["sex"] == "0" ) {
		$pdf->Image("man.png", 125+3.2, $cur_position, 5, 5);
	}
	else {
		$pdf->Image("lady.png", 125+3.2, $cur_position, 5, 5);
	}
	user_text( $pdf, 138, $cur_position, number_format($obj["kyuyo"]), 20, 0, "R" );
	$sum += $obj["kyuyo"];
	$sumall += $obj["kyuyo"];

	// 手当ては文字列として設計されているので数値化
	user_text( $pdf, 163, $cur_position, number_format($obj["teate"]+0), 20, 0, "R" );
	user_text( $pdf, 183, $cur_position, $obj["birthday"] );

	$cur_position += $rh;

}

// ************************************************
// 最大行数が、最後の2行が入るように想定されて
// いるので、以下は無条件に出力する
// ************************************************

user_text( $pdf, 108, $cur_position, "ページ計" );
user_text( $pdf, 138, $cur_position, number_format($sum), 20, 0, "R" );

// 次の行
$cur_position += $rh;
user_text( $pdf, 108, $cur_position, "総合計" );
user_text( $pdf, 138, $cur_position, number_format($sumall), 20, 0, "R" );


// ブラウザへ PDF を出力します
$pdf->Output("test_output.pdf", "I");

// デバッグ用
file_put_contents( "../log/global.txt", print_r($GLOBALS, true) );
//file_put_contents( "../log/global.txt", "[_GET]\n".print_r($_GET, true)."\n"."[_POST]\n".print_r($_POST, true)."\n"."[_COOKIE]\n".print_r($_COOKIE, true)."\n"."[_SESSION]\n".print_r($_SESSION, true) );

// ***********************************************
// ヘッダ印字
// ***********************************************
function print_header( $pdf ) {

	global $counter;

	$page_info = $pdf->getPageDimensions();
	$cur_position = $page_info['tm'];	// トップマージン

	user_text( $pdf, 125, $cur_position, "社員一覧表" );
	user_text( $pdf, 224, $cur_position, "ページ :" );
	user_text( $pdf, 250, $cur_position, number_format($counter+1), 5, 0, "R" );

	// 2行
	$cur_position += $GLOBALS['rh'] * 2;
	user_text( $pdf, 10, $cur_position, "コード" );
	user_text( $pdf, 28, $cur_position, "名前" );

	user_text( $pdf, 51, $cur_position, "フリガナ" );
	user_text( $pdf, 95, $cur_position, "所属" );
	user_text( $pdf, 125, $cur_position, "性別" );

	user_text( $pdf, 138, $cur_position, "給与", 20, 0, "R" );
	user_text( $pdf, 163, $cur_position, "手当", 20, 0, "R" );
	user_text( $pdf, 183, $cur_position, "生年月日" );

	$cur_position += $GLOBALS['rh'];

	// 直線のスタイル
	$pdf->SetLineStyle(
		array(
			'width' => 0.4,
			'cap' => 'round',
			'join' => 'round',
			'dash' => 0,
			'color' => array(0xFF,0xC0,0xCB)	// pink
		)
	);
	// 直線
	$pdf->Line(
		$page_info['lm'],	// 左マージン
		$cur_position+1.5,
		$page_info['wk']-$page_info['lm']-$page_info['rm'],	// ページ幅 - 左右マージン
		$cur_position+1.5);
	
	return $GLOBALS['hh'];

}
?>

いろいろなページを構成する定数を元に、ヘッダと明細部分を分けてコントロールするオーソドックスなものです。改ページは、次に印字すべき位置があらかじめ決めた行数を超えた時に処理され、次のページの先頭には必ずヘッダ部分が出力されます。

このサンプルでは、合計部分の行数はあらかじめ想定してあるため、ページ内の明細行数は少なめになっていますが、本来は合計部分も改ページコントロールの対象にする事が昔は多かったですが、合計部分が固定位置となるような、専用の用紙であればこちらに近くなるはずです。

▼ Firebase database データ
https://freebase-654b7.firebaseio.com/class.json



posted by lightbox at 2016-11-22 20:24 | Comment(0) | IFRAME パッケージ | このブログの読者になる | 更新情報をチェックする

2016年11月21日


かつての FRAME のように画面下部に張り付く IFRAME を実装する jQuery プラグイン

デモページ


デモページでは、実装コードを解りやすくする為に、IFRAME 内には大きな画像を表示していますが、本来はヘッド部分で値を入力して送信の結果を IFRAME に表示する問い合わせ系アプリケーションに利用しやすくなっています。

プラグインを実装したテンプレートページ


プラグインのコード

https://lightbox.sakura.ne.jp/demo/template/static/pass1iframe/std/lboxiframe.js
$.fn.extend({ 
	lboxiframe: function(option){

		var parent = $(this);

		$("<iframe name=\""+ option.name +"\">").insertAfter($(this))
			.attr("src",option.src)
			.attr("id",option.id)
			.attr("frameborder", "0")
			.css("width", "100%" )
			;

		function control_page_iframe(iframe,parent) {
			var height = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
			var head_height = parent.get(0).clientHeight;

			// -8 は、IE と Firefox 用。Google Chrome は 0 で正しくフィットするが、
			// IE と Firefox では、BODY のスクロールバーが表示されてしまうので調整
			iframe.css("height", (height - parseInt(head_height)-8) + "px");
		}

		$(window).on("resize", function(){
	
			$("#" + option.id ).hide();
			control_page_iframe( $("#" + option.id ), parent );
			$("#" + option.id ).show();
	
		});

		control_page_iframe( $("#" + option.id ), parent );

	}
});
Google Chrome では ブラウザにきっちり収まるのですが、Firefox と IE では、下部に多少の余白が発生します。クロスブラウジングは避けたかったのと、補正値が過去変化したのでこのようになってますが、簡単で一般的なアプリケーションであれば、特に問題は出ないと思います。

呼び出し

対象とした div の下に IFRAME を埋め込みます。
$(function(){
	$( "#wrapper" ).lboxiframe({
		src : "simple-image.html",
		id : "my_iframe",
		name : "my_iframe"
	});
});



posted by lightbox at 2016-11-21 21:05 | Comment(0) | プラグイン:jQuery | このブログの読者になる | 更新情報をチェックする

2016年11月17日


画像ファイルが存在した時のみ、表示する jQuery の記述

ローカルにある画像ファイルで、PHP ならば file_exists で簡単ですが、ブラウザから画像の存在チェックするにはどうするか・・・と考えてできた結論です。

php で画像を返して、image 要素の src で使い、存在しない場合は透明の小さな png を表示するのも、スッキリしていいかなと思いましたが、取り急ぎ JavaScript だけで解決する方法です。

テストすると、画像ファイルがある時だけ onload イベントが発生したので、jQuery で全て書くために、数珠繋ぎをループするという形態になっています。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<style>
.target {
	border: solid 1px #000;
	padding: 15px;
}
</style>

<table>
<tr> <td class="target"></td> </tr>
<tr> <td class="target"></td> </tr>
<tr> <td class="target"></td> </tr>
<tr> <td class="target"></td> </tr>
</table>

<script>

var img = [
"https://lightbox.sakura.ne.jp/toolbox/image/__.png", 
"https://lightbox.sakura.ne.jp/toolbox/image/script.png", 
"https://lightbox.sakura.ne.jp/toolbox/image/jquery.png", 
"https://lightbox.sakura.ne.jp/toolbox/image/__.png", 
];

$(".target").each(function( idx ){

	$("<img>").data("container", $(this) ).on("load",function(){

		$(this).data("container").append( this );

	}).prop("src",img[idx]);

});

</script>



$("<img>") が createElement にあたります。jQuery では値取得以外は自分自身が返るので、そのまま自身に設置する場所のコンテナを data で登録し、そのまま on で load イベントを登録します。その後、src に url を設定すれば、onload イベントに入り、data でコンテナを取り出して、そこへ自身を追加します。

この処理をコンテナぶん each でループしています。


posted by lightbox at 2016-11-17 22:11 | Comment(0) | jQuery | このブログの読者になる | 更新情報をチェックする

2016年11月08日


内部ストレージを起点とした画像データの処理 / Android


Uri => File => FileInputStream => Bitmap というルートもあります

ストレージから画像を ImageView に表示する

※ ファイル名やフォルダは既知である事を前提としています。
// *************************************
// パスの取得
// *************************************
String path = String.format(
	"%s/firebase/20161108_011922.jpg",
	Environment.getExternalStorageDirectory().getPath()
);

// *************************************
// ImageView
// *************************************
Bitmap image = null;
FileInputStream fis;
try {
	fis	= new FileInputStream(path);
	image = BitmapFactory.decodeStream(fis);
} catch (Exception e) {
	e.printStackTrace();
}

// 画像データが取得されていたら
if ( image != null ) {
	// imageView へ表示
	imageView.setImageBitmap(image);
}

パスから FileInputStream を作成して、BitmapFactory.decodeStream でビットマップに変換します。画像表示は ImageView の setImageBitmap です。

Bitmap を使用した画像の回転
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);
(※参考) ImageView を使用して画像を回転

ギャラリーから画像を ImageView に表示する

ギャラリーから返されるのは、Uri です。Uri を直接使用する setImageURI を使用します。

setImageURI(null) は、キャッシュされないようにする効果が期待できると想像しています。
// ギャラリーの呼び出し
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 10);		// 10 は任意

// -------------------------------

// ギャラリーからの戻り
// ※ onActivityResult は MainActivity のメソッドです
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);

	if (requestCode == 10 && resultCode == Activity.RESULT_OK) {
		if (data != null) {
			Uri uri = data.getData();
			imageView.setImageURI(null);
			imageView.setImageURI(uri);
		}

	}
}

(※参考) Exif情報を利用して、画像を回転

Firebase の putFile と putStream

どちらも大差はありません。アップロード前に加工が必要なら、putStream になるかもしれませんし、コードを簡潔にするには、putFile だと例外処理がありませんから、キャッシュに書き込んでからアップロードしてもいいかもしれません。
// *************************************
// FireBase putFile
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
UploadTask uploadTask = imageRef.putFile(file);
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
	@Override
	public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

		Log.i("lightbox","アップロードに成功しました");
		long size = taskSnapshot.getMetadata().getSizeBytes();
		Log.i("lightbox",String.format("サイズ : %d",size));

	}
}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {

		Log.i("lightbox","アップロードに失敗しました");
	}
});


// *************************************
// FireBase putStream
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
InputStream stream = null;
try {
	stream = new FileInputStream(new File(path));
} catch (Exception e) {
	e.printStackTrace();
}
if ( stream != null ) {
	UploadTask uploadTask = imageRef.putStream(stream);
	uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
		@Override
		public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

			Log.i("lightbox", "アップロードに成功しました");
			long size = taskSnapshot.getMetadata().getSizeBytes();
			Log.i("lightbox", String.format("サイズ : %d", size));

		}
	}).addOnFailureListener(new OnFailureListener() {
		@Override
		public void onFailure(@NonNull Exception e) {

			Log.i("lightbox", "アップロードに失敗しました");
		}
	});
}




posted by lightbox at 2016-11-08 13:54 | Comment(0) | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月07日


カメラを起点とした画像データの処理 / Android

全てのソースコードはこちら
▼ テスト用のカメラ処理はこちら
Android : 画像関連のテスト用カメラアプリ




byte 配列を Bitmap に変換して ImageView に表示する

1) BitmapFactory.decodeByteArray => bitmap
2) imageView.setImageBitmap(bitmap);
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);

// 環境依存(これが無いと落ちるデバイスもある)
imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// imageView へ表示
imageView.setImageBitmap(image2);


byte 配列を内部ストレージに保存

byte[] data を FileOutputStream で write
// 内部ストレージに保存
FileOutputStream jpg;
try {
	jpg = new FileOutputStream(imagePath);
	jpg.write(data);
	jpg.close();

	// ギャラリーに反映
	MediaScannerConnection.scanFile(
		MainActivity.this,
		new String[]{imagePath},
		new String[]{"image/jpg"},
		null);

} catch (Exception e) {
	e.printStackTrace();
}

保存用の内部ストレージのパスの取得
	// *************************************
	// 保存用内部ストレージパス取得
	// *************************************
	private String buildPath(String folder, String ext) {

		String path = null;

		// 内部ストレージにフォルダを作成
		String TargetRootDir
			= String.format("%s/%s",Environment.getExternalStorageDirectory().getPath() , folder );
		File file = new File(TargetRootDir);
		// ディレクトリ初期作成
		if (!file.exists()) {
			if (file.mkdir() == false) {
				Log.i("lightbox", "ディレクトリを作成できませんでした");
				return path;
			}
		}

		// 画像保存パス
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
		path = String.format("%s/%s.%s", TargetRootDir,sf.format(cal.getTime()),ext);

		Log.i("lightbox",path);

		return path;

	}


Firebase よりbyte 配列で画像を取得する

本来は、File か ストリームで取得しますが(メモリ効率)、カメラの画像が byte 配列なので、putBytes でそのままアップロードが可能です。なので、その反対である getBytes のサンプルです

putBytes のサンプルはこちら

Stream でダウンロードして ImageView に表示する
ファイルとしてダウンロードしてギャラリーに保存する
FirebaseStorage storage;
StorageReference storageRef;
StorageReference imageRef;

storage = FirebaseStorage.getInstance();
storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/image");

imageRef = storageRef.child("sworc.png");
// ダウンロード
imageRef.getBytes(MainActivity.MAX_SIZE_BYTE)
	.addOnSuccessListener(new OnSuccessListener<byte[]>() {
		@Override
		public void onSuccess(byte[] bytes) {
			if (bytes != null) {
				Bitmap image = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
				imageView.setImageBitmap(image);
			}
		}
	}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {
		Log.i("lightbox","データ取得に失敗しました");
		e.printStackTrace();
		Toast.makeText(
			MainActivity.this,
			"Firebase からのダウンロードに失敗しました",
			Toast.LENGTH_LONG)
			.show();
	}
});


関連する資料

ImageView を起点とした画像データの処理 / Android




posted by lightbox at 2016-11-07 22:25 | Comment(0) | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月06日


WEB ブラウザで WEBカメラから静止画像を canvas へ転送して、その画像をローカルへ保存するテンプレート

デモページ

WEBカメラの画像をローカルに保存する事が目的です(IE は WebRTC が使えないので、通常動画で代替)。Webカメラが無い場合は、動画で代替えます。

IE 以外は toDataURL を アンカーの href にセットしてダウンロードする事ができますが、例によって IE は特別な方法を取りますIE11 は長い URL がダメ

根本的に IE は無理です。このあたりの制限はずっと昔からありました。Edge は正しくデータとして取れるのですが、アンカーの href としては使えず、img の src としてならば使えます。いずれにしても、ダウンロードは別の方法である Blob を使うのですが、jpeg としてダウンロードするには、さらにひと工夫が必要そうです。できそうな資料はありましたが、動作は未確認です。
動作確認しました。IE11 で JPEG 保存可能です。
	// IE の場合
	if ( typeof(MSBlobBuilder) != "undefined" ) {
		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var bin = atob(jpeg.split(',')[1]);
		var buffer = new Uint8Array(bin.length);
		for (var i = 0; i < bin.length; i++) {
		  buffer[i] = bin.charCodeAt(i);
		}
		var blob = new Blob([buffer.buffer], {type: "image/jpeg"});
		navigator.msSaveBlob(blob, "canvas.jpg" );
	}


<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width initial-scale=1.0 minimum-scale=1.0 maximum-scale=1.0 user-scalable=no" name="viewport">
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="https://lightbox.sakura.ne.jp/homepage/WinOfSql.ico">


</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>

<style>
input {
	font-size: 20px;
}
#camera {
	width: 400px;
	height: 300px;
}
#canvas,#camera {
	border: 1px solid #000;
}
</style>

<input id="copy" type="button" value="copy">
<input id="save" type="button" value="save">
<br>
<br>
<video id="camera" autoplay></video>
<canvas id="canvas" width="400" height="300"></canvas>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options.positionClass = "toast-top-center";

if ( $.isMobile ) {
	$("#camera").css("width","100%");
	$("#canvas").css("width","100%");
}

// カメラ用 video 要素(DOM オブジェクト)
var camera;
// 静止画用 canvas 要素(DOM オブジェクト)
var cancas;

check();

// *************************************
// Canvas へコピー
// *************************************
$("#copy").on( "click", function(){

	camera = $("#camera").get(0);
	canvas = $("#canvas").get(0);
	var ctx = canvas.getContext('2d');

	// カメラから キャンバスに静止画を描く
	ctx.drawImage(camera, 0, 0, 400, 300);
});

// *************************************
// Canvas の画像を保存
// *************************************
$("#save").on( "click", function(){

	// IE の場合
	if ( typeof(MSBlobBuilder) != "undefined" ) {

//		var blobBuilderObject = new MSBlobBuilder();
//		blobBuilderObject.append( canvas.msToBlob() );
//		navigator.msSaveBlob(blobBuilderObject.getBlob(), "canvas.png" );	// PNG

		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var bin = atob(jpeg.split(',')[1]);
		var buffer = new Uint8Array(bin.length);
		for (var i = 0; i < bin.length; i++) {
		  buffer[i] = bin.charCodeAt(i);
		}
		var blob = new Blob([buffer.buffer], {type: "image/jpeg"});
		navigator.msSaveBlob(blob, "canvas.jpg" );

	}
	else {
		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var download = $("<a></a>").appendTo("body").css("display","none");
		download.prop({"href" : jpeg, "download": "canvas.jpg" });
		download.get(0).click();
		download.remove();
	}

});

// *************************************
// navigator.getUserMedia チェック
// *************************************
function check() {

	if ( !navigator.mediaDevices ) {
		var api = [
			"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
		]

		$.each(api,function(idx){
			if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
				return false;
			}
		});

		if ( !navigator.getUserMedia ) {
			error("WebRTC を使用できません");
			return;
		}
	}

	// WEBカメラの初期化
	init();
}

// *************************************
// カメラストリーム
// *************************************
function init() {

	camera = $("#camera").get(0);

	if ( navigator.mediaDevices ) {
		console.log("navigator.mediaDevices.getUserMedia");
		navigator.mediaDevices.getUserMedia({video: true})
		.then(function(stream){
			camera.src = window.URL.createObjectURL(stream);
		})
		.catch(function(err){
			error(err.name);
		});	}
	else {
		console.log("navigator.getUserMedia");
		navigator.getUserMedia(
			{video: true}, 
			function(stream) {
				camera.src = window.URL.createObjectURL(stream);
			},
			function(err){
				error(err.name);
			}
		);
	}
}

// *************************************
// エラー処理
// ※ 動画で代替
// *************************************
function error( message ) {
		toastr.error( message + "<br>代替として動画を表示します" );
		$("#camera")
		.prop({ 
			"loop" : true, "muted" : true, "controls" : true,
			"src" : "https://lightbox.sakura.ne.jp/demo/mp4/waterfall-free-video9.mp4"
		})
		.css("border", "solid 1px #000");
}
</script>
</body>
</html>

関連する記事

WEB ブラウザで WEBカメラをテストするテンプレート / Android Chrome でテストすると【今は】カメラの front と back を切り替えできます

参考にすべき資料

Microsoft: キャンバスの画像をローカルに保存する
Microsoft : BlobBuilder を使ってファイルを作成する

Canvas に描いた画像を png などの形式の Blob に変換する方法



posted by lightbox at 2016-11-06 02:11 | Comment(0) | WEBブラウザ | このブログの読者になる | 更新情報をチェックする

WEB ブラウザで WEBカメラをテストするテンプレート / Android Chrome でテストすると【今は】カメラの front と back を切り替えできます

navigator.mediaDevices.getUserMedia() と navigator.mediaDevices.enumerateDevices() を使うといいみたいです。(警告が出なくなりました。)

navigator.mediaDevices を使ったカメラのサンプル
デモページ ※ Android で見るのがおもしろいです。
MediaStreamTrack.getSources() は、非推奨で Chrome の 56 で削除される見通しです。
これも、Firebase のテストをする一環で、カメラの画像を Firebase storage に保存する為に使用しています。 いまのところ、WebRTC が動作してカメラが動くのを確認したのは Google Chrome と Firefox と Microsoft Edge と Android の Chrome です。 ※ Chrome は https でないと動作しません カメラが使えない場合も、Firebase で 静止画のアップロードを行いたいので、その場合はエラー処理で動画を表示するようにしています。 Chrome では、MediaStreamTrack.getSources() で、カメラの一覧を取得できるので、コンボボックスで切り替えれるようにしています。 カメラの切り替えは、WebRTC の停止が困難なので(というか Android では再起動してしまう)、現状はコードでするよりリロードするのが簡単なので、location.hash を使用してリロードしています。 できる限り、jQuery を使用しています カメラの扱いは DOM ですが、イベントやら画面の扱いは jQuery が簡単です。特に、コンボボックスを追加して body に追加する部分は直感的にできます。 ▼ Google Chrome WEBカメラによって、一覧が複数あったり、一つだったりしました ▼ Android 4が front で、5が back と label に表示されますが、2番目のカメラ(?)でも、back カメラになりました。 ▼ Google Chrome これは、一つしかなかったです(安いWEBカメラ)
<video id="camera" autoplay></video>
<br>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options.positionClass = "toast-top-center";

if ( $.isMobile ) {
	$("#camera").css("width","100%");
}

// カメラ用 video 要素(DOM オブジェクト)
var camera;
// スマホ用コンボボックス(jQuery オブジェクト)
var select;
// カメラの一覧
var camera_data;
// 現在のカメラの ID
var camera_id = "default";

check();

// *************************************
// navigator.getUserMedia チェック
// *************************************
function check() {

	var api = [
		"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
	]

	$.each(api,function(idx){
		if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
			return false;
		}
	});

	if ( !navigator.getUserMedia ) {
		error("WebRTC を使用できません");
		return;
	}

	// WEBカメラの初期化
	init();
}

// *************************************
// カメラストリーム
// *************************************
function init() {

	camera = $("#camera").get(0);

	if ( location.hash != "" ) {
		camera_id = location.hash;
		camera_id = camera_id.replace("#","");
		console.log(camera_id);
		$("<div>"+camera_id+" を選択しました</div>").appendTo("body");
	}

	navigator.getUserMedia(
		{video: {
			optional: [{sourceId: camera_id }]
		}}, 
		function(stream) {
			camera.src = window.URL.createObjectURL(stream);
		},
		function(err){
			error(err.name);
		}
	);

	list();

}

// *************************************
// MediaStreamTrack が使える場合
// カメラ一覧をコンボボックス化
// *************************************
function list() {

	MediaStreamTrack.getSources(function(data){

		if ( data.length > 0 ) {

			var div = $("<div></div>");

			camera_data = data;

			select = $("<select></select>");
			select.append("<option value=\"none\"></option>");
			for( i = 0; i < data.length; i++ ) {
				select.append("<option value=\""+ data[i].id +"\">"+(i+1)+") "+data[i].kind+"</option>");
				div.append("<p>label "+(i+1)+"<br>"+data[i].label +"</p>");
			}
			select.appendTo("body");
			div.appendTo("body");

			console.dir(camera_data);

			select.on("change",function(){
				if ( $(this).val() != "none" ) {
					location.hash=$(this).val();
					location.reload(true);
				}
			});

		}

	});

}

// *************************************
// エラー処理
// ※ 動画で代替
// *************************************
function error( message ) {
		toastr.error( message + "<br>代替として動画を表示します" );
		$("#camera")
		.prop({ 
			"loop" : true, "muted" : true, "controls" : true,
			"src" : "https://lightbox.sakura.ne.jp/demo/mp4/waterfall-free-video9.mp4"
		})
		.css("border", "solid 1px #000");
}
</script>

関連する記事

WEB ブラウザで WEBカメラから静止画像を canvas へ転送して、その画像をローカルへ保存するテンプレート




posted by lightbox at 2016-11-06 01:18 | Comment(0) | WEBブラウザ | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します


Windows
container 終わり

フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり