ラベル Word の投稿を表示しています。 すべての投稿を表示
ラベル Word の投稿を表示しています。 すべての投稿を表示

2019年10月24日木曜日

FileMakerからWord(.docx)ファイルへデータ差し込み|使い方

動作検証:
FileMaker Pro 16以降
Windows 10
PowerShell 5.1
Word 2016

プラグイン不要

※元のテンプレートとなるWordファイルを作成時は、Wordが必要ですが、
FileMakerからWord(.docx)ファイルを出力する時は、Word無しでOK。

作成方法は、コチラ
https://qbxxdp.blogspot.com/2019/10/filemakerworddocx.html

使用方法:
テンプレートとなるWordファイルを作成。
{受取人名} の様な {*****} の部分をFileMakerで置換します。
Wordファイル作成時注意事項:
Wordファイルを編集していくと文字列の断片化が発生します。
*.docx 内の document.xml 内で文字列の断片化が発生した場合、以下のようになり、
 {*****} の部分の置換が出来なくなります。
解消方法(1)
XMLファイルを直接編集。後で説明※1

解消方法(2)
Word上で {受取人名} をコピー メモ帳(notepad.exe)に貼り付け > メモ帳 上の  {受取人名} をコピー > Wordに貼り付け。
※XMLの確認方法は後で説明※1

FileMaker
サンプルファイル「FmToDocx.fmp12」
フィールド:Title|任意の名前を設定
フィールド:obj_docx|Wordファイルを挿入

[*.docx内のXMLを取得] ボタンをクリック
*.docx 内の document.xml の内容が取得できます。
文字列の断片化の確認修正が行えます。※1

サンプルファイル「FaxCoverSheet.fmp12」

フィールド:id_FmToDocx|ファイル「FmToDocx.fmp12」の対象レコードを選択
各フィールドに置換する文字列を入力


[Create_docx]ボタンをクリック
Word(.docx)ファイルが作成されます。





FileMakerからWord(.docx)ファイルへデータ差し込み|作り方

動作検証:
FileMaker Pro 16以降
Windows 10
PowerShell 5.1
Word 2016

プラグイン不要

※元のテンプレートとなるWordファイルを作成時は、Wordが必要ですが、
FileMakerからWord(.docx)ファイルを出力する時は、Word無しでOK。

使用方法は、コチラ
https://qbxxdp.blogspot.com/2019/10/filemaker-word-docx.html

作るのが面倒な場合は、完成品をダウンロード(\500)
https://fm-aid.stores.jp/items/5db13868745e6c1bce94c7db

作成:
──────────ファイル「FmToDocx.fmp12」──────────
テーブル:

フィールド:

レイアウト:

 ボタン[*.docx内のXMLを取得] に スクリプト:Get_DocumentXml を割り当て


スクリプト:


Get_DocumentXml
変数を設定 [$docx; 値: FmToDocx::obj_docx]
変数を設定 [$FileName; 値: GetContainerAttribute ($docx ; "filename")]
If [Right ( $FileName ; 5 ) ≠ ".docx"]
カスタムダイアログを表示 ["!"; $FileName & " は、ファイル:*.docx ではありません。"]
現在のスクリプト終了 [テキスト結果: ]
End If
スクリプト実行 [指定:一覧から; 「Expand_docx」; 引数: ]
スクリプト実行 [指定:一覧から; 「Import_DocumentXml」; 引数: ]
現在のスクリプト終了 [テキスト結果:FmToDocx::document_xml]

Create_docx(XML)
変数を設定 [$XML; 値: Get(スクリプト引数)]
If [IsEmpty ( $XML )]
現在のスクリプト終了 [テキスト結果: ]
End If
ブレークポイント スクリプト実行 [指定:一覧から; 「Expand_docx」; 引数: ]
スクリプト一時停止/続行 [間隔(秒): 1]
ブレークポイント スクリプト実行 [指定:一覧から; 「Export_DocumentXml(XML)」; 引数:$XML]
ブレークポイント スクリプト実行 [指定:一覧から; 「Compress_docx」; 引数: ]
ブレークポイント 変数を設定 [$WordDocxPath_win; 値: Get(スクリプトの結果)]
変数を設定 [$WordDocxPath; 値: "/" & Substitute ( $WordDocxPath_win ; "\\" ; "/" )]
スクリプト一時停止/続行 [間隔(秒): 1]
ファイルを挿入 [ターゲット:FmToDocx::gResult_docx; 「$WordDocxPath」]
フィールドへ移動 []
現在のスクリプト終了 [テキスト結果:FmToDocx::document_xml]

Setting_FilePath
変数を設定 [$TempPath; 値: Get ( テンポラリパス )]
変数を設定 [$$WordZipPath; 値: $TempPath & "word.zip"]
変数を設定 [$$TempPath_win; 値: Substitute ( Replace ( $TempPath ; 1 ; 1 ; "" ) ; "/" ; "\\" )]
変数を設定 [$$WordZipPath_win; 値: $$TempPath_win & "word.zip"]
変数を設定 [$$wordArchivePath_win; 値: $$TempPath_win & "word_archive"]
変数を設定 [$$DocumentXmlPath_win; 値: $$TempPath_win & "word_archive\word\document.xml"]

Expand_docx
If [IsEmpty ( $$WordZipPath )]
スクリプト実行 [指定:一覧から; 「Setting_FilePath」; 引数: ]
End If

# ファイル:*.docx を ファイル名:word.zip でテンポラリフォルダへ保存
フィールド内容のエクスポート [FmToDocx::obj_docx; 「$$WordZipPath」; フォルダを作成:オフ]

# *.docx を展開したフォルダ:word_archive を削除
変数を設定 [$Command; 値: Let([ ¢cm=" Remove-Item -Recurse {TargetFolder}; " ]; Substitute ( ¢cm ; ["{TargetFolder}" ; Quote ( $$wordArchivePath_win )] ) )]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
Event を送信 [「aevt」; 「odoc」; $PS]
スクリプト一時停止/続行 [間隔(秒): .5]

# word.zip を展開 -> フォルダ:word_archive
変数を設定 [$Command; 値: Let([ ¢cm=" Expand-Archive -Force -Path {source} -DestinationPath {TargetFolder}; " ]; Substitute ( ¢cm ; ["{source}" ; Quote ( $$WordZipPath_win )] ; ["{TargetFolder}" ; Quote ( $$wordArchivePath_win )] ) )]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
Event を送信 [「aevt」; 「odoc」; $PS]



変数を設定 [$Command; 値: Let([ ¢cm=" Test-Path {Target}|clip; " ]; Substitute ( ¢cm ; ["{Target}" ; Quote ( $$wordArchivePath_win )] ) )]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
フィールド設定 [FmToDocx::gForScriptText; ""]
変数を設定 [$n; 値: 1]
ブレークポイント Loop
Exit Loop If [$n>10]
Exit Loop If [ValueCount ( FilterValues ( Lower ( FmToDocx::gForScriptText ) ; "true" ) )]
Event を送信 [「aevt」; 「odoc」; $PS]
スクリプト一時停止/続行 [間隔(秒): 1]
貼り付け [選択; FmToDocx::gForScriptText]
フィールドへ移動 []
変数を設定 [$n; 値: $n+1]
End Loop
If [$n>10]
カスタムダイアログを表示 ["!ERROR"; Get ( スクリプト名 )]
全スクリプト終了
End If

現在のスクリプト終了 [テキスト結果: ]

Compress_docx
変数を設定 [$WordDocxPath_win; 値: $$TempPath_win & GetContainerAttribute (FmToDocx::obj_docx ; "filename")]


# *.docx を削除
変数を設定 [$Command; 値: Let([ ¢cm=" Remove-Item -Recurse {Target}; " ]; Substitute ( ¢cm ; ["{Target}" ; Quote ( $WordDocxPath_win )] ) )]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
Event を送信 [「aevt」; 「odoc」; $PS]
スクリプト一時停止/続行 [間隔(秒): .5]


# フォルダ:word_archive 圧縮 -> word.zip
# 拡張子変更 word.zip -> *.docx
変数を設定 [$Command; 値: Let([¢cm="Set-Location -Path {source};Compress-Archive -Force -literalPath _rels, docProps, word, '[Content_Types].xml' -Destination {Target};Move-Item -Force {Target} {TargetDocx} ;"];Substitute ( ¢cm ; ["{source}" ; Quote ( $$wordArchivePath_win )]; ["{Target}" ; Quote ( $$WordZipPath_win ) ]; ["{TargetDocx}" ; Quote ( $WordDocxPath_win ) ] ))]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
Event を送信 [「aevt」; 「odoc」; $PS]

変数を設定 [$Command; 値: Let([ ¢cm=" Test-Path {Target}|clip; " ]; Substitute ( ¢cm ; ["{Target}" ; Quote ( $WordDocxPath_win )] ) )]
変数を設定 [$PS; 値: "powershell -WindowStyle Hidden -Command " & Quote ( $Command )]
フィールド設定 [FmToDocx::gForScriptText; ""]
変数を設定 [$n; 値: 1]
Loop
Exit Loop If [$n>10]
Exit Loop If [ValueCount ( FilterValues ( Lower ( FmToDocx::gForScriptText ) ; "true" ) )]
Event を送信 [「aevt」; 「odoc」; $PS]
スクリプト一時停止/続行 [間隔(秒): 1]
貼り付け [選択; FmToDocx::gForScriptText]
フィールドへ移動 []
変数を設定 [$n; 値: $n+1]
End Loop
If [$n>10]
カスタムダイアログを表示 ["!ERROR"; Get ( スクリプト名 )]
全スクリプト終了
End If

現在のスクリプト終了 [テキスト結果:$WordDocxPath_win]

Export_DocumentXml(XML)
変数を設定 [$XML; 値: Get(スクリプト引数)]
If [IsEmpty ( $XML )]
現在のスクリプト終了 [テキスト結果: ]
End If

# $$DocumentXmlPath_win="{TEMP}\word_archive\word\document.xml"
# Windows用にパスをURLエンコード(shift_jis)
スクリプト実行 [指定:一覧から; 「Get_ProperPath(arg)」; 引数:$$DocumentXmlPath_win]
変数を設定 [$DocumentXmlPath_win_enc; 値: Get(スクリプトの結果)]


変数を設定 [$DATA; 値: $XML]
URL から挿入 [選択; ダイアログあり:オフ; $er; "file:///" & $DocumentXmlPath_win_enc; cURL オプション:" -T $DATA "; URL を自動的にエンコードしない]
変数を設定 [$LastError; 値: Get ( 最終エラー )]
フィールドへ移動 []
If [$LastError ≠ 0]
カスタムダイアログを表示 ["!ERROR"; "ERROR CODE: " & $LastError]
End If

Import_DocumentXml
# $$DocumentXmlPath_win="{TEMP}\word_archive\word\document.xml"
# Windows用にパスをURLエンコード(shift_jis)
スクリプト実行 [指定:一覧から; 「Get_ProperPath(arg)」; 引数:$$DocumentXmlPath_win]
変数を設定 [$DocumentXmlPath_win_enc; 値: Get(スクリプトの結果)]

フィールド設定 [FmToDocx::document_xml; ""]
変数を設定 [$n; 値: 1]
Loop
Exit Loop If [$n>20]
スクリプト一時停止/続行 [間隔(秒): 1]
エラー処理 [オン]
ブレークポイント URL から挿入 [選択; ダイアログあり:オフ; ターゲット:FmToDocx::document_xml; "file:///" & $DocumentXmlPath_win_enc; URL を自動的にエンコードしない]
変数を設定 [$LastError; 値: Get ( 最終エラー )]
Exit Loop If [ not IsEmpty ( FmToDocx::document_xml )]
変数を設定 [$n; 値: $n+1]
End Loop
フィールドへ移動 []
If [$LastError ≠ 0]
カスタムダイアログを表示 ["!ERROR"; "ERROR CODE: " & $LastError]
End If

Get_ProperPath(arg)
# Windows、全角やスペースw含むパスの対応
変数を設定 [$FilePath; 値: Get(スクリプト引数)]
変数を設定 [$FilePath; 値: Substitute ( $FilePath ; ["file:////" ; "file:///"] )]
If [Get (システムプラットフォーム) = -2 /*Windows*/]
# 全角有無チェック
変数を設定 [$ByteCount; 値: Length($FilePath & Filter ( $FilePath ; RomanZenkaku ( KanaZenkaku ( $FilePath ) )))]
変数を設定 [$Length; 値: Length ( $FilePath )]
If [$ByteCount = $Length]
変数を設定 [$Path; 値: $FilePath]
Else
# Windowsの場合は、SHIFT-JIS URLエンコード
変数を設定 [$Path; 値: Let([ $Val = $FilePath ; $MAX = Length ( $Val ) ; $n = 1 ; $fnc0= "Case ( $n > $MAX ; $Result ; Let( [ $text=Middle ( $Val ; $n ; 1 ); $Result = $Result & if(Code ( $text )>127; Let([ $txt = HexEncode ( TextEncode ( $text ; \"shift_jis\" ; 1 ) ) ; $pos = Length ( $txt ) - 1 ; $fnc= \"Case ( $pos < 0 ; $txt ; Let( [ $txt = Replace ( $txt ; $pos ; 0 ; \\\"%\\\" ) ; $pos = $pos - 2 ]; Evaluate($fnc)) )\"]; Evaluate($fnc)) ; Middle ( $Val ; $n ; 1 ) ); $n = $n + 1 ]; Evaluate($fnc0)) )"]; Evaluate($fnc0))]
End If

Else
変数を設定 [$Path; 値: $FilePath]
End If
現在のスクリプト終了 [テキスト結果:$Path]

OnLastWindowClose
変数を設定 [$FieldFullName_s; 値: /*ファイル内のすべてのグローバルフィールドの完全名称と繰り返し数を取得。*/Let ([ $query ="select '$FieldFullName='||'\"'||TableName ||'::'||FieldName||'\"','$FieldReps='||'\"'||FieldReps||'\"'FROM FILEMAKER_FIELDSWHERE FieldClass = 'Normal'ANDFieldType LIKE 'global%'"];ExecuteSQL ( $query ; ";" ; "" ))]
変数を設定 [$MAX; 値: ValueCount ( $FieldFullName_s )]
変数を設定 [$n; 値: 1]
Loop
Exit Loop If [$n>$MAX]
変数を設定 [$Val; 値: GetValue ( $FieldFullName_s ; $n )]
変数を設定 [$er; 値: Evaluate ( "Let ( [" & $val & "] ; 0 )" )]
変数を設定 [$k; 値: 1]
Loop
Exit Loop If [$k>$FieldReps]
フィールドを名前で設定 [$FieldFullName & "[" & $k & "]"; ""]
変数を設定 [$k; 値: $k+1]
End Loop
変数を設定 [$n; 値: $n+1]
End Loop


──────────ファイル「FmToDocx.fmp12」──────────
テーブル:

フィールド:

レイアウト:

スクリプト:

Create_docx
変数を設定 [$XML; 値: Substitute ( FmToDocx::document_xml
; ["{受取人名}" ; EscXml ( FaxCoverSheet::受取人名 )]
; ["{受取人FAX}" ; EscXml ( FaxCoverSheet::受取人FAX )]
; ["{受取人の電話番号}" ; EscXml ( FaxCoverSheet::受取人の電話番号 )]
; ["{差出人名}" ; EscXml ( FaxCoverSheet::差出人名 )]
; ["{差出人FAX}" ; EscXml ( FaxCoverSheet::差出人FAX )]
; ["{差出人の電話番号}" ; EscXml ( FaxCoverSheet::差出人の電話番号 )]
; ["{件名}" ; EscXml ( FaxCoverSheet::件名 )]
; ["{日付}" ; EscXml ( FaxCoverSheet::日付 )]
; ["{コメント}" ; EscXml ( FaxCoverSheet::コメント ) ]
)]
関連レコードへ移動 [関連レコードのみを表示; テーブル: 「FmToDocx」; 外部; 使用するレイアウト: 「FmToDocx」]
スクリプト実行 [指定:一覧から; 「Create_docx(XML)」 , ファイル: 「FmToDocx」; 引数:$XML]
ウインドウを選択 [現在のウインドウ]
フィールド内容のエクスポート [FmToDocx::gResult_docx; フォルダを作成:オフ]

カスタム関数:

EscXml(XML)
Substitute ( XML
; ["&";"&amp;"]
; ["\"";"&quot;"]
; ["'";"&apos;"]
; ["<";"&lt;"]
; [">";"&gt;"]
; [¶ ; ""]
)
※上記の &amp;  &quot; &apos; &lt;  &gt; の & が全角になっています。FileMakerに記述する際は、半角に変更して下さい。