ViewerFormFiller.cs
// 
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © MESCIUS inc. All rights reserved.
// 
using System.IO;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;

namespace DsPdfWeb.Demos
{
    // 
    // このサンプルでは、PDF ビューワのフォームフィラーについて紹介しています。
    // 例として商業用賃貸申込書を読み込みこんでいます。   
    // フォーム入力をより簡単にするため、formFiller オプションを使用しフォームフィラー
    // ダイアログの表示と動作をカスタマイズすることができます。
    // ドキュメントを読み込むと、クライアント側のshowFormFillerメソッドを使って
    // フォームフィラーダイアログが表示されます。

    // このセクションのサンプルでは、クライアント上のPDF ビューワが、サーバー上で
    // 動作するDsPdfに接続している場合に、PDF ビューワで使用できるPDFファイルの編集機能
    // (注釈やフォームフィールドの追加や編集、ページの回転など)を主に紹介しています。
    // 
    // ビューワの編集機能を有効にするためには、supportApiプロパティに、ビューワにて
    // 使用が想定される編集機能をサポートするAPIのすべてまたは一部を実装したサーバのURLを
    // 設定する必要があります。このDsPdfデモサイトでは、それらのAPIを提供しており、
    // このサンプルでPDF ビューワを開いた際に編集のデモを行うことができます。
    // このサンプルをダウンロードすると、サンプルPDFを生成する.NET コンソール
    // アプリプロジェクトに加えて、必要なAPIを提供するASP.NET Coreプロジェクトも
    // ダウンロードしたzipに含まれています(ダウンロードしたzipのGcPdfViewerWeb
    // フォルダ内にあります)。具体的には、APIを実装し、特別なコントローラを介して
    // そのAPIを提供するプロジェクトが含まれています。実際にこのDsPdfのデモサイトで
    // 使用されているものと同じコントローラで、どのASP.NET Coreサイトでも
    // ビューワの編集機能を有効にすることができます。
    // 
    // 詳細については、サンプルからダウンロードしたzipにある以下のファイルをご参照ください。
    // - GcPdfViewerWeb\SupportApiDemo: ASP.NET Coreのサンプルwebサイトです。
    // - GcPdfViewerWeb\SupportApiDemo.sln: サンプルwebサイトを実行するためのソリューションです。
    // - GcPdfViewerWeb\SupportApi: 実装済みのSupportAPIです(どのサイトでもご使用いただけます)。
    // - GcPdfViewerWeb\SupportApi\Controllers\GcPdfViewerController.cs: SupportAPIのコントローラです。
    // 
    // このセクションのサンプルは、現時点ではC#でしかご利用いただけませんのでご注意ください。
    // 
    public class ViewerFormFiller
    {
        public void CreatePDF(Stream stream)
        {
            CreatePDF(stream, 0);
        }

        public void CreatePDF(Stream stream, int _)
        {
            var doc = new GcPdfDocument();
            using var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "Commercial-Rental-Application-Form.pdf"));
            doc.Load(fs);
            doc.Save(stream);
        }

        public const string JS_CODE = @"
var requiredPhonePattern = '^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$';
var requiredPhoneValidationMessage = '有効な形式: 1234567890, (123)456-7890,\n 123-456-7890, 123.456.7890, +31636363634, 075-63546725';
var optionalPhonePattern = '^$|^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$';
var optionalPhoneValidationMessage = '入力は任意です。有効な形式:\n 1234567890, (123)456-7890,\n 123-456-7890, 123.456.7890, +31636363634, 075-63546725';
var ovnPark_FieldValue_On = '夜間駐車があります', ovnPark_FieldValue_Off = 'ありません';
var updatingFieldsFlag = false;
function combineTwoFieldsIntoOneValue(destfieldName, fieldName2, formFiller) {
            var addr1 = formFiller.getFieldByName(destfieldName);
            var addr2 = formFiller.getFieldByName(fieldName2);
            if(addr1 && addr2) {
                if(addr2.fieldValue) {
                    addr1.fieldValue = addr1.fieldValue + '\n' + addr2.fieldValue;
                    addr2.fieldValue = '';
                    formFiller.onFieldChanged(addr1);
                    formFiller.onFieldChanged(addr2);
                }
            }
}
function splitFieldValueIntoTwoFields(srcfieldName, fieldName2, formFiller) {
            var addr1 = formFiller.getFieldByName(srcfieldName);
            var addr2 = formFiller.getFieldByName(fieldName2);
            if(addr1 && addr2) {
                var s = addr1.fieldValue;
                var nlInd = s.indexOf('\n');
                if(nlInd !== -1) {
                    var firstPart = s.substring(0, nlInd);
                    var secondPart = s.substr(nlInd + 1);
                    addr1.fieldValue = firstPart;
                    addr2.fieldValue = secondPart;
                } else {
                    addr2.fieldValue = '';
                }
                formFiller.onFieldChanged(addr1);
                formFiller.onFieldChanged(addr2);
            }
}
function createPdfViewer(selector, baseOptions) {
    var options = baseOptions || {};
    // 'RentalApplicationForm'ドキュメント用のフォームフィラーオプション
    var formFiller_RentalApplicationForm = {
        onInitialize: function(formFiller) {
            combineTwoFieldsIntoOneValue('Addr1', 'Addr2', formFiller);
            combineTwoFieldsIntoOneValue('BusAddr1', 'BusAddr2', formFiller);
            var ovnParkField = formFiller.getFieldByName('OvnPark');
            if(ovnParkField.fieldValue) {
                var ovnPark_LowVal = (ovnParkField.fieldValue || '').toLowerCase();
                if(!ovnPark_LowVal || ovnPark_LowVal.indexOf('no') !== -1 || ovnPark_LowVal === 'off' || ovnPark_LowVal === 'false') {
                    ovnParkField.fieldValue = 'Off';
                    formFiller.onFieldChanged(ovnParkField);
                } else {
                    ovnPark_FieldValue_On = ovnParkField.fieldValue;// 現在の'On'の値を記憶します
                    ovnParkField.fieldValue = 'On';
                    formFiller.onFieldChanged(ovnParkField);
                }
            }
        },
        beforeApplyChanges: function(formFiller) {
            splitFieldValueIntoTwoFields('Addr1', 'Addr2', formFiller);
            splitFieldValueIntoTwoFields('BusAddr1', 'BusAddr2', formFiller);
            var ovnParkField = formFiller.getFieldByName('OvnPark');
            if(ovnParkField.fieldName === 'OvnPark') {
                if(ovnParkField.fieldValue === 'On') {
                    ovnParkField.fieldValue = ovnPark_FieldValue_On;
                } else {
                    ovnParkField.fieldValue = ovnPark_FieldValue_Off;
                }
            }
            return true;
        },
        beforeFieldChange: function(changedField, formFiller) {
            if(updatingFieldsFlag) 
                return true;
            var setFieldValue = function(chkboxFieldName, newVal) {
                var tmpField = formFiller.getFieldByName(chkboxFieldName);
                if(tmpField.fieldValue !== newVal) {
                    tmpField.fieldValue = newVal;
                    formFiller.onFieldChanged(tmpField);
                }
            };
            updatingFieldsFlag = true;
            try {
                var fieldName = changedField.fieldName;
                var fieldValue = changedField.fieldValue;
                if(fieldName === 'Married1CHK' || fieldName === 'Single1CHK') {
                    if(fieldName === 'Married1CHK')
                        setFieldValue('Single1CHK', 'Off');
                    if(fieldName === 'Single1CHK')
                        setFieldValue('Married1CHK', 'Off');
                    if(fieldValue === 'Off')
                        return false; // 未チェックは拒否します
                }
                if(fieldName === 'CorpCHK' || fieldName === 'GPCHK' || fieldName === 'IndivCHK') {
                    // 他のフィールドのチェックを外します
                    switch(fieldName) {
                        case 'CorpCHK':
                            setFieldValue('GPCHK', 'Off');
                            setFieldValue('IndivCHK', 'Off');
                            break;
                        case 'GPCHK':
                            setFieldValue('CorpCHK', 'Off');
                            setFieldValue('IndivCHK', 'Off');
                            break;
                        case 'IndivCHK':
                            setFieldValue('CorpCHK', 'Off');
                            setFieldValue('GPCHK', 'Off');
                            break;
                        default:
                            break;
                    }
                    if(fieldValue === 'Off')
                        return false; // 未チェックは拒否します
                }
            } finally {
                updatingFieldsFlag = false;
            }
            return true;
        },

        mappings: {
            'CustomContent1': { 
                type: 'custom-content', 
                content: '<h2>商業用賃貸申込書</h2>'
            },
            'AppDate': {                                
                title: '申請日',
                displayname: '申請日',
                type: 'date',
                defaultvalue: new Date().toJSON().slice(0, 10)
            },
            'Entity': {                
                autofocus: true,
                title: '個人・組合・法人名',
                displayname: '申請者または事業者',
                placeholder: '個人・組合・法人名',
                required: true,
                validationmessage: '入力は必須です',
                validateoninput: true
            },
            'CustomContent2': { 
                type: 'custom-content', 
                content: '<table>	<tr><td style=\'vertical-align:top;\'>		<i><u>法人の方:</u></i>	</td><td style=\'vertical-align:top;\'>		<i>定款の提出と2年分の年次報告書と法人税申告書の提出が必要です。</i>	</td></tr>	<tr><td style=\'vertical-align:top;\'>		<i><u>組合の方:</u></i>	</td><td style=\'vertical-align:top;\'>		<i>組合契約書に加えて、パートナーそれぞれの現在の個人財務報告書と2年分の個人納税申告書を提出する必要があります。</i>	</td></tr>	<tr><td style=\'vertical-align:top;\'>		<i><u>個人の方:</u></i>	</td><td style=\'vertical-align:top;\'>		<i>個人の貸借対照表と2年分の確定申告書を提出してください。運転免許証番号の記載が必要です。</i>	</td></tr></table>'
            },
            'Addr1': {       
                title: '現在の本社または自宅住所(私書箱は不可)',
                displayname: '現在の住所',
                placeholder: '現在の本社または自宅住所(私書箱は不可)',
                multiline: true,
                required: true,
                validationmessage: '入力は必須です',
                validateoninput: true
            },
            'Addr2': {        
                hidden: true,
            },
            'CorpPhone': {                
                title: '会社の電話番号',
                displayname: '会社の電話番号',
                placeholder: '会社の電話番号',
                type: 'tel',
                pattern: requiredPhonePattern,
                validationmessage: requiredPhoneValidationMessage,
                validateoninput: true
            },

            'CorpState': {
                title: '会社が登録されている自治体',
                displayname: '会社の登録自治体',
                placeholder: '会社の登録自治体',
                validateoninput: true,
                validator: function(fieldValue, field) { 
                    if(!fieldValue || !fieldValue.trim())
                        return '入力は必須です';
                    if(fieldValue.toLowerCase().indexOf('宮城県') === -1)
                        return '宮城県のみ入力可能です';
                    return true;
                }
            },
            'DBA': {                
                title: '会社の事業内容',
                displayname: '会社の事業内容',
                placeholder: '事業内容',
                required: true
            },
            'HomePhone': {                
                title: '自宅の電話番号 (組合または個人の場合)',
                displayname: '自宅の電話番号',
                placeholder: '自宅の電話番号 (任意)',
                type: 'tel',
                pattern: optionalPhonePattern,
                validationmessage: optionalPhoneValidationMessage,
                validateoninput: true
            },
            'PtnrState': {
                title: '組合結成の地域',
                displayname: '組合結成の地域',
                placeholder: '組合結成の地域'
            },
            'LocalPhone': {                
                title: '市内電話番号',
                displayname: '市内電話番号',
                placeholder: '市内電話番号 (任意)',
                type: 'tel',
                pattern: optionalPhonePattern,
                validationmessage: optionalPhoneValidationMessage,
                validateoninput: true
            },

            'Use1': {                
                title: '使用目的の詳細な説明',
                displayname: '使用目的',
                placeholder: '使用目的 (1行目)',
                required: true
            },
            'Use2': {       
                title: '使用目的の詳細な説明',
                displayname: '',
                placeholder: '使用目的 (2行目)'
            },
            'Use3': {                
                title: '使用目的の詳細な説明',
                displayname: '',
                placeholder: '使用目的 (3行目)'
            },

            'CustomContent3': { 
                type: 'custom-content', 
                content: '<h2>現在の事業概要</h2>'
            },
            'CurrLandlord': {
                title: '現在の事業主名',
                displayname: '事業主名',
                placeholder: '事業主名'
            },

            LandLordPhone: {
                title: '電話番号',
                displayname: '電話番号',
                placeholder: '電話番号 (任意)',
                type: 'tel',
                pattern: optionalPhonePattern,
                validationmessage: optionalPhoneValidationMessage,
                validateoninput: true

            },
            BusAddr1: {
                title: '現在の事業所所在地',
                displayname: '現在の事業所所在地',
                placeholder: '現在の事業所所在地',
                multiline: true
            },
            BusAddr2: {
             hidden: true
            },
            NumYears: {
                title: '現在の所在地での年数',
                displayname: '現在の所在地での年数',
                placeholder: '現在の所在地での年数',
                type: 'number',
                min: 0,
                max: 100
            },
            NumEmployees: {
                title: '従業員数',
                displayname: '従業員数',
                placeholder: '従業員数',
                type: 'number',
                min: 0,
                max: 1000000
            },
            PkgSpaces: {
                title: '必要な駐車場の数',
                displayname: '必要な駐車場の数',
                placeholder: '駐車場の数',
                type: 'number',
                min: 0,
                max: 1000000
            },
            OvnPark: {
                title: '夜間駐車はあるか?',
                displayname: '夜間駐車はあるか?',
                type: 'checkbox'
            },
            BankAcct: {
                title: '銀行の口座番号',
                displayname: '口座番号',
                placeholder: '口座番号',
                minlength: 8,
                maxlength: 12,
                validationmessage: '8~12桁で入力してください',
                validateoninput: true
            },
            BankPhone: {
                title: '銀行の電話番号',
                displayname: '電話番号'
            },
            BankRef: {
                title: 'バンク・リファレンス',
                displayname: 'バンク・リファレンス'
            },
            BankContact: {
                title: '担当者名',
                displayname: '担当者名'
            },

            'CustomContent4': { 
                type: 'custom-content', 
                content: '<h4>敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください</h4>'
            },
            HazSub1: {
                title: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください',
                placeholder: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください (1行目)',
                nolabel: true
            },
            HazSub2: {
                title: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください',
                placeholder: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください (2行目)',
                nolabel: true
            },
            HazSub3: {
                title: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください',
                placeholder: '敷地内に保有する予定のすべての有害物質とそのおおよその量を記載してください (3行目)',
                nolabel: true
            },

            'CustomContent5': { 
                type: 'custom-content', 
                content: '<h4>1つチェックしてください</h4>'
            },
            CorpCHK: {
                title: '法人',
                displayname: '法人'
            },
            GPCHK: {
                title: '無限責任組合員',
                displayname: '無限責任組合員'
            },
            IndivCHK: {
                title: '賃貸借契約を締結する個人',
                displayname: '賃貸借契約を締結する個人'
            },

            'CustomContent6': { 
                type: 'custom-content', 
                content: '<h4>代表者</h4>'
            },
            Fullname1: {
                title: '氏名',
                displayname: '氏名',
                placeholder: '氏名'
            },
            Title1: {
                title: '役職',
                displayname: '役職',
                placeholder: '役職'

            },
            SSN1: {
                title: '社会保障番号 (SSN)',
                displayname: '社会保障番号',
                placeholder: 'SSN (9桁)'
            },
            Married1CHK: {
                title: '既婚',
                displayname: '既婚'
            },
            Single1CHK: {
                title: '未婚',
                displayname: '未婚'
            },
            DL1: {
                title: '運転免許証',
                displayname: '運転免許証',
                placeholder: '運転免許証'
            },
            DLState1: {
                title: '運転免許証の発行自治体',
                displayname: '運転免許証の発行自治体',
                placeholder: '運転免許証の発行自治体'
            },

            'CustomContent7': { 
                type: 'custom-content', 
                content: '<h4>配偶者</h4>'
            },
            SpFullname1: {
                title: '氏名',
                displayname: '配偶者の氏名',
                placeholder: '氏名'            
            },
            SpSSN1: {
                title: '社会保障番号 (SSN)',
                displayname: '社会保障番号',
                placeholder: 'SSN (9桁)'
            },

            'CustomContent8': { 
                type: 'custom-content', 
                content: '<h4現在の家主または住宅ローン会社</h4>'
            },
            LL1: {
                title: '会社名',
                displayname: '会社名',
                placeholder: '会社名'
            },
            LLPhone1: {
                title: '会社の電話番号',
                displayname: '電話番号',
                placeholder: '電話番号'
            },

            ResAddr1A: {
                title: '現在の住所',
                displayname: '現在の住所',
                placeholder: '現在の住所 (1行目)'
            },
            ResAddr1B: {
                title: '現在の住所',
                displayname: '',
                placeholder: '現在の住所 (2行目)'
            },
            HmPhone1: {
                title: '自宅の電話',
                displayname: '自宅の電話',
                placeholder: '自宅の電話'
            },
            Years1: {
                title: '居住年数',
                displayname: '居住年数',
                placeholder: '居住年数',
                type: 'number',
                min: 0,
                max: 100
            },

            PrintName1: {
                title: '署名 (名前)',
                displayname: '署名 (名前)',
                placeholder: '署名 (名前)'
            },
            Title1a: {
                title: '署名 (役職)',
                displayname: '署名 (役職)',
                placeholder: '署名 (役職)'
            },
            SignedDate: {
                title: '署名日',
                displayname: '署名日',
                placeholder: '署名日',
                type: 'date',
                defaultvalue: new Date().toJSON().slice(0, 10)
            },

            Fullname2: {
             hidden: true
            },
            Title2: {
             hidden: true
            },
            SSN2: {
             hidden: true
            },
            Married2CHK: {
             hidden: true
            },
            Single2CHK: {
             hidden: true
            },
            DL2: {
             hidden: true
            },
            DLState2: {
             hidden: true
            },
            SpFullname2: {
             hidden: true
            },
            SpSSN2: {
             hidden: true
            },
            LL2: {
             hidden: true
            },
            LLPhone2: {
             hidden: true
            },
            ResAddr2A: {
             hidden: true
            },
            ResAddr2B: {
             hidden: true
            },
            HmPhone2: {
             hidden: true
            },
            Years2: {
             hidden: true
            },

            TradeRef1: {
                title: 'トレード・レファレンス',
                displayname: 'トレード・レファレンス',
                placeholder: 'トレード・レファレンス (1行目)'
            },
            TradeRef2: {
                title: 'Trade references',
                displayname: '',
                placeholder: 'トレード・レファレンス (2行目)'
            },
            TradeRef3: {
                title: 'Trade references',
                displayname: '',
                placeholder: 'トレード・レファレンス (3行目)'
            },

            PrintName2: {
             hidden: true
            },
            Title2a: {
             hidden: true
            },
            SignedDate2: {
             hidden: true
            }
        }
    };
    
    var viewer = new GcPdfViewer(selector, options);

    // デフォルトのサイドバーパネルを追加します
    viewer.addDefaultPanels();

    // ツールバーのボタンを設定します
    viewer.toolbarLayout.viewer = { 
        default: ['open', 'save', 'form-filler', '$navigation', '$split', 'text-selection', 'pan', '$zoom', '$fullscreen', 'print', 'title', 'about'], 
        mobile: ['open', 'save', 'form-filler', '$navigation', 'title', 'about'], 
        fullscreen: ['$fullscreen', 'open', 'save', 'form-filler', '$navigation', '$split', 'text-selection', 'pan', '$zoom', 'print', 'title', 'about'] 
    };
    if(!viewer.options.supportApi) {
        viewer.options.supportApi = {
            apiUrl: (window.__baseUrl || '') + 'support-api/gc-pdf-viewer',
            token: window.afToken || '',
            webSocketUrl: (window.__baseUrl || '') + 'signalr',
            suppressInfoMessages: true, suppressErrorMessages: true
        };
    }
    viewer.applyToolbarLayout();
    viewer.applyOptions();
    viewer.onAfterOpen.register(function(args) {
        var fileName = viewer.fileName;
        if (fileName.indexOf('viewer-form-filler') !== -1 || 
            fileName.indexOf('get-sample-pdf') !== -1 ||
            fileName.indexOf('GetSamplePdf') !== -1) {
            viewer.options.formFiller = formFiller_RentalApplicationForm;
            viewer.showFormFiller();
        } else {
            viewer.options.formFiller = {};
        }
    });
    return viewer;
}
";

        public static List<string[]> GetSampleParamsList()
        {
            return new List<string[]>()
            {
                new string[] { "@viewer/Form Filler", "Form filler customized in JS code for a particular PDF form", null },
                new string[] { "@use/Tenant Application", "PDF ビューワのフォームフィラーダイアログを使用して商業用賃貸申込書を記入する", null },
            };
        }
    }
}