DocRaptor: Generate PDF

DocRaptor: PDF 生成

This item generates a PDF file from HTML.

Basic Configs
Step Name
Note
Auto Step icon
Configs for this Auto Step
conf_Auth
C1: Basic Auth Setting in which API Key is set as Username *
conf_SourceHtml
C2: Data item that stores the source HTML *
conf_Files
C3: Data item to save the generated PDF file *
conf_DeleteOtherFiles
C4: Delete other files when saving
conf_FileName
C5: File name to save as *#{EL}

Notes

  • Set the API Key of your DocRaptor account as username of the Basic Authentication
    • Leave the password blank
  • PDF files generated in a debug process will contain the watermark of DocRaptor
    • Instead, they will not consume any of the documents assigned to your DocRaptor account
  • Document generation logs, which you can refer to on DocRaptor web site when logged in, are to be named as “Questetra-m{App ID}-p{Process ID}”
  • When you specify the source HTML by a file-type data item:
    • only one file can be attached
    • file size allowed is up to 1MB (1,048,576 byte)

Capture

See Also

Script (click to open)
  • An XML file that contains the code below is available to download
    • docraptor-pdf-generate.xml (C) Questetra, Inc. (MIT License)
    • If you are using Professional, you can modify the contents of this file and use it as your own add-on auto step


const MAX_SOURCE_FILE_SIZE = 1048576; // 1MB。標準アイテムではファイルの読み出しサイズに制限はないが、無制限もよくないので

function main(){
    //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
    const auth = configs.get('conf_Auth');
    const html = retrieveSourceHtml();
    const filesDef = configs.getObject('conf_Files');
    const deleteOtherFiles = configs.getObject('conf_DeleteOtherFiles');
    const fileName = retrieveFileName();

    //// == 演算 / Calculating ==
    const statusId = generatePdf(auth, html);

    // 処理状況を確認し、未完了なら proceed() に進む
    if (checkStatusAndSaveFile(auth, statusId, filesDef, deleteOtherFiles, fileName) === false) {
        engine.saveTemporaryData(statusId);
        return false;
    }
}

function proceed() {
    //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
    const auth = configs.get('conf_Auth');
    const filesDef = configs.getObject('conf_Files');
    const deleteOtherFiles = configs.getObject('conf_DeleteOtherFiles');
    const fileName = retrieveFileName();

    //// == Restoring Temporary Data / 一時データの読み出し ==
    const statusId = engine.restoreTemporaryData();
    if (statusId === null) {
        throw 'Temporary data has not been saved.';
    }

    //// == 演算 / Calculating ==
    if (checkStatusAndSaveFile(auth, statusId, filesDef, deleteOtherFiles, fileName) === false) {
        return false;
    }
}

/**
  * config に設定したファイル型/文字型データ項目から変換元 HTML を読み出す
  * @return {String} html
  */
function retrieveSourceHtml() {
    const sourceHtmlDef = configs.getObject('conf_SourceHtml');
    if (sourceHtmlDef.matchDataType('FILE')) { // ファイル型データ項目の場合
        return retrieveSourceHtmlFromFile(sourceHtmlDef);
    }
    // 文字型データ項目の場合
    const html = engine.findData(sourceHtmlDef);
    if (html === null) {
        throw 'Source HTML is empty.';
    }
    return html;
}

/**
  * ファイル型データ項目から変換元 HTML を読み出す
  * @param {ProcessDataDefinitionView} sourceFilesDef
  * @return {String} html
  */
function retrieveSourceHtmlFromFile(sourceFilesDef) {
    const sourceFiles = engine.findData(sourceFilesDef);
    if (sourceFiles === null) {
        throw 'No source file attached.';
    }
    if (sourceFiles.size() > 1) {
        throw 'More than one source files attached.';
    }
    const sourceFile = sourceFiles.get(0);
    if (sourceFile.getLength() === 0) {
        throw 'Source file is empty.';
    }
    if (sourceFile.getLength() > MAX_SOURCE_FILE_SIZE) {
        throw 'Source file is too large.';
    }
    const contentType = sourceFile.getContentType();
    if (!contentType.startsWith('text/html')) {
        throw 'Content-Type of the source file is not text/html.';
    }
    return fileRepository.readFile(sourceFile, decideCharset(sourceFile));
}

/**
  * config から保存する際のファイル名を読み出す
  * 空の場合はエラー
  * @return {String} fileName
  */
function retrieveFileName() {
    const fileName = configs.get('conf_FileName');
    if (fileName === '') {
        throw 'File name is blank.';
    }
    return fileName;
}

/**
  * ファイルの charset を読み出す
  * charset が設定されていない場合は UTF-8 を返す
  * @param {QfileView} sourceFile
  * @return {String} charset
  */
function decideCharset(sourceFile) {
    const charset = sourceFile.getCharset();
    if (charset === null) {
        return 'UTF-8';
    }
    return charset;
}

/**
  * DocRaptor の PDF 生成 API に POST リクエストを送信し、ステータス ID を返す
  * プロセスがデバッグ実行の場合、PDF 生成をテスト扱いにする
  * @param {String} auth  認証設定名
  * @param {String} html  HTML ソース
  * @return {String} statusId  ステータス ID
  */
function generatePdf(auth, html) {
    const requestBody = generateRequestBody(html);
    const response = httpClient.begin()
        .authSetting(auth)
        .body(requestBody, 'application/json')
        .post('https://api.docraptor.com/docs');
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to post PDF generation request. status: ${status}`;
    }
    return JSON.parse(responseStr).status_id;
}

/**
  * PDF ファイル生成 API のリクエストボディを生成し、JSON 文字列で返す
  * - 非同期生成をリクエスト
  * - プロセスがデバッグ実行の場合、テストリクエスト扱いにする
  * @param {String} html
  * @return {String} requestBody
  */
function generateRequestBody(html) {
    const requestBody = {
        document_type: 'pdf',
        document_content: html,
        async: true,
        name: `Questetra-m${processInstance.getProcessModelInfoId()}-p${processInstance.getProcessInstanceId()}`
    };
    // プロセスがデバッグ実行の場合、test パラメータに true を設定
    if (processInstance.getProcessInstanceDebug()) {
        requestBody.test = true;
    }
    return JSON.stringify(requestBody);
}

/**
  * PDF ファイル生成の処理状態を確認し、完了していればファイルを保存する
  * 未完了の場合は false を返す
  * @param {String} auth  認証設定名
  * @param {String} statusId  ステータス ID
  * @param {DataDefinitionView} filesDef  ファイルを保存するデータ項目
  * @param {boolean} deleteOtherFiles  添付されている他のファイルを削除するかどうか
  * @param {String} fileName  保存ファイル名
  * @return {boolean}
  */
function checkStatusAndSaveFile(auth, statusId, filesDef, deleteOtherFiles, fileName) {
    const response = httpClient.begin()
        .authSetting(auth)
        .get(`https://docraptor.com/status/${statusId}`);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to get PDF generation status. status: ${status}`;
    }
    const json = JSON.parse(responseStr);
    switch (json.status) {
        case 'failed':
            engine.log(JSON.stringify(json));
            throw 'Failed to generate PDF.';
        case 'completed':
            downloadAndSaveFile(auth, json.download_url, filesDef, deleteOtherFiles, fileName);
            break;
        default:
            return false;
    }
}

/**
  * PDF ファイルをダウンロードし、を保存する
  * @param {String} auth  認証設定名
  * @param {String} downloadUrl  ダウンロード先 URL
  * @param {DataDefinitionView} filesDef  ファイルを保存するデータ項目
  * @param {boolean} deleteOtherFiles  添付されている他のファイルを削除するかどうか
  * @param {String} fileName  保存ファイル名
  * @return {boolean}
  */
function downloadAndSaveFile(auth, downloadUrl, filesDef, deleteOtherFiles, fileName) {
    const response = httpClient.begin()
        .authSetting(auth)
        .get(downloadUrl);
    const status = response.getStatusCode();
    if (status !== 200) {
        engine.log(response.getResponseAsString());
        throw `Failed to download PDF file. status: ${status}`;
    }
    const qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
        fileName, response.getContentType(), response.getResponse()
    );
    let files = engine.findData(filesDef);
    if (files === null || deleteOtherFiles) {
        files = new java.util.ArrayList();
    }
    files.add(qfile);
    engine.setData(filesDef, files);
}

    
%d bloggers like this: