コンバータ: #TSV 文字列 to #SVG ファイル

コンバータ: #TSV 文字列 to #SVG ファイル

translate Converter: #TSV String to #SVG File

数値TSV文字列を「積み上げ棒グラフのSVGファイル」に変換します。TSVの1列目は「年月」や「四半期」などの集計単位として扱われます。また、TSVの1行目は「営業担当」や「取引先」といったデータ系列名として扱われます。

Auto Step icon
Configs for this Auto Step
StrConfA
A: TSV文字列をセットしてください *#{EL}
StrConfB1
B1: 表示幅ピクセル値(ユニット名・棒グラフ・合計値)をCSVでセットしてください(デフォルト: 60,360,60)#{EL}
StrConfB2
B2: 系列の色をCSVでセットしてください(デフォルト: “#e66,#39f,#f90,#a3c,…”)#{EL}
StrConfB3
B3: グラフ目盛りの最大値をセットしてください(デフォルト: 100)#{EL}
StrConfB4
B4: 目盛りとして表示する文字列をCSVでセットしてください(例: “0,50,100%”)#{EL}
StrConfB5
B5: 合計値の接頭辞・接尾辞をCSVでセットしてください(例: “¥,”, “,千円”)#{EL}
BoolConfB6
B6: 凡例 (色見本+系列名), 表示しない ⇔ 表示する
BoolConfB7
B7: 目盛りラベル, 表示しない ⇔ 表示する
SelectConfC
C: 生成されたSVGを格納するファイル型データを選択してください (追加) *
StrConfB8
B8: 生成されるSVGのファイル名をセットしてください(デフォルト: chart.svg)#{EL}
SelectConfD1
D1: TSV行数を格納する数値型データを選択してください (更新)
SelectConfD2
D2: TSV行数(空行を無視)を格納する数値型データを選択してください (更新)
Script (click to open)
// Script Example of Business Process Automation
// for 'engine type: 3' ("GraalJS standard mode")
main();
function main(){ 
//// == Config Retrieving / 工程コンフィグの参照 ==
const strTsv        = configs.get       ( "StrConfA" );     // REQUIRED
  const arr2dTsv    = parseAsRectangular( strTsv );
  if( arr2dTsv.length === 0 ){
    throw new Error( "\n AutomatedTask ConfigError: Config {A: TSV} is empty \n" );
  }
  if( arr2dTsv.length === 1 ){
    throw new Error( "\n AutomatedTask ConfigError: Config {A: TSV} requires at least 2 lines (header and data) \n" );
  }
const strWidths     = configs.get       ( "StrConfB1" ) || "60,360,60"; // default
const arrWidths     = strWidths.split(",");
const numWidthUnit  = parseInt (arrWidths[0], 10) || 60;
const numWidthBar   = parseInt (arrWidths[1], 10) || 360;
const numWidthTotal = parseInt (arrWidths[2], 10) || 60;
const strColors     = configs.get       ( "StrConfB2" ) || "#e66,#39f,#f90,#a3c,#6a5,#678,#cc3,#999";     // default
const arrColors     = strColors.split(",");
const strMaxScale   = configs.get       ( "StrConfB3" );
const numMaxScale   = strMaxScale !== "" ? parseFloat(strMaxScale) : 100; // default 100
const strLabels     = configs.get       ( "StrConfB4" ) || "0,50,100%";
const arrLabels     = strLabels.split(",");
const strPrefSuf    = configs.get("StrConfB5") || ","; // デフォルトはカンマのみ
const arrPrefSuf    = strPrefSuf.split(",");
const strPrefix     = arrPrefSuf[0] || ""; // カンマの左側
const strSuffix     = arrPrefSuf.length > 1 ? arrPrefSuf[1] : ""; // カンマの右側
const isShowLegend  = configs.getObject ( "BoolConfB6" );
const isShowLabels  = configs.getObject ( "BoolConfB7" );
const strFileName   = configs.get       ( "StrConfB8" ) || "chart.svg";
const filesPocketC  = configs.getObject ( "SelectConfC" ); // REQUIRED
const numPocketD1   = configs.getObject ( "SelectConfD1" );
const numPocketD2   = configs.getObject ( "SelectConfD2" );
//// == Calculating / 演算 (SVG文字列の生成) ==
const marginL = 15; // 左側の余白
const marginR = 15; // 右側の余白
const svgTotalWidth = marginL + numWidthUnit + numWidthBar + numWidthTotal + marginR;
let svgY = 10; // Y座標の初期オフセット
let svgContent = `<style>text { font-family: sans-serif; font-size: 12px; fill: #333; }</style>\n`;
// XMLエスケープ用関数
function escapeXml(unsafe) {
    return unsafe.replace(/[<>&'"]/g, function (c) {
        switch (c) {
            case '<': return '&lt;';
            case '>': return '&gt;';
            case '&': return '&amp;';
            case '\'': return '&apos;';
            case '"': return '&quot;';
        }
    });
}
// --- 1. 凡例の描画 ---
if (isShowLegend) {
  let lx = marginL;
  for (let i = 1; i < arr2dTsv[0].length; i++) {
    const color = arrColors[(i - 1) % arrColors.length];
    const seriesName = escapeXml(arr2dTsv[0][i]);
    // 簡易的な文字幅推測(長すぎたら改行)
    const estimateW = seriesName.length * 12 + 25; 
    if (lx + estimateW > svgTotalWidth - marginR) {
      lx = marginL;
      svgY += 20;
    }
    svgContent += `<rect x="${lx}" y="${svgY}" width="10" height="10" fill="${color}" rx="2"/>\n`;
    svgContent += `<text x="${lx + 16}" y="${svgY + 9}">${seriesName}</text>\n`;
    lx += estimateW + 10;
  }
  svgY += 25; // 下部マージン
}
// --- 2. 目盛りの描画 ---
if (isShowLabels) {
  const stepW = numWidthBar / (arrLabels.length - 1 || 1);
  for (let i = 0; i < arrLabels.length; i++) {
    const xPos = marginL + numWidthUnit + (stepW * i);
    let anchor = i === 0 ? "start" : i === arrLabels.length - 1 ? "end" : "middle";
    svgContent += `<text x="${xPos}" y="${svgY + 10}" fill="#777" text-anchor="${anchor}">${escapeXml(arrLabels[i])}</text>\n`;
  }
  svgY += 20;
}
// --- 3. 棒グラフの描画 ---
const rowHeight = 18; // 1行の高さ (MdChartの間隔に寄せるため短縮)
for (let i = 1; i < arr2dTsv.length; i++) {
  // ユニット名(左側)
  svgContent += `<text x="${marginL}" y="${svgY + 13}">${escapeXml(arr2dTsv[i][0])}</text>\n`;
  // グラフ背景(グレー) ※MdChartに合わせて高さを10pxに変更
  svgContent += `<rect x="${marginL + numWidthUnit}" y="${svgY + 4}" width="${numWidthBar}" height="10" fill="#eee" rx="2"/>\n`;
  let currentX = marginL + numWidthUnit;
  let totalValue = 0;
  // 系列ごとの積み上げ描画
  for (let j = 1; j < arr2dTsv[i].length; j++) {
    const cellValue = parseFloat(arr2dTsv[i][j].replace(/[^0-9.-]/g, "")) || 0;
    totalValue += cellValue;
    const barW = Math.max(0, (cellValue / numMaxScale) * numWidthBar);
    
    if (barW > 0) {
      const color = arrColors[(j - 1) % arrColors.length];
      svgContent += `<rect x="${currentX}" y="${svgY + 4}" width="${barW}" height="10" fill="${color}"/>\n`;
      currentX += barW;
    }
  }
  // 合計値(右側)
  const totalStr = `${strPrefix}${totalValue.toLocaleString()}${strSuffix}`;
  svgContent += `<text x="${marginL + numWidthUnit + numWidthBar + 10}" y="${svgY + 13}" text-anchor="start">${escapeXml(totalStr)}</text>\n`;
  svgY += rowHeight; // 次の行へY座標を移動
}
// --- 4. 全体をSVGタグでラップ ---
const finalSvgHeight = svgY + 10;
let strSvg = `<?xml version="1.0" encoding="UTF-8"?>\n`;
strSvg += `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svgTotalWidth} ${finalSvgHeight}" width="${svgTotalWidth}" height="${finalSvgHeight}">\n`;
strSvg += `<rect width="100%" height="100%" fill="#ffffff"/>\n`; // 背景の透過防止用白塗り
strSvg += svgContent;
strSvg += `</svg>`;
//// == Data Updating / ワークフローデータへの代入 ==
// SVGファイルの保存 (C)
if (filesPocketC !== null) { 
  let filesSvg = engine.findData(filesPocketC);
  if (filesSvg === null) {
    filesSvg = new java.util.ArrayList();
  }
  
  // 指定されたファイル名、なければ "chart.svg"
  let finalFileName = strFileName;
  if (!finalFileName.toLowerCase().endsWith(".svg")) {
      finalFileName += ".svg";
  }
  filesSvg.add(
    new com.questetra.bpms.core.event.scripttask.NewQfile(
      finalFileName,
      "image/svg+xml; charset=UTF-8",
      strSvg
    )
  );
  engine.setData(filesPocketC, filesSvg);
}
// オリジナルのTSV文字列の行数 (D1)
if ( numPocketD1 !== null) { 
  const totalLines = strTsv.split("\n").length;
  engine.setData( numPocketD1, new java.math.BigDecimal(totalLines) );
}
// 空行を除いたTSV行数 (D2)
if ( numPocketD2 !== null) { 
  engine.setData( numPocketD2, new java.math.BigDecimal(arr2dTsv.length) );
}
} //////// END "main()" /////////////////////////////////////////////////////////////////
/**
 * Parses TSV string as a two-dimensional rectangular data matrix and creates a 2D array.
 * @param {string} strTsv - The input TSV string
 * @returns {Array<Array<string>>} Rectangular 2D array
 */
function parseAsRectangular( strTsv ){
  const arrTsv = strTsv.split("\n");
  let numMinWidth   = Infinity;
  let numMaxWidth   = 0;
  let numBlanklines = 0;
  
  const parsedRows = [];
  for( let i = 0; i < arrTsv.length; i++ ){
    const line = arrTsv[i];
    if( line === "" ){
      numBlanklines++;
      continue;
    }
    const arrCells = line.split("\t");
    const len = arrCells.length;
    if( len < numMinWidth ){ numMinWidth = len; }
    if( len > numMaxWidth ){ numMaxWidth = len; }
    parsedRows.push( arrCells );
  }
  
  if( numMinWidth === Infinity ){
    numMinWidth = 0;
  }
  engine.log( " AutomatedTask TsvDataCheck:" + 
              " MinWidth:" + numMinWidth +
              " MaxWidth:" + numMaxWidth +
              " Lines:" + arrTsv.length +
              " (BlankLines:" + numBlanklines + ")" );
  for( let i = 0; i < parsedRows.length; i++ ){
    const row = parsedRows[i];
    while( row.length < numMaxWidth ){
      row.push( "" );
    }
  }
  return parsedRows;
}
/*
### NOTES-en
- A bar chart (SVG file) is automatically generated each time a Case reaches this Automated Step.
    - From the second row of the input TSV string onward, all fields are parsed as "numeric strings", except for the first column (Column A).
        - Only the first column (Column A) is considered as the aggregation unit name, such as "YYYY-MM" or "Quarter".
        - If characters other than a "period (.)", a "minus sign (-)", and "0-9" exist, the "stripped string" is parsed.
        - Even if commas exist, they are considered thousands separators, and the "stripped string" is parsed.
        - The "stripped string" is parsed using `parseFloat()`.
        - If parsing fails, the value is evaluated as `0`.
    - The generated "SVG file" will be added to the specified File-type data item.
        - For an N-row, 2-column TSV string, a simple bar chart is displayed (e.g., "Monthly Sales").
        - For an N-row, M-column TSV string, a stacked bar chart with (M-1) "series" is displayed.
        - The width of each series is calculated and drawn based on the "Max Scale Value (B3)".
- The input TSV is parsed as the "simplest tab-separated string".
- The number of lines in the input TSV string can also be saved as Case Data.
### NOTES-ja
- このアドオン自動工程にケース(案件)が到達する度に、自動的に棒グラフ(SVGファイル)が生成されます。
    - 入力TSV文字列の2行目以降は、全フィールドが「数値文字列」として解析されます。※1列目(A列)を除く
        - 1列目(A列)だけは、「年月」や「四半期」などの集計単位名とみなされます。
        - 「ピリオド(.)」と「マイナス記号」と「0~9」以外の文字が存在する場合、「除去された文字列」が解析されます。
        - カンマが存在する場合も、(桁区切り文字とみなされ)、「除去された文字列」が解析されます。
        - 「除去された文字列」が `parseFloat()` によって解析されます。
        - 解析に失敗した場合 `0` とみなされます。
    - 生成された "SVGファイル" は、指定されたファイル型データ項目に追加保存されます。
        - N行2列TSV文字列の場合、シンプル棒グラフが表示されます。 ※「月ごとの売上」など
        - N行M列TSV文字列の場合、"系列" が (M-1) 個の積み上げ棒グラフが表示されます。
        - 各系列の横幅は「グラフ目盛りの最大値(B3)」を基準に算出・描画されます。
- 入力TSVは「もっともシンプルなタブ区切り文字列」としてパースされます。
- 入力TSV文字列の行数もケースデータとして格納可能です。
#### TSV example
YYYY-MM	Tanaka	Sato	Suzuki	Yamada
2026-01	610,000	520,000	380,000	450,000
2026-02	580,000	490,000	410,000	480,000
2026-03	700,000	600,000	450,000	550,000
2026-04	550,000	480,000	390,000	420,000
2026-05	590,000	510,000	420,000	460,000
2026-06	630,000	550,000	460,000	510,000
2026-07	680,000	620,000	250,000	280,000
2026-08	570,000	530,000	230,000	290,000
2026-09	650,000	310,000	220,000	250,000
2026-10	560,000	290,000	240,000	270,000
2026-11	600,000	300,000	250,000	260,000
2026-12	800,000	720,000	600,000	680,000
### APPENDIX-en
- Blank lines within the input TSV string are ignored.
- Please set series colors corresponding to the number of series in the TSV (TSV column count - 1).
    - (It is fine to set more colors than required)
    - Example of high-contrast series colors: "#e66,#39f,#f90,#a3c,#6a5,#678,#cc3,#999".
    - Example of warm/cool color gradation: "#c42,#e84,#fb6,#cc3,#6ad,#9cf,#cde,#eef".
- Special characters like `<`, `>`, `&` in the TSV string will be automatically XML-escaped to prevent SVG file corruption.
- You can specify a prefix and suffix for the total value.
    - To add only a prefix: Set `$,` → `$1,200,000`
    - To add only a suffix: Set `, items` → `150 items`
    - To add both: Set `Approx. , hours` → `Approx. 40 hours`
    - To include a space: Set `$ ,` → `$ 1,200`
### APPENDIX-ja
- 入力TSV文字列内の空行は無視されます。
- 系列の色は、TSV内の系列の数(TSV列数 - 1)だけセットしてください。(多く設定する分には構いません)
    - 系列のメリハリを効かせる例: "#e66,#39f,#f90,#a3c,#6a5,#678,#cc3,#999"
    - 寒色暖色でグラデーション例: "#c42,#e84,#fb6,#cc3,#6ad,#9cf,#cde,#eef"
- TSV内の `<` や `&` といった特殊記号は、SVGファイルの破損を防ぐために自動的にXMLエスケープされます。
- 合計値には接頭辞・接尾辞を指定できます。
    - 前だけにつけたい場合: `¥,` と設定 → `¥1,200,000`
    - 後ろだけにつけたい場合: `,件` と設定 → `150件`
    - 前後両方につけたい場合: `約,時間` と設定 → `約40時間`
    - 間にスペースを入れたい場合: `$ ,` と設定 → `$ 1,200`
*/

Download

warning 自由改変可能な JavaScript (ECMAScript) コードです。いかなる保証もありません。
(アドオン自動工程のインストールは Professional editionでのみ可能です)

Notes

  • このアドオン自動工程にケース(案件)が到達する度に、自動的に棒グラフ(SVGファイル)が生成されます。
    • 入力TSV文字列の2行目以降は、全フィールドが「数値文字列」として解析されます。※1列目(A列)を除く
      • 1列目(A列)だけは、「年月」や「四半期」などの集計単位名とみなされます。
      • 「ピリオド(.)」と「マイナス記号」と「0~9」以外の文字が存在する場合、「除去された文字列」が解析されます。
      • カンマが存在する場合も、(桁区切り文字とみなされ)、「除去された文字列」が解析されます。
      • 「除去された文字列」が parseFloat() によって解析されます。
      • 解析に失敗した場合 0 とみなされます。
    • 生成された “SVGファイル” は、指定されたファイル型データ項目に追加保存されます。
      • N行2列TSV文字列の場合、シンプル棒グラフが表示されます。 ※「月ごとの売上」など
      • N行M列TSV文字列の場合、”系列” が (M-1) 個の積み上げ棒グラフが表示されます。
      • 各系列の横幅は「グラフ目盛りの最大値(B3)」を基準に算出・描画されます。
  • 入力TSVは「もっともシンプルなタブ区切り文字列」としてパースされます。
  • 入力TSV文字列の行数もケースデータとして格納可能です。

Capture

Appendix

  • 入力TSV文字列内の空行は無視されます。
  • 系列の色は、TSV内の系列の数(TSV列数 – 1)だけセットしてください。(多く設定する分には構いません)
    • 系列のメリハリを効かせる例: “#e66,#39f,#f90,#a3c,#6a5,#678,#cc3,#999”
    • 寒色暖色でグラデーション例: “#c42,#e84,#fb6,#cc3,#6ad,#9cf,#cde,#eef”
  • TSV内の <& といった特殊記号は、SVGファイルの破損を防ぐために自動的にXMLエスケープされます。
  • 合計値には接頭辞・接尾辞を指定できます。
    • 前だけにつけたい場合: ¥, と設定 → ¥1,200,000
    • 後ろだけにつけたい場合: ,件 と設定 → 150件
    • 前後両方につけたい場合: 約,時間 と設定 → 約40時間
    • 間にスペースを入れたい場合: $ , と設定 → $ 1,200

See Also

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む