Docurain Labo

Docurainサービス開発日記

わかりやすいテンプレートのために - IF / IFS / SWITCHのご紹介

今回は条件分岐がシンプルに書けるようになったIF / IFS / SWITCHをご紹介します。

従来の条件分岐

従来はセル内で条件分岐をさせて値を出力する場合に

#if($cond) $a #end
#if($cond) $a #else $b #end
#if($cond1) $a #elseif($cond2) $b #elseif($cond3) $c #else $d #end

のように書かなければならず、少し面倒でした。

プログラマの美徳は怠惰。というわけで、より簡潔な表現のためにExcelの関数(IF / IFS / SWITCH)とほぼ同等の記述を可能にしました。 それが #IF / #IFS / #SWITCH マクロです。

#IF / #IFS / #SWITCH

上記の通り、Excelの関数(IF / IFS / SWITCH)と類似の記法・動作になっています。

概要は次の通りです。

#IF

従来の書き方(#if) #IF
#if($cond)

#end
#IF($cond, 'A')
#if($cond)

#else

#end
#IF($cond, 'A', 'B')

#IFS

従来の書き方(#if) #IFS
#if($cond1)

#elseif($cond2)

#elseif($cond3)

#end
#IFS([$cond1, 'A', $cond2, 'B', $cond3, 'C'])
#if($cond1)

#elseif($cond2)

#elseif($cond3)

#else

#end
#IFS([$cond1, 'A', $cond2, 'B', $cond3, 'C'], 'D')

#SWITCH

従来の書き方(#if) #SWITCH
#if($val == 1)

#elseif($val == 2)

#elseif($val == 3)

#else

#end
#SWITCH($val, [1, 'A', 2, 'B', 3, 'C'], 'D')

詳細はDocurainのマニュアル 帳票作成マニュアル(条件分岐支援) をご参照ください。

実際に使うとこんな感じです。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/09/スクリーンショット-2020-09-09-12.41.02.png

このようにセル内がスッキリして、テンプレートが読みやすくなりました。

帳票はただでさえ複雑な物が多いですし...

テンプレートは最大限単純にしておきたいものです!

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。

わかりやすいテンプレートのために - 式リテラルのご紹介

Docurainのテンプレート

DocurainではExcelテンプレートに差し込みたいデータの位置をプレースホルダーとして記述します。

また、出力する帳票の行を動的に増やす、特定の条件のときだけ表示する、などの動的な部分は組み込みのマクロ命令により実現します。 このマクロ命令はVelocityに準拠しています。

式リテラル

今回ご紹介する式リテラルはこのVelocityの制約を突破するために追加されたDocurain独自の機能です。 Velocityの制約とは、いわゆる「式」を使える箇所が限られている点です。

通常、式を指定できるのは

#if($a < 0) ... #end
#set($b = $a + 1)

のような箇所だけです。つまり、

$a 足す 1 は $a + 1 です。
#set($list = [$a, $a + 1, $a + 2])

のような記述ができず、

#set($plus1 = $a + 1)
#set($plus2 = $a + 2)

$a 足す 1 は $plus1 です。
#set($list = [$a, $plus1, $plus2])

のように回りくどい書き方になります。なかなかキツいですねこれは。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/スクリーンショット-2020-08-31-14.27.10.png

エンジニアは怠惰であるべき。怠けるために全力を出した結果、生み出されたのが「式リテラル」です。

使用例

式リテラルは、バッククオート ` で囲んだ文字列がそのまま値として評価されます。*1

↑のテンプレートを式リテラルを使用することにより簡潔にすることが可能です。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/スクリーンショット-2020-08-31-14.41.40.png

#set($plus1 = %{num} + 1)
%{num} 足す 1 は $plus1 です。
%{num} 足す 1 は `%{num} + 1` です。
#set($b = %{a} + 1)
#set($c = %{a} + 2)
#set($list = [%{a}, $b, $c])
#set($list = [%{a}, `%{a} + 1`, `%{a} + 2`])

のように記述できるようになりました。

スッキリしてわかりやすくなりましたね! Javascriptのテンプレートリテラルに似せたデザインにしたので、違和感も少ないかと思います。

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。

*1:現在のところバッククオートのエスケープはできません。すなわち、バッククオートを含む式リテラルは使用できません。

DocuSign対抗! Docurainも画像、回せます

このようなツイートを目にしました。

そこでDocuSignに対抗??して、Docurainでも画像回転できるようにしました!
(DocuSign... 名前も似てますし、やるしかないですよね...!!)

通常の画像出力

通常の画像出力です。
シェイプに#IMAGE($e.stamp)のように記載することで渡された画像データを出力します。

画像回転の指定方法

上記の画像出力で#IMAGE($e.stamp, 'rotate=[角度]')のようにすると、画像を指定した角度に回転させることができます。
-360°から360°の範囲で指定可能で、それぞれ以下の意味を持ちます。

正の角度 元画像のサイズのままで回転される。
よって、長方形の場合は見切れることがある。
負の角度 元画像のサイズを超えないように自動縮小される。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/2120.png (画像はこちらをお借りしております 「たいへんよくできました」のようなハンコのジェネレーターを作ってみた。 - RainbowVortex)

回していきましょう!

まずは正の角度を指定します。
#IMAGE($e.stamp, 'rotate=30')

回ってますね〜
画像サイズはそのままですね。

次は負の角度を指定して出力します。
#IMAGE($e.stamp, 'rotate=-30')

こちらは元画像のサイズを超えないように自動縮小がされてますね。

このようにDocurainでも手軽に画像がまわせます。

はんこお辞儀が必要な場合にも使えます...!

 

他にも様々な機能がありますので、ぜひ登録無料、試用期限なしのアカウント取得をしてマニュアルを参照しお試しください。

わかりやすいテンプレートのために - 省略記法のご紹介

省略記法とは

DocurainではExcelテンプレートに差し込みたいデータの位置をプレースホルダーとして記述し、 差し込むデータをJSON形式*1で定義します。 省略記法はそのJSONのキーにマルチバイト文字を設定する・したい場合に便利な機能です。

{
    "名前": "山本 太郎",
    "生年月日": "1990-10-10",
    "住所": "東京都台東区XXXXXXXXXXXXXXX",
    "商品リスト": [
        {
            "商品名": "AAA",
            "単価": "1000",
            "数量": "1"
        },
        {
            "商品名": "BBB",
            "単価": "500",
            "数量": "3"
        }
    ],
    "備考": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

さて、このようなJSONの場合に、Docurainが準拠しているVelocityの文法では、マルチバイトのプロパティ名を使用できません。 つまり ${ENTITY.名前}と書けず、以下のように${ENTITY['名前']}と書く必要があり冗長です。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/with_blog_1.png

ここで省略記法を用いると以下のように簡潔に%{名前}と記述することができます。

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/with_blog_2.png

#with([コンテキストオブジェクト])または#WITH([コンテキストオブジェクト])を使用することで、省略記法の起点となるオブジェクトを指定し、 そのオブジェクトのプロパティ参照を簡潔に%{名前}のように記述できるようになります。

省略記法の起点となるオブジェクトを指定する

初期状態で$ENTITYは 省略記法の起点となるオブジェクト(以降コンテキストオブジェクト)として#WITHで指定済みです。
つまりサンプルの"名前"や"生年月日"のようなプロパティを#WITHの記述無しに%{名前}%{生年月日}で指定可能です。

コンテキストオブジェクトの指定には、大文字の#WITHと小文字の#withの2種類があります。

#WITH グローバルな設定でありブロック命令のスコープは考慮されません。
#with ブロック内のみ有効であり、スコープを抜けると外側の`#with(...)`の指定に戻ります。

使用例

#WITHを使ったテンプレートサンプル https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/with_blog_3.png

このように#foreachブロック内で商品リストの各エントリを#WITHで指定すると、エントリ内プロパティを%{商品名}%{単価}のように簡潔に書けます。
#WITHはグローバルな設定のため、この#foreachブロック後の14行目以降でのコンテキストオブジェクトは最後の#WITH()の呼び出し、すなわち商品リストの最後のエントリです。
よって、%{備考}で出力するためには14行目で再度#WITH($ENTITY)と記述する必要があり面倒です。

このような場合に、#withを使うことで 上記の様に再度#WITH($ENTITY)をする必要がなくなります。

#withを使ったテンプレートサンプル

https://s3.ap-northeast-1.amazonaws.com/site.docurain.jp/blog/2020/08/with_blog_4.png

このように#foreachブロック内で商品リストの各エントリを#with(ブロック内のみ有効)で指定すると #foreachブロック外のコンテキストオブジェクトは初期状態の$ENTITYのままです。

こちらの方がずっとわかりやすいですね。

今回作成したサンプルは、無料トライアルページからも登録不要で試してみることが出来ます。

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。

*1:CSV, TSVも可

「外字」も出力可能です

官公庁・自治体・医療方面からのお客様からの問い合わせで多いのが外字の対応状況です。

もちろん、Docurainは「外字」*1に対応しています。

一般的に外字を必要とするのは人名・地名といった名詞において正確に一致する漢字を取り扱うケースですが、 Docurainは IPAmj明朝 に対応しています。このフォントは、いわゆる「外字」として必要とされるグリフのほぼ全て*2を含んだ特殊なフォントです。

例えば、f:id:yutay:20200720105116p:plain:w30 (U+3C08)という漢字の異体字である f:id:yutay:20200720105144p:plain:w30 (MJ文字図形名 MJ001865)は、この文字を埋め込むセルのフォントをIPAmj明朝にした上で、JSONで以下のようにユニコードエスケープで表現可能です。

f:id:yutay:20200720105144p:plain:w30

{ "str": "\ud879\udfdd" }

または、

{ "str": "\u{2e7dd}" }

と記述できます。

IPAmj明朝の文字一覧はこちらを参照してください。

今回は上記のIPAmj明朝の文字一覧からいくつかの文字を抜粋してPDF出力してみました。
f:id:yutay:20200720105144p:plain:w30f:id:yutay:20200720105956p:plain:w30 (の異体字、MJ文字図形名 MJ030058) をJSONに設定しています。

テンプレート・JSONは以下のように作成しています。
サンプルはこちらからダウンロードできます。

  • テンプレート f:id:yutay:20200720110500p:plain

  • 文字を埋め込むセルのフォントをIPAmj明朝に設定しています f:id:yutay:20200720110517p:plain

データ(JSON)はユニコードエスケープで表現したものを準備します。

{
    "外字1": "\ud879\udfdd",
    "外字2": "\u9f4a\udb40\udd03",
    "外字3": "\ud878\udda8",
    "外字4": "\ud873\udf1c",
    "外字5": "\ud87a\udcd9",
    ...省略...,
}

このテンプレートとJSONを使って出力してみましょう。

f:id:yutay:20200720114353p:plain

全て問題なく出力されていますね…!

IPAmj明朝の文字一覧に存在する5万8,844文字は全て検証済みで出力可能となっています。

今回作成したサンプルは、無料トライアルページからも登録不要で試してみることが出来ます。 外字を扱う帳票の作成・出力が必要な際はぜひ試してみてください!

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。

*1:ただし、本当の意味の外字(任意のフォントをインストール)を扱うことは出来ません。よって「外字」という表記にしています。

*2:総務省の「市区町村が使用する外字の実態調査」報告書(平成24年3月)によると、IPAmjで、全国市区町村が使用する外字の95.52%をカバーできるとのことです。

Docurainの紹介動画を追加しました

Docurainの魅力をわかりやすくお伝えするために動画を用意しました!

公式サイトからご覧いただけますが、以下にもフルバージョンを掲載しておきます。

ムービー中で繰り返し触れられているのがコスト削減効果ですが、これは帳票の生産性を従来製品に比べて大幅に向上させているとも言い換える事が出来ます。

エンジニアの皆様向けに少し補足すると、従来の帳票生成ツールというのは、まずデータを取得するデータソースを定義して、クエリを定義して、罫線やボックスをマウスでポチポチ配置して、データによる条件分岐をコード(イベントハンドラ)で実装して、テキストのはみ出しが無いか最大長のテキストデータでテストして…という、かなり手間のかかる作業フローに多くの時間が割かれていました。

それを、多くの人が使い慣れたExcel上でデザインし、かつ、高度な条件分岐や各種集計処理、改ページ制御などなどまですべてExcel上で完結できるとしたら…?どうでしょう?

Excelでの帳票テンプレートのデザインは特殊なアドオンなどは一切不要です。プログラミングの経験がある方ならば、前提知識が一切なくともテンプレートを見ただけで何となく使い方を理解できる、そんな可読性の高い優れた仕組みを採用しています。

「コスト1/5」は本当なのか?盛ってるんじゃないの?と思われますよね?私ならばそういう感想を持ちます…。

それはぜひ登録不要のテストページや登録無料、試用期限なしのアカウント取得を通じて試してみて判断して下さい!!

キーブレイクによる改ページが簡単にできるようになりました

配列データ内の任意の項目(キー)が別の値になった場合に改ページしたいことがよくあります。例えば、請求明細の取引先が変わったら改ページ、のような感じです。 Docurainもこの要望を頂くことが多かったため、正式に「キーブレイク機能」として実装し、簡単に扱えるようにしました。

従来は任意の項目(キー)が別の値になったらページを分けたいという場合、該当項目の値ごとに分けた配列データとしてJSONを生成してください、というご案内をしていましたが、 キーブレイク機能により、単純な一次元配列のまま簡潔に記述することが出来ます。

早速試してみる

キーブレイク機能のサンプルのテンプレートとデータ(JSON)です。

ダウンロード f:id:yutay:20200602155129p:plain

[
  { "顧客名": "山田商店", "顧客ID": 100001, "商品": "みかん", "個数": 10, "単価": 100,  "請求額": 1000 }
  , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "ぶどう", "個数": 15, "単価": 120,  "請求額": 1800 }
  , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "バナナ", "個数": 5,  "単価": 200,  "請求額": 1000 }
  , /* 山田商店の明細がここに12個続く */
  , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "バナナ", "個数": 10, "単価": 100,  "請求額": 1000 }
  , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "レモン", "個数": 15, "単価": 120,  "請求額": 1800 }
  , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "メロン", "個数": 5,  "単価": 1200,  "請求額": 6000 }
  , /* 田中青果店の明細がここに7個続く) */
]

以下は↑のテンプレートとデータ(JSON)を使用してDocurainで出力したPDFです。
配列の各要素内の顧客名が別の値になったら改ページされていることがわかります。 f:id:yutay:20200602155152p:plain

データがどう変形されるか確認する

上記のサンプルデータのように一つの配列下に明細が並んでいるデータは、 $ENTITIES.chunk('顧客名') と記述することで 顧客名 をキーブレイクとして次のような二次元配列へと変換されます。

[
  [
    { "顧客名": "山田商店", "顧客ID": 100001, "商品": "みかん", "個数": 10, "単価": 100,  "請求額": 1000 }
    , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "ぶどう", "個数": 15, "単価": 120,  "請求額": 1800 }
    , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "バナナ", "個数": 5,  "単価": 200,  "請求額": 1000 }
    , /* 山田商店の明細がここに12個続く */
  ],
  [
    , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "いちご", "個数": 10, "単価": 100,  "請求額": 1000 }
    , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "すいか", "個数": 15, "単価": 120,  "請求額": 1800 }
    , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "メロン", "個数": 5,  "単価": 200,  "請求額": 1000 }
    , /* 田中青果店の明細がここに7個続く */
  ]
]

サンプルのテンプレートではこのトップレベルの配列を #foreach($dataGroup in $ENTITIES.chunk('顧客名')) のように繰り返しており、 上記のサンプルデータでは 顧客名 が "山田商店" の配列で1ページ目、"田中青果店"の配列で2ページ目が出力されることになります。

明細部の出力行数を制限

更に「1ページあたり10明細まで」のような制約を課したい場合は、以下のようにキーブレイクした配列を更に chunk(10) で分割します。 f:id:yutay:20200602155528p:plain

以下が上のテンプレートを使用してDocurainで出力したPDFです。(データは同じです)
顧客名が"山田商店"のデータは15個有りますが、1ページ目は10明細まで出力され、残りは2ページ目となっています。

f:id:yutay:20200602155414p:plain

#foreach($dataGroup in $ENTITIES.chunk('顧客名'))
  #foreach($dataOfPage in $dataGroup.chunk(10))
    ...
  #end
#end

と記述することで 顧客名 をキーブレイクとして顧客名ごとの二次元配列へ変形後、chunkで指定した10エントリごとの次のような配列に変形します。
(下記はイメージです。サンプル内ではforeachで繰り返すなかでchunk(10)により顧客ごとの配列をで10エントリごとの配列にしています)

[
  [
    [
      { "顧客名": "山田商店", "顧客ID": 100001, "商品": "みかん", "個数": 10, "単価": 100,  "請求額": 1000 }
      , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "ぶどう", "個数": 15, "単価": 120,  "請求額": 1800 }
      , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "バナナ", "個数": 5,  "単価": 200,  "請求額": 1000 }
      , ",...(山田商店の明細がここに7個続く)"
    ],
    [
      { "顧客名": "山田商店", "顧客ID": 100001, "商品": "みかん", "個数": 10, "単価": 100,  "請求額": 1000 }
      , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "ぶどう", "個数": 15, "単価": 120,  "請求額": 1800 }
      , { "顧客名": "山田商店", "顧客ID": 100001, "商品": "バナナ", "個数": 5,  "単価": 200,  "請求額": 1000 }
      , ",...(山田商店の明細がここに2個続く)"
    ]
  ],
  [
    [
      { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "いちご", "個数": 10, "単価": 100,  "請求額": 1000 }
      , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "すいか", "個数": 15, "単価": 120,  "請求額": 1800 }
      , { "顧客名": "田中青果店", "顧客ID": 200001, "商品": "メロン", "個数": 5,  "単価": 200,  "請求額": 1000 }
      , "...(田中青果店の明細がここに7個続く)"
    ]
  ]
]

今回使用したサンプルのテンプレートとデータ(JSON)こちらのページからも登録不要で試してみることが出来ます!

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。