- 投稿日:2019-10-09T23:52:20+09:00
PDFBoxを使って既存のPDF文書に線を引く
PDFBox を使って既存のPDF文書に対して線を書き込みたいというニッチなニーズがあったので、その方法を紹介します。例えば、行政に提出する様式で、申請内容に応じて関係のない部分に取り消し線を引くといったケースを想定しています。
確認環境
- PDFBox 2.0.15
- AdoptOpenJDK 11.0.3+7
- macOS 10.14.6
実現方法
- PDPageContentStream を初期化するときに AppendMode.APPEND を指定してあげるのがポイント
- デフォルトだと AppendMode.OVERWRITE なので、元の内容がクリアされて真っ白な背景になってしまう
- PDF の座標系はページの左下が (0, 0) で右上に向かって大きくなる
- 描画位置の特定方法は後述
void line() throws IOException { try (InputStream is = getInputStream("sample.pdf"); PDDocument doc = PDDocument.load(is)) { PDPage page = doc.getPage(0); PDPageContentStream contents = new PDPageContentStream(doc, page, AppendMode.APPEND, false); contents.moveTo(290f, 808f); // 始点座標 contents.lineTo(365f, 808f); // 終点座標 contents.setLineWidth(1f); // 線の太さ contents.stroke(); // 描画 contents.moveTo(290f, 805f); // 始点座標 contents.lineTo(365f, 805f); // 終点座標 contents.setLineWidth(1f); // 線の太さ contents.stroke(); // 描画 contents.close(); doc.save("/tmp/output.pdf"); } }描画位置の特定
適切なツールを使うとかもっと賢い方法がありそうですが、分からなかったので泥臭く自分で座標系を描画してあげることにします。
void grid() throws IOException { try (InputStream is = getInputStream("sample.pdf"); PDDocument doc = PDDocument.load(is)) { PDPage page = doc.getPage(0); // ページ左下から右上に向かって格子線を引く PDPageContentStream contents = new PDPageContentStream(doc, page, AppendMode.APPEND, false); PDRectangle bbox = page.getBBox(); for (float x = 0; x < bbox.getWidth(); x += 10) { contents.moveTo(bbox.getLowerLeftX() + x, bbox.getLowerLeftY()); contents.lineTo(bbox.getLowerLeftX() + x, bbox.getUpperRightY()); contents.setLineWidth((x % 100 == 0) ? 2f : 0.5f); contents.stroke(); } for (float y = 0; y < bbox.getHeight(); y += 10) { contents.moveTo(bbox.getLowerLeftX(), bbox.getLowerLeftY() + y); contents.lineTo(bbox.getUpperRightX(), bbox.getLowerLeftY() + y); contents.setLineWidth((y % 100 == 0) ? 2f : 0.5f); contents.stroke(); } contents.close(); doc.save("/tmp/output.pdf"); } }あとは左下から目盛りを数えてやれば、描画したい位置の大まかな座標を特定できるというわけです。
もっと良い方法があれば、ぜひ教えて下さい
- 投稿日:2019-10-09T22:11:41+09:00
【NetBeans】起動しなくなってしまった時の話
NetBeansをいつものように起動しようとしたら、何も出ないままいつまで経っても起動しない…。前回は普通に起動していたのに…。このようになった時のことをここに書き残しておく。
*環境
OS: Windows 10
NetBeans: Apache NetBeans IDE 11.1
Java: JDK 13*netbeans.conf(下の方でも記述あり)を編集したら同様に起動しなくなってしまうことがあるので、もしnetbeans.confを直近で編集したのであればまず元に戻してみよう。それでもダメだった時の話が以下のとおり。
エラーの内容
デスクトップ上とかのショートカットで起動しようとすると、何も起こらない。エラーメッセージなど何も出ない。ここで、コマンドプロンプトで起動してみる。
C:\Program Files\NetBeans-11.1\netbeans\bin>netbeans.exe
こちらでもやはり起動に失敗するが、エラーメッセージが出る。
(再現しようと思ったが、できなかったのでどのようなエラーメッセージかは出せませんでした。すみません。)
そのエラーメッセージの中で、ログを出している場所を教えてくれるので、そのログを開いてみよう。場所の例としては、
C:\Users\【ユーザー名】\AppData\Local\Temp
で、ログファイルの名称はhs_err_pid18004.logというような感じ。(数字はプロセスIDなので、きっと違う番号になっている。)中身の最初の方は以下のような感じ。#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007fff2a50f98a, pid=18004, tid=12052
#
# JRE version: Java(TM) SE Runtime Environment (13.0+33) (build 13+33)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (13+33, mixed mode, sharing, tiered, >compressed oops, g1 gc, windows-amd64)
# Problematic frame:
# C [awt.dll+0x8f98a]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of >Windows
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.EXCEPTION_ACCESS_VIOLATIONがエラーの内容であるわけだが、メモリの読み取り違反ではないかと推測される。どこで問題が起こったかというと、
# C [awt.dll+0x8f98a]
の所に書かれている。
awt.dllというのは、java\jdk-13\binの中にあるファイル。これはいじりようがないファイル…。今回の解決方法
最終的には起動に成功したわけだが、どうしたかというと、
JDKをバージョンダウンした
ことである。JDKを13から12.0.2にバージョンダウンして、
C:\Program Files\NetBeans-11.1\netbeans\etc
にあるnetbeans.confの中身の一部を以下のように書き換える。(数字はバージョンに合わせる)
netbeans_jdkhome="C:\Program Files\Java\jdk-12.0.2"
*Windowsでは直に書き換えられない(保存できない)と思うので、そのファイルをどこかにコピーして書き換えて、その書き換えたファイルをペーストしよう。これで起動に成功!
同じような目に遭った方がいたら、参考にしてくれると幸いです。
余談
起動に成功したから、この記事を書くために再現しようとnetbeans.confを戻したらなぜかそれでも起動できた。一体何だったんだ…。
- 投稿日:2019-10-09T17:25:41+09:00
\tの使い方 macとWindowsで異なる-java
文字列内で使う時
System.out.println("美味しい\tみかん");コンソール: 美味しい みかん
変数を使う時
String sample1 = "ふつうの"; String sample2 = "いちご"; System.out.println(sample1 + "\t" + sample2);コンソール: ふつうの いちご
Windows
System.out.println(sample1 + "\t" + sample2); System.out.println(sample1 + "¥t" + sample2);\と¥両方が使える
mac
System.out.println(sample1 + "\t" + sample2); System.out.println(sample1 + "¥t" + sample2); //macではバックスラッシュと円記号が区別されるコンソール:
ふつうの いちご
ふつうの¥tいちご
- 投稿日:2019-10-09T14:44:33+09:00
【Azure】無料でJava アプリを作成してみた〜FTPで接続 編〜【初心者】
概要
この記事ではAzureの「App Service」を使用して、
前回【Azure】無料でJava アプリを作成してみた〜Web App作成 編〜【初心者】で作成した、
WebアプリにFTPで接続してTOPの文言変更しますAzure サブスクリプションをお持ちでない場合は、
開始する前に無料アカウントを作成してください。【Azure】無料でJava アプリを作成してみた
【Azure】無料でJava アプリを作成してみた〜Web App作成 編〜【初心者】
・「App Service」を使用して「java」Webアプリを作る
・TOPページに「Hello world!!」まで表示させる【Azure】無料でJava アプリを作成してみた〜FTPで接続 編〜【初心者】 今ここ
・前回作成した「java」WebアプリにFTPで接続する
・TOPページの「Hello world!!」を変更する〜 次回以降の予定 〜
【Azure】無料でJava アプリを作成してみた〜Git 編〜【初心者】
【Azure】無料でJava アプリを作成してみた〜SQL Server 編〜【初心者】FTPで接続する
FTP情報を取得する
ブラウザーでポータルを開く
[ホーム] > [App Service]でFTPで接続したいアプリを選択する
※ 今回は記事は、前回作成した「namari-sampleWebApp-java」を使用していく。[概要]にある[URL]に飛びサイトが表示されているか確認する。(現状の状態を確認!)
[デプロイ センター]を選択し、[Manual Deployment (push / sync)] > [FTP]を選択すると下に[ダッシュボード]が表示されすのでそれをクリックする
表示されている情報をFTPに入力して接続する(今回はAtomで接続します)
※ [ユーザーの資格情報]を設定している場合は、設定したユーザー名・パスワードを使用することも可能- サンプル -
FTP情報 接続できない場合
(Atom...etc)FTPS エンドポイント(ホスト) ftps://waws-prod-dm1-161.ftp.azurewebsites.windows.net/site/wwwroot waws-prod-dm1-161.ftp.azurewebsites.windows.net ユーザー名 namari-sampleWebApp-java\$namari-sampleWebApp-java namari-sampleWebApp-java パスワード ●●●●●●●●●● ●●●●●●●●●● ポート 21 21 TOPページの内容を変更する(接続確認)
- TOPページの文言を「Hello world!!」→「FTP!OK!」に変更する。
※ 前回作成したアプリだと編集するファイルは、
/site/wwwroot/webapps/ROOT/index.jsp
になる文言が変更されているかサイトを確認する(https://< appName >.azurewebsites.net/)
変更されていたらOK!!
!!! 完了 !!!
関連記事
- 【Azure】無料でJava アプリを作成してみた〜Web App作成 編〜【初心者】
- 【Azure】無料でJava アプリを作成してみた〜FTPで接続 編〜【初心者】
- 【Azure】無料でJava アプリを作成してみた〜Git 編〜【初心者】
- 【Azure】無料でJava アプリを作成してみた〜SQL Server 編〜【初心者】
- 投稿日:2019-10-09T13:05:15+09:00
基本型-java
基本型
・整数 int -2147483648~2147483647 int i =100; 小数点なし
・整数 long -9223372036854775808~9223372036854775807 long lo = 300000L; 最後にL付ける 小数点なし
・整数 short -32768~32767
・整数 byte -128~127整数は普通int型使うし byteとshortの存在意義がわからない
でもオラクルの問題に出たりする・真偽値 boolean 真偽値,trueまたはfalse boolean Win = false;
・文字型 char Unicode文字 ¥u0000~¥uFFFF char A ='A';char uni = ‘¤’; //OK char uni2 = U+00A4; //NG ユニコード入れてもだめ char uni3 = ‘U+00A4’; //NG ''で囲んでも無駄無駄・浮動小数型 float 単精度浮動小数点数 小数点付きで、末尾にFまたはf float fo = 30.5f;
・浮動小数型 double 倍精度浮動小数点数 double do = 30.5;
整数型 最小値 最大値 byte Byte.MIN_VALUE Byte.MAX_VALUE short Short.MIN_VALUE Short.MAX_VALUE int Integer.MIN_VALUE Integer.MAX_VALUE long Long.MIN_VALUE Long.MAX_VALUE で最大値と最小値を取得できる
- 投稿日:2019-10-09T10:09:56+09:00
引っ掛け問題-java
引っ掛かった数だけ更新
public class Sample { public static void main(String args) { int i = 10; double d = 3.14; boolean b = true; System.out.print(i + " " + d + " " + b); } }10 3.14 trueが表示されるかと思いきや実行時に起動エラー。
エラーメッセージ:エディターにはメイン型が含まれていません。mainメソッドの引数の表記が正しくない []
public static void main(String args) ❌ public static void main(String[] args) ⭕️文法的には正しいのでコンパイルは通ってしまう。
- 投稿日:2019-10-09T09:55:49+09:00
識別式のルール-java
識別子
プログラミング言語において、パッケージ、クラス、インターフェイス、メソッド、フィールドや変数などに付ける名前のこと。言語の種類によって命名規則が異なる。
識別子は数字で始めることはできない
使うことができる記号は$と_のみ
長さの制限は無しint $$$ = 100; //OK int no_32 = 32; //OK int _Max = 5; //OK int 12s = 12; //エラー int var-1 = 1; //エラー int if = 1; //エラー・使用できる文字 AからZ、aからz、0から9、_、$
(例) animal, Max, No_1
・大文字と小文字は区別される
(例) animal と Animalは別の変数
・予約語は使用できない
(例) if,classなどの予約語は使用できない典型的な予約語・キーワード
・流れ制御を表す単語(if、while など)
・プログラムの構成要素を表す単語(function、const など)
・組み込み関数(open、readなど)
・組み込みの型(int、stringなど)
・他の言語などと混同して、誤用される可能性のある語(Javaのgoto、constなど)
・将来キーワードとして利用するかも知れない語(JavaScriptのlet、super、C++11のexport (以前は使われていた))
過去にキーワードだったため意味が無くなった後も残してあるもの
- 投稿日:2019-10-09T00:43:20+09:00
Spring-boot+Heroku+PostgreSQL+Flywayでアプリ作成してみた
はじめに
Spring-bootとLINEMessagingAPIで電車遅延通知botを作る の続編?です。
ここから新しい機能を追加した感じです。
LINE Messaging API でもう少し色々やってみたいなぁと思い、とりあえずDBを使える感じにしてみました。ターゲット
こんな人に役立つかと思います。
- 「Spring-boot + PostgreSQL + Flyway」 の連携周りではまっている人。
- 「Heroku + Spring-boot」で簡単にアプリを作りたい人。
ソース(差分)
先にどんな修正をしたか記載しています。
少し余計な修正も入っているので、そっと読み飛ばしてください。
https://github.com/ken-araki/line_bot/commit/324662adcff85cd7fd2aa1445c258be1b03602cd本題
ここから本題です。
1. Spring-bootとPostgreSQL+Flywayを連携する
build.gradle
build.gradleplugins { id 'org.springframework.boot' version '2.1.8.RELEASE' id 'java' id 'eclipse' id "org.flywaydb.flyway" version "5.2.4" } // 省略... dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'com.linecorp.bot:line-bot-spring-boot:2.7.0' implementation 'org.projectlombok:lombok:1.16.18' implementation 'org.flywaydb:flyway-core:5.2.4' implementation 'org.postgresql:postgresql:42.2.8' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.1' // 省略...pluginsに
flyway
を追加し、dependenciesにflyway-core
とpostgresql
とmybatis-spring-boot-starter
を追加します。ちなみに、version6系だと以下エラーで正しく動きませんでした(SrpingFramework2.2から解消される?)
Caused by: java.lang.ClassNotFoundException: org.flywaydb.core.api.callback.FlywayCallbackMapper(TobuyMapper.java, TobuyMapper.xml)
build.gradle
からもわかりますが、ORマッパーとしてiBtatis
を利用しています。
Mapperアノテーションをつけて、メソッドを定義するだけです。
メソッド名をSQL IDと合わせておけばよしなにマッピングされます。TobuyMapper.javapackage com.linebot.mapper; import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import com.linebot.domain.Tobuy; @Mapper public interface TobuyMapper { List<Tobuy> findByIsCompleted(@Param("isCompleted") String isCompleted); int insert(Tobuy tobuy); int updateAllCompleted(); }SQLは
src/main/resources/
配下にMapperクラスとパスを合わせて用意します。
※ 今回ならsrc/main/resources/com/linebot/mapper/TobuyMapper.xml
になります。TobuyMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.linebot.mapper.TobuyMapper"> <insert id="insert" parameterType="com.linebot.domain.Tobuy"> insert into tobuy ( goods, is_completed, created_date, updated_date ) values ( #{goods}, #{isCompleted}, current_timestamp, current_timestamp ) </insert> <update id="updateAllCompleted"> update tobuy set is_completed = '1', updated_date = current_timestamp where is_completed = '0' </update> <select id="findByIsCompleted" resultType="com.linebot.domain.Tobuy"> select id, goods, is_completed, created_date, updated_date from tobuy where is_completed = #{isCompleted} order by id </select> </mapper>細かい部分は、mybatis-springドキュメント が参考になると思います。
MigrationSQL(V1__CreateShopping.sql)
DDLはこちらで定義できます。
V${バージョン}__${SQL名}.sql
というファイルをsrc/main/resources/db/migration
に
配置すればアプリケーション起動時にMigrateしてくれます。
今回はtobuy
というテーブルを新規作成するSQLを用意しました。V1__CreateShopping.sqlCREATE TABLE tobuy ( id SERIAL NOT NULL, goods VARCHAR(255) NOT NULL, is_completed VARCHAR(1) NOT NULL, created_date TIMESTAMP, updated_date TIMESTAMP, PRIMARY KEY (id) );2. ローカルでPostgreSQL を利用する
マイグレーションとsqlの準備ができたので、正しく動作するかローカルで確認していきます。
dockerを利用してローカルDBを作成するので、docker-compose.yml
を用意します。docker/docker-compose.ymlversion: "3" services: db: image: postgres:11.5-alpine container_name: postgres-db ports: - "5432:5432" environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres1234 - PGPASSWORD=postgres1234 - POSTGRES_DB=testdb - DATABASE_HOST=localhost volumes: - ./db/init:/docker-entrypoint-initdb.d以下コマンドで起動させます。
docker-compose up -d
application.properties
にdocker-compose.yml
に合わせてDB接続情報を追加します。
※ ローカル、Herokuで設定を分岐させるのはここでは書きません。application.propertiesspring.datasource.url = jdbc:postgresql://localhost:5432/testdb spring.datasource.username = postgres spring.datasource.password = postgres1234 spring.datasource.driver-class-name = org.postgresql.Driver flyway.url = jdbc:postgresql://localhost:5432/testdb flyway.user = postgres flyway.password = postgres1234 flyway.driver = org.postgresql.Driver
./gradlew bootRun
でアプリケーションを起動させます。起動ログにもマイグレーションしていることが出ていますが、実際にテーブルが作られていることを確認します。
docker-compose exec db psql -U postgres testdb testdb=# \dt List of relations Schema | Name | Type | Owner --------+-----------------------+-------+---------- public | flyway_schema_history | table | postgres public | tobuy | table | postgres3. 本番(Heroku)とローカルでDB接続先を変えられるようにする
そこまで難しくないので、軽く記載しておきます。
Herokuとローカルで読み込む設定を切り替えられるようにします。
application.properties
をこんな感じで修正します。application.propertiesspring.profiles.active = local spring.profiles.include = common, ${spring.profiles.active}ミスで本番環境へ接続しないように基本
local
にしてます。
ただ、このままだとローカル設定値しか読み込めないので、Procfile
というファイルを修正してHerokuの設定値を見にいくようにします。web: java -Dserver.port=$PORT -jar build/libs/line_bot-0.0.1-SNAPSHOT.jar --spring.profiles.active=productionアプリケーションを起動させるコマンドを記載しています。
--spring.profiles.active=production
でapplication.properties
の設定を上書きしています。
※ ちなみに、デプロイするコマンドを指定したいときはrelease: XXXX
とします。これで切り分けができたので、共通、ローカル、Heroku、の設定値を以下のように作成していきます。
application-common.propertiesline.bot.channelSecret = ${LINE_BOT_CHANNEL_SECRET} line.bot.channelToken = ${LINE_BOT_CHANNEL_TOKEN} line.bot.handler.path = /callback ark.line.id = ${LINE_ID} ark.line.host = ${LINE_HOST} ark.line.port = ${LINE_PORT}ローカル、Herokuどちらでも利用する設定値は、
common
に定義します。
※ 現状使っていないので消そうと思っている設定値ですが・・・application-production.propertiesspring.datasource.url = ${DATABASE_JDBC_URL} spring.datasource.username = ${DATABASE_USER} spring.datasource.password = ${DATABASE_PASSWORD} spring.datasource.driver-class-name = ${DATABASE_DRIVER_CLASS_NAME} flyway.url = ${DATABASE_JDBC_URL} flyway.user = ${DATABASE_USER} flyway.password = ${DATABASE_PASSWORD} flyway.driver = ${DATABASE_DRIVER_CLASS_NAME}Herokuで読み込む設定値は
production
に定義します。おわりに
これでいい感じにHeroku+PostgreSQLでアプリ起動ができたと思います。
ホントはちゃんとした状態にしてから書きたかったのですが、いつになるかわからなかったのでコツコツ記事にしていきます。次はこんなことをやっていこうと思います。
- LINE Message API 部分の作り込み
- 少なくてもローカルで動作確認取れるようにしたい
- MyBatis -> JPA へ書き換え
- Java -> Kotlin へ書き換え
- 普通にリファクタ。とりあえず動くもので進めすぎた
- JUnit作成。常識ですよね・・・
- 投稿日:2019-10-09T00:32:56+09:00
JavaとJavaScript(平均の求め方での違い)
単純ではありますがこちらもアウトプット
Java
import java.util.*; public class Test12{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); int sum = 0; //合計を保存するための変数 int a=0; while (true) { System.out.println("数値を繰り返し入力して下さい(0で合計と平均をだして終了)"); //メッセージに終了条件を追加 int num = scan.nextInt(); if (num == 0) { break; } else { sum += num; a++;//おわるまで加算していく } } //System.out.println("合計は"+sum);// System.out.println("平均は"+sum/a); } }一例です。
JavaScript
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>jsave</title> </head> <body> <h1>平均の計算</h1> <input type="number" id="num" value="" placeholder="数値を入力"/> <button onclick="add()">Add</button> <button onclick="btCalc()">Calc!</button> <div id="msg"></div> <script> var nums=[]; var str=""; var eleNum=document.getElementById("num"); var eleMsg=document.getElementById("msg"); function add(){ //intに変換だがJavaと違いIntegerではない var num=parseInt(eleNum.value); nums.push(num); eleMsg.textContent=num+"を配列に追加"; } function btCalc(){ var sum=0; for(var i=0;i<nums.length;i++){ str +=nums[i]+(i==nums.length-1?"":","); sum +=nums[i]; } document.getElementById("data").textContent=str; var msg='[{${nums.join(",")}]の平均は${sum/nums.length}です。'; eleMsg.textContent=msg; } </script> </body> </html>とりあえずvarはブロックスコープ(let,const)に変えるべきなんでしょうな。
躓いた所で言えば
<script> 'use strict'; window.onload=function(){ //dom取得 let ele=document.getElementById("bt"); let result=document.getElementById("result"); //特定のイベントが対象に配信されるたびに呼び出される関数を設定します。 ele.addEventListener("click",function(){ result.textContent="Clicked!"; }); ele.addEventListener("click",function(){ window.alert("Clicked!!!"); }); }; </script>のようにHTMLを読み込んだ後にメソッドを実行?とか。。意味や意義を考えてたら理解が難しかったですね。。