PythonエンジニアがPHPフレームワークSymfonyをざっくり紹介する

普段私はPythonDjangoで書くことが多いのですが、先日PHPSymfonyを触る機会がありました。

そこで今回は、普段Djangoを使っている目線でのSymfonyの構成などをざっくりご紹介したいと思います。

  1. まずSymfonyとは?

Symphonyではありません。Google先生に頼っていて思うような検索結果が出ていないときは、結構な確率でタイポしていたりします。

Symfonyとは、PHPのフレームワークの一つです。

PHPのフレームワークというと、日本ではLaravelCakePHPがメジャーですが、海外ではSymfonyも幅広く使われています。

Symfonyの設計思想はMVCModel View Controller)です。

DjangoMTVModel Template View)ですね。

役割的には

  • Model データ
  • ViewSymfony), Template 画面に表示されるもの
  • Controller, ViewDjangoModelView(Symfony)やTemplateに渡す部分

ざっくり上記になっています。名前が違ったり被っていたりするので分かりにくいですが、大まかな考え方は近しい部分があります。

  1. Symfonyの構造

ディレクトリ構成は大まかに以下のような形になっています

紹介解説用に番号を振っています

0. sample

├── 1. app                    

  ├── 1-1. Resources

    └── 1-1-1. views     

  └── 1-2. config             

├── 2. bin                    

├── 3. src                    

  └── 3-1. SampleBundle      

    ├── 3-1-1. Controller          

    ├── 3-1-2. Entity     

    ├── 3-1-3. Form            

    ├── 3-1-4. Resources

        └── 3-1-4-1. views   

    ├── 3-1-5. Service

    └── 3-1-6. Validator

├── 4. var                    

└── 5. vendor         

└── 6. web    

  • 0 sample

プロジェクトディレクトリになります

  • 1 app

ここに、プロジェクト全体で使用する設定ファイル(yamlなど)や、初期表示のindex.htmlを入れたりします。

細かい表示制御やデータの加工など各ページで使用するソースは基本的にこのディレクトリには置きません

  • 2 bin

コンソールで使用するコマンドなどのファイルを格納します。

  • 3 src

BundleごとにソースコードやHTMLファイルを格納します。主に開発をする際触るのはここになるかと思います。

  • 3-1 SampleBundle

そもそもBundleって何?と思う方も多いかと思われます。

簡潔にまとめると、Bundleとは、データベースと関連付いたエンティティや、ソースコードやテスト、画面に表示するHTMLなどのファイルをひとところにまとめた機能の塊です。

Djangoで言うところのアプリケーションに近いものだと思うと、想像しやすいかもしれません。

Bundle単位で機能拡張ソースコードも多く公開されているので、取捨選択することで自分でコードを書かずとも色々な機能を組み込めるようになっています。

  • 3-1-1 Controller

Djangoで言うところのView部分です。画面表示するデータを制御したり、URLのルーティング制御を行います。

  • 3-1-2 Entity

データベースのテーブルに応じたEntityを格納する場所です。getter, setterを設定したり、更新時のシーケンスなど、データベースの接続の際の付加処理を設定します。Djangoの方がこの部分に記載する処理の幅は広くなりがちですが、各アプリのmodels.pyに記載するような、データベースに応じたオブジェクトを定義する場所です。

  • 3-1-3 Form

画面で使用するFormを設定する部分です。ここで、各項目のバリデーションも設定するのですが、細かいバリデーション処理はまた別途記載したります。Djangoでいう各アプリのforms.pyに近い役割を担っています。

  • 3-1-4 Resources

viewsディレクトリに、HTMLファイルを格納します。

また、Bundle固有で使う設定ファイルなどある場合は、ここに置いたりも。

  • 3-1-5 Service

Serviceだったりhelperだったり、Bundle内の位置付けで名称は多々あるかと。

Controllerでやるには複雑な処理や、細かいビジネスロジックを記載していく部分になります。

  • 3-1-6 Validator

文字通り、バリデーションを記述する部分です。ここでバリデーションクラスを作成し、formの方でインポートして各項目へのバリデーションを設定します。

  • 4 var

キャッシュファイルや、ログファイルを格納しておくディレクトリになります。

  • 5 vendor

ライブラリ一般を格納するディレクトリになります。

  • 6 web

Jscss、画像ファイルなど、画面上から見えるファイルを格納する場所です。Djangoでいうstaticのような役割です。

  1. Symfonyのメリット・特徴

個人的に使っていてSymfonyってここが面白い!と感じた部分を取り上げてみます。

  • 分業がしやすい

画面表示はここ、バリデーションはここ、formの生成内容はここ、と細かく記述ファイルが分かれているので、多人数で分業をする際はコンフリクトを減らしやすいかと思います。また、Bundleごとに切り分けて開発を進める事も出来るため、開発方法の幅が広がります。

  • テンプレート言語のTwigが使いやすい

Djangotemplate記法と近しい、Twigが使えます。画面上でオブジェクトをループさせてテーブルに値を入れ込んだり、if分岐で表示するものを変えたり、生のHTMLでは表現しにくい処理が簡潔に行えます。

  • Formを使う機能を補助する仕組みが協力

検索であったりデータの登録・更新、ログインと、formを使わないシステムはほぼ無いかと思われます。Symfonyでは、Validationクラスで細かく設定してのバリデーションや、entityをまるっとフォームに入れてそれを使用してデータの更新を行う事、また画面にレンダリングする際も、細かくlabelform本体の配置は行えます。使い方のルールがある程度定まっている事で、多人数での開発の際にソースの揺れを減らせる事もメリットだと思います。

今までSymfonyの名前も知らなかったのですが、使ってみると意外と馴染んだ部分もありました。

PHPのメリットでありデメリットでもある自由にコードが書ける(≒フォーマットの統一が難しい)という部分を、Symfonyの記法である程度統一規格を持たせ一貫性のあるソースに仕上げやすいという風にうまくカバーしていました。

日本でのシェアが広くない事もあり、ドキュメントは少ないのでそこはお気をつけください。

もしPHPで開発を行う事がありましたら、フレームワークの選択肢の一つに検討してみてはいかがでしょうか?

2016/09/19PHP

PostgreSQLで階層マスタを扱う

とある案件で階層マスタを扱うことがありました。

DBがOracleの場合だと、「START WITH 〜 CONNECT BY PRIOR 〜」という、階層問い合わせの構文があるのですが、PostgreSQLだと階層問い合わせが出来るのか知らなかったので調べてみました。
(なお、MySQLでは使えません)

1. データを準備する

以下の様な階層構造を考えます。

レベル0 レベル1 レベル2
勘定科目
資産科目
現金
預金
負債科目
借入金
預り金
資本科目
資本金
利益剰余金


テーブル定義を以下の通りとします。

列名 内容
id int 主キー
code varchar(20) 科目コード
name varchar(100) 科目名
parent_id int 親科目の主キー (外部キー)


DDLは以下の通りです。

CREATE TABLE kamoku (
  id int primary key,
  code varchar(20),
  name varchar(100),
  parent_id int references kamoku(id)
);

INSERT文は以下になります。

INSERT INTO kamoku (id, code, name, parent_id) VALUES (1, 'A', '勘定科目', NULL);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (2, 'A01', '資産科目', 1);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (3, 'A02', '負債科目', 1);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (4, 'A03', '資本科目', 1);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (5, 'A0101', '現金', 2);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (6, 'A0102', '預金', 2);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (7, 'A0201', '借入金', 3);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (8, 'A0202', '預り金', 3);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (9, 'A0301', '資本金', 4);
INSERT INTO kamoku (id, code, name, parent_id) VALUES (10, 'A0302', '利益剰余金', 4);

こうして作ったデータをSELECTすると、以下の結果になります。

hoge=# select * from kamoku;

id code name parent_id
1 A 勘定科目
2 A01 資産科目 1
3 A02 負債科目 1
4 A03 資本科目 1
5 A0101 現金 2
6 A0102 預金 2
7 A0201 借入金 3
8 A0202 預り金 3
9 A0301 資本金 4
10 A0302 利益剰余金 4

(10 rows)

2. WITH RECURSIVE句による階層問い合わせ

PostgreSQLにはWITH句というサブクエリを定義する句が使えます。
ここに、RECURSIVEを付けると、再帰的なサブクエリを書くことが出来ます。

具体的には、以下の様に書きます。

WITH RECURSIVE child (level, id, code, name, parent_id) AS (
  SELECT 0, kamoku.id, kamoku.code, kamoku.name, kamoku.parent_id FROM kamoku WHERE kamoku.id = 1
UNION ALL
  SELECT
    child.level + 1,
    kamoku.id,
    kamoku.code,
    kamoku.name,
    kamoku.parent_id
  FROM
    kamoku, child
  WHERE
    kamoku.parent_id = child.id)
SELECT level, id, code, name, parent_id FROM child ORDER BY code;

結果は以下の通りです。

level id code name parent_id
0 1 A 勘定科目
1 2 A01 資産科目 1
2 5 A0101 現金 2
2 6 A0102 預金 2
1 3 A02    負債科目 1
2 7 A0201 借入金 3
2 8 A0202 預り金 3
1 4 A03    資本科目 1
2 9 A0301 資本金 4
2 10 A0302 利益剰余金 4

(10 rows)

2行目のSELECT文のWHERE句の条件が、開始のidになります。
開始のidを指定すれば、途中から取得することも出来ます。

WITH RECURSIVE child (level, id, code, name, parent_id) AS (
  SELECT 0, kamoku.id, kamoku.code, kamoku.name, kamoku.parent_id FROM kamoku WHERE kamoku.id = 2
UNION ALL
  SELECT
    child.level + 1,
    kamoku.id,
    kamoku.code,
    kamoku.name,
    kamoku.parent_id
  FROM
    kamoku, child
  WHERE
    kamoku.parent_id = child.id)
SELECT level, id, code, name, parent_id FROM child ORDER BY code;

level id code name parent_id
0 2 A01 資産科目 1
1 5 A0101 現金 2
1 6 A0102 預金 2

(3 rows)

逆順(子→親)も出来ます。

WITH RECURSIVE child (level, id, code, name, parent_id) AS (
  SELECT 0, kamoku.id, kamoku.code, kamoku.name, kamoku.parent_id FROM kamoku WHERE kamoku.id = 10
UNION ALL
  SELECT
    child.level + 1,
    kamoku.id,
    kamoku.code,
    kamoku.name,
    kamoku.parent_id
  FROM
    kamoku, child
  WHERE
    child.parent_id = kamoku.id)
SELECT level, id, code, name, parent_id FROM child ORDER BY code DESC;

level id code name parent_id
0 10 A0302 利益剰余金 4
1 4 A03 資本科目 1
2 1 A 勘定科目

(3 rows)

3. まとめと落とし穴

今回の階層問い合わせを再帰的に行うクエリだと、1クエリで出来て速度も速いので良い方法だと思います。

ですが、各階層(level)毎に取得している為、最後に並び替えをしないと、ツリー状に並ぶ結果が返ってきません。
今回は簡単にするために、科目コード(code)にルールを作って並ぶようにしています。

実際に使う時は、もう一工夫したいところです。

2016/09/19Database

jQuery アンカーでPOST送信

「アンカーリンクで画面遷移の際にPOST送信で送りたい!」とのちょっと特殊な要件の対応をしたため、備忘録がてらに投稿。

要件概要

・アンカータグのプロパティ(data-〜)に設定されたパラメータをhrefで指定された画面へPOST送信したい!

・削除ボタンなどでも使いたい!その際には確認ダイアログを表示できる様にしたい!

用意するもの

・jQuery(1.12.4

JS

postlink.js

function createPostLink(selector) {
    // selectorで指定された要素のクリックイベントにpost送信の機能を付与
    $(selector).click(function () {
        // 後々パラメータをsubmitするためのformを生成
        var $form = $('<form/>').attr({
            'action': '',
            'method': 'post',
        });
        // post先のパスを取得
        var action = $(this).attr('href');
        // data-〜から始まるプロパティのリストを取得
        var params = $(this).data();
        // formのアクションを設定
        $form.attr('action', action);
        var cmsgs = '';
        for (var name in params) {
            if ("confirm_message" == name) {
                // data-confirm_messageプロパティがあった場合は確認ダイアログ用メッセージとして設定
                cmsgs = params[name];
                continue;
            }
            // data-confirm_message以外は送信パラメータに設定し、hiddenでformに追加
            var $input = $('<input type="hidden" />').attr('name', name).val(params[name]);
            $form.append($input);
        }
        if (cmsgs) {
            // 確認ダイアログメッセージが設定されている場合はconfirmでダイアログを表示
            if (window.confirm(cmsgs)) {
                // OKなら生成したformをbodyに追加し、submitを実行
                $('body').append($form);
                $form.submit();
            }
        } else {
            // 確認ダイアログメッセージが設定されていなければリンククリックでsubmitを実行
            $('body').append($form);
            $form.submit();
        }
        return false;
    });
}

HTMLサンプル

<body>
    <a href="/action/delete.php" data-confirm_message="削除しますか?" data-target_id="1" class="delete-btn">削除</a>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="postlink.js"></script>
    <script>
        $(function(){
           // class="delete-btn" 要素にpost送信機能を付与
           createPostLink('.delete-btn');
        });
    </script>
</body>

2016/09/19

2ファイルでLAMP開発環境構築

LAMPでの開発を行う際、まず必要となるのがローカル開発環境の用意でしょう。
ローカル環境で開発を行い、
開発環境 => ステージング環境 => 本番環境
へと順々にソースコードを反映していくのが一般的なフローかと思います。

今回、一番手元である「LAMPローカル環境」の簡単な構築方法について初心者向けに紹介したいと思います。
タイトル通り「Vagrant + シェルスクリプト」で構築を行っていきますが、各バージョンは以下となります。


vagrant 2.2.3
virtualBox 6.0
centos 7.2
php 7.3
mysql 8.0

※執筆時点、極力最新版のもので統一させていただいております。


「Vagrant + シェルスクリプト」ということで、必要となるのは
Vagrantfile と シェルスクリプトの2つのみです。
ファイルのみの構成となるので、共有が非常に楽という利点があります。
イメージファイルではないので、サイズを気にすることもありません。

もちろん、Vagrantfileにシェルスクリプトを記述することもできますが、
別ファイルで定義したほうが見やすかったり再利用できたりもするので、別々に記述する手法を私はよく取ります。
(個人的にはこのほうが分かりやすいのかなと思っています)

それでは、順々に見ていきましょう。

1. 設定準備をしよう

まずは、以下からvagrantとVirtualBoxをインストールします。

[vagrant]

https://www.vagrantup.com/downloads.html

[virtualbox]

https://www.virtualbox.org/wiki/Downloads

インストールが完了したら Vagrantfile を設定していきます
適当なディレクトリで(執筆サンプルでは「dev」とします)

$ vagrant init

を実行し、Vagrantfileを作成します。
(あるいは、Vagrantfileを自分で作成します)
加え、「provision.sh」シェルスクリプトファイルを作成しておき、
構築確認用に「source」のディレクトリを作成、php_info.phpというファイルを作っておきましょう。中身は以下となります。

--php_info.php--

<?php
    phpinfo();
?>

ディレクトリ構成はこのような形になるでしょう
    dev(適当なディレクトリ)
      |----Vagrantfile
      |----provision.sh
      |----source(確認用ファイル設置ディレクトリ)
                |----php_info.php

2. Vagrantfileを設定しよう

Vagrantfileのの中身を以下のように記述します。


--Vagrantfile--

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.define "centos7-web" do |web|
    web.vm.box = "centos7.2"
    web.vm.box_url = "https://github.com/CommanderK5/packer-centos-template/releases/download/0.7.2/vagrant-centos-7.2.box"
    web.vm.hostname = "centos7-web"
    web.vm.network :private_network, ip: "192.168.33.10"
    web.vm.provision :shell, :path => "provision.sh",:privileged => true
    web.vm.synced_folder "./source" , "/var/www/html/source"
  end
end

------------------------------------------------------------------------------------------------------------

順にみていきましょう

1. VAGRANTFILE_API_VERSION = "2"
  設定構文のバージョン番号を定義します。
  現状(2019年1月執筆時点)vagrantのバージョン1.1以降は"2"を設定するようにします。

2. Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  構文についてはインデントでブロックを表しています。endで終了です。
  サンプルでは、configブロックとwebブロックがあります。webブロックを順に追っていきましょう。

3. web.vm.box = "centos7.2"
  vagrant box add を意味します。centos7.2の名前で追加をします(名前は自由につけて大丈夫です)

4. web.vm.box_url = "https://github.com/CommanderK5/packer-centos-template/releases/download/0.7.2/vagrant-centos-7.2.b
  追加するボックス(サンプルではcentos7.2)がなかった場合、指定したURLから取得しにいきます

5. web.vm.hostname = "centos7-web"
  ホスト名を指定します。未指定ですと、どんな仮想マシンに接続しているのか分かり難いので指定しましょう。

6. web.vm.network :private_network, ip: "192.168.33.10"
  IPアドレスを指定します。空きのあるアドレスを指定しましょう。


7. web.vm.provision :shell, :path => "./provision.sh",:privileged => true
  vagrant起動時にシェルスクリプトを実行させます(サンプルでは、同階層の「provision.sh」を実行します)。
  本記事のキモとなる箇所です。root権限で実行させるためにも「:privileged => true」を記述しておきます

web.vm.synced_folder "./source" , "/var/www/html/source"
  第1引数:同期をとりたいディレクトリを指定します。(サンプル同階層の「source」ディレクトリと同期をとります)
  第2引数:仮想マシン内に設置するパスを指定します。
  

3. シェルスクリプトを設定しよう

vagrant起動時に実行するシェルスクリプトの内容を記述します。
シェルスクリプトについては、必要に応じてインストールしたいものを追加していきましょう。
今回の場合は、Apache、MySQL、PHPをインストールしていきます。サンプルは以下となります。
--provision.sh--

#!/bin/sh

# epalとremiをそれぞれをインストール
yum -y install epel-release
rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

# apacheインストール
yum -y --enablerepo=remi install httpd

# php7.3インストール
yum -y install --enablerepo=remi,remi-php73 php php-devel php-mbstring php-pdo php-gd php-xml php-mcrypt

# phpのタイムゾーンをUTCにする
sed -i -e "/;date\.timezone /a date\.timezone = 'UTC'" /etc/php.ini

# phpのerror_logを出力させる
sed -i -e "s/;display_errors\ =\ Off/display_errors = On/g" /etc/php.ini
sed -i -e "s/;error_log\ =\ php_errors.log/error_log\ =\ '\/var\/log\/php\/error.log'/g" /etc/php.ini
mkdir /var/log/php/
touch /var/log/php/error.log
chown apache:apache /var/log/php/error.log

# mysql8インストール
yum -y remove mariadb-libs
yum -y remove mysql*
rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
yum -y install mysql-community-server

# mysqlの文字セットをUTF8にする
sed -i -e "/\[mysqld\]/a character-set-server=utf8" /etc/my.cnf
sed -i -e "/\[mysqld_safe\]/i \[mysql\]\ndefault-character-set = utf8" /etc/my.cnf

# 利用するサービスを開始する
systemctl enable httpd.service
systemctl enable mysqld.service
systemctl restart httpd
systemctl restart mysqld.service

# セットアップ終了
echo "COMPLETE!"


------------------------------------------------------------------------------------------------------------

4. vagrantを起動しよう

設定が完了したら、
$ vagrant up
を実行し、しばらく待ちましょう。

完了後(上記のように「COMPLETE!」表示で完了です。シェルで記述したmysqlインストール→「COMPLETE!」が動いています)
http://192.168.33.10/source/php_info.php
にアクセスし、php情報が表示されていればOKです

5. おわりに

如何でしたでしょうか。
2ファイルで容易に「ローカル環境」が作成できたかと思います。
この2つを用いることで手軽に環境構築/破壊が気軽に行えます。
試行錯誤を手軽さに行えるので非常に便利です。
この手軽さは、これからphpを始めてみようと思う方に特におすすめなのではと。

以上、phpエンジニアの簡単な紹介でした。乱文長文、失礼しました。

2016/09/19PHP

ImageMagick

ImageMagickという画像処理ツールをご存知でしょうか?

どの言語を使っていても、画像処理をしようとすると一度はお世話になった事がある方も多いかと思われます。

ちなみに名前のスペルは誤字に見えますが敢えてこうなっているようです。余談ですがよくtypoします。

とても便利で使用場所を選ばない便利なツールですが、

少々癖のあるため中々やりたい事を実現する方法にたどり着けない事もあります。

そこで、ImageMagickと少し仲良くなるためのtipsを紹介したいと思います。

1. ImageMagickとは?

端的にまとめると、画像を操作するためのツールです。

  • 文字から画像を作成したり
  • 既存の画像の拡張子を変更したり
  • リサイズを行ったり
  • 画像の濃度をいじったり

出来る事は多岐に渡ります。

コマンドラインから呼び出す事も出来ますし、

各言語のライブラリを使用してプログラム内で使う事も出来ます。

(例、PHPならIMagickPythonならPythonMagickなど)

2. インストール方法

バージョンにこだわらなければ、下記ページのインストーラーからどうぞ。

大体その時の最新バージョンがインストールできます。

http://www.imagemagick.org/script/download.php

特定のバージョンが欲しい場合は、該当バージョンのソースを探してくる必要があります。

例えばこういう所から

https://launchpad.net/imagemagick/main/

試しに上記のサイトから6.7.8-9バージョンをインストールする際はこんな感じで、

ソースをダウンロードしてインストールファイルを作成し、それからインストールしてあげる必要があります。

cd 作業用適当なフォルダへ
wget https://launchpad.net/imagemagick/main/6.7.8-9/+download/ImageMagick-6.7.8-9.tar.gz
tar xvfz ImageMagick-6.7.8-9.tar.gz
cd ImageMagick-6.7.8-9/
./configure
make
make install

3. 画像をいじりたい!よくある例

・pngしか上げられないのに手元にjpgしかない……拡張子変えたい!

convert 元画像パス 変換後の画像パス

    例:convert test.jpg test_converted.png

ちなみに、jpgへ変換すると画像の透過情報などが消えるので要注意。

・ サンプル用にとあるサイズの画像が欲しいけど、一々生成サイト使うのは面倒。サイズ指定して画像を作りたい!

convert -size x xc:”色” 出力画像パス

    例:convert -size 100x200 xc:#000000 created.png

・ 作る画像のサイズ間違えた。リサイズしたい!

convert 元画像パス -resize x 出力画像パス

    例:convert created.png -resize 300x500 convert_after.png

・ てかそもそもこの画像ファイルの縦横サイズいくつ?

identify -format "%w %h" 調べたい画像パス

    例:identify -format "%w %h" test.png

上記コマンド実行で横幅と縦幅が出力されます。

・ 見出し画像などに。文言指定して文字から画像を作成したい!

convert -font フォント -size x -fill "文字色" -background 背景色 -kerning 文字間 -pointsize 文字サイズ caption:"出力する文字" 出力画像パス

    例:convert -font /Library/Fonts//AppleMyungjo.ttf -size 100x100 -fill “white” -background red -kerning 2.5 -pointsize 20 caption:”テスト” text.png

使えるフォントは

convert -list font

で見られます。

背景を透明にしたい場合は背景色指定をnoneに。

・ 別々に作ったけどやっぱり画像同士を重ねて使いたい!

convert -size 背景の横x背景の縦 xc:"背景色" 一枚目の画像パス -geometry +一枚目の画像の背景左上から横の位置+一枚目の画像の背景左上から縦の位置 -composite 重ねる画像パス -geometry +重ねる画像の背景左上から横の位置+重ねる画像の背景左上から縦の位置 -composite 出力画像パス

    例:convert -size 200x300 xc:"blue" created.png -geometry +10+20 -composite text.png -geometry +20+100 -composite comb_result.png

複数画像を重ねたい時は重ねる分だけ

-composite 重ねる画像パス -geometry +重ねる画像の背景左上から横の位置+重ねる画像の背景左上から縦の位置

の塊を増やしてください。

4. 困った時は

ImageMagickは書籍などで纏まっている情報が少ないのが難点の一つ。

こちらのサイトが、日本語で分かりやすく情報がまとまっているので困ったらまずどうぞ。

http://imagemagick.rulez.jp/

最新の情報にアクセスしたい方、ついでに英語の勉強もしたい方は公式へ。

https://www.imagemagick.org/

以上、普段は大体python触ってる社員その1でした。

2016/09/19その他
1