既存からの最も簡単なODTファイル生成

かつて、私の前に、私たちの会社のWebサイトからクライアントの契約の生成を実装することがタスクでした。
最初は、タスクがひどく解決されました-HTMLコントラクトテンプレートが準備され、ユーザーにPDF変換テンプレートが与えられました。 もちろん、これにより、契約内の何かを変更する必要がある場合など、多くの不便が生じました。

次の解決策は、odtドキュメントを生成することでした。 これにより、マネージャーはサイトやプログラマーに関係なくドキュメントを編集できました。
完全にゼロから生成することは意味がありません。 既存のファイル(OpenOfficeで編集)を使用して、必要な要素を単純に置き換えてみませんか?

これが私たちがやることです。

しかし、まずは...
UPD! このエラーを修正する2番目の記事はhabrahabr.ru/blogs/php/87254です。
いくつかのコメント:
-テキスト変数のみでなく、ドキュメントのテキスト内のデータのみを変更するようにタスクを強く制限します。
-この問題を解決するために、私は個人的にSimpleXMLZIPArchiveを使用しました 。 他のツールの使用を禁止する人はいません。
-この記事に記載されているのは、既成のツールではなく、簡略化された切り捨てられた例です。

OpenOfficeでテンプレートを作成する:
通常のodtドキュメントを作成し、適切な場所にユーザー変数を挿入します。
挿入メニュー->フィールド->詳細
変数タブ
「ユーザーフィールド」を選択し、画像に示すようにフィールドを追加/貼り付けます。


ファイルを保存します。 私の例では、これはtest.odtファイルです。

ファイルをサーバーにアップロードします。
ODTは、多くの人が知っているODFファイルと同様に、通常のZIPアーカイブです。

upload.php
<?php

//一時アーカイブへのパス
$ tmpfile = 'upload / temp.zip' ;

//受信したドキュメントを保存します
if isset $ _FILES [ 'document' ] およびmove_uploaded_file $ _FILES [ 'document' ] [ 'tmp_name' ] $ tmpfile {

//ディレクトリ関数を削除します
関数 deleteDirectory $ dir {
if file_exists $ dir trueを 返し ます
if is_dir $ dir || is_link $ dir return unlink $ dir ;
foreach scandir $ dir as $ item {
if $ item == '。' || $ item == '..' continue ;
if deleteDirectory $ dir " /"。$ item {
chmod $ dir " /"。$ item 0777 ;
if deleteDirectory $ dir " /"。$ item )は falseを 返し ます
} ;
}
return rmdir $ dir ;
}

//ドキュメントの内容を含むディレクトリを削除し、再度作成します
//必要に応じて、古いバージョンをどこかに移動して、バージョン管理されたドキュメントを作成できます
deleteDirectory 'doc /' ;
mkdir 'doc /' ;


//アーカイブを抽出します
$ zip = 新しい ZipArchive ;
if $ zip- > open $ tmpfile === TRUE {
//ファイルパスを目的の順序で保存します
//これは将来必要になります。
//たとえば、odf形式の要求では、mimetypeファイルはアーカイブの最初にある必要があります。
$ files = array ;
for $ i = 0 ; $ i < $ zip- > numFiles ; $ i ++ {
$ files [ ] = $ zip- > getNameIndex $ i ;
}
file_put_contents "doc.list" implode " \ n " $ files ;

//取得
$ zip- > extractTo 'doc /' ;
$ zip- > close ;
} else {
die "zip error" ;
}

リンク解除 $ tmpfile ;

$ print = 'ファイルが正常にアップロードされました' ;
}
その他 {
$ print = '
<form action = "" method = "post" enctype = "multipart / form-data">
<入力タイプ= "ファイル"名= "ドキュメント"> <br>
<入力タイプ= "送信"値= "ダウンロード"> <br>
</ form> ' ;
}
'<html>を 印刷
<head>
<meta http-equiv = "content-type" content = "text / html; charset = utf-8" />
<title>ドキュメントのダウンロード</ title>
</ head>
<本体>
$ print ''
</ body>
</ html> ' ;

?>


ダウンロードに成功すると、odtファイルの内容を含むフォルダーと、ファイルのリストを含むdoc.listが取得されます。

変更したファイルをユーザーに提供します。
カスタムフィールドの値を置き換え、すべてを圧縮してアーカイブに戻す必要があります。

download.php

<?php

//一時ファイルへのパス
$ tmpfile = 'download / doc.odt' ;
//与えられるファイル
$ outname = 'zayavlenie.odt' ;


//古いファイルを削除します
リンク解除 $ tmpfile ;


//新しいアーカイブを作成します
$ zip = 新しい ZipArchive ;
if $ zip- > open $ tmpfile ZIPARCHIVE :: CREATE === TRUE {
//アーカイブの構造を調べます
$ files = file 'doc.list' ;
foreach $ files as $ filename {
$ filename = trim $ filename ;

//ディレクトリの場合-追加します
if is_dir 'doc /'。$ filename )) {
$ zip- > addEmptyDir $ filename ;
}
//それ以外の場合はファイルを追加します
その他 {

//必要なファイルの場合、その中のユーザーフィールドの置換を実行します
if $ filename == "content.xml" {

//フィールド値
$ vars = array
「名前」 => 「イワノバI.I.」
'日付' =>日付 'dmY'
「惑星」 => 「木星」
;

// simplexmlオブジェクトを作成します
$ xml = new SimpleXMLElement file_get_contents 'doc /'.$filename ;

//事前に名前空間を取得します
$ ns = $ xml- > getNamespaces true ;

// xmlの要素と属性にアクセスするために必要な2つの変数
$ usr = "user-field-decls" ;
$ str = "string-value" ;

//ファイルにユーザーフィールドが含まれているかどうかを確認します
if $ fields = $ xml- > children $ ns [ "office" ] -> body- > text- > children $ ns [ "text" ] -> $ usr {
//もしあれば、それらを調べて、それらの文字列値属性を新しいものに置き換えます
foreach $ fields- > children $ ns [ "text" ] as as $ field {

if isset $ vars [ string $ field- > attributes $ ns [ "text" ] -> name ] {
$ field- > attributes $ ns [ "office" ] -> $ str = $ vars [ string $ field- > attributes $ ns [ "text" ] -> name ] ;
}
}

}
//アーカイブに追加
$ zip- > addFromString $ filename $ xml- > asXML ;
}
その他 {
//ファイルからアーカイブに追加します
$ zip- > addFile 'doc /'。$ filename $ filename ;
}
}
}


$ zip- > close ;
} else {
die "zip error" ;
}

//バッファをクリアしてファイルを発行します
ob_clean ;

header 'Content-Disposition:attachment; filename = "' $ outname '"' ;
header 'Content-type:application / odt' ;
print file_get_contents $ tmpfile ;

?>


出来上がり。

重要な注意事項とリンク:
-この例では、テキストフィールドのみを使用しましたが、他のタイプのフィールドも使用できます。
-ODTでは、条件要素を使用することもできます(たとえば、条件に応じてテキストの一部を表示するかしないか、たとえば、カスタムフィールドの値)
-この例では、content.xmlのフィールドの値のみを変更しました。 ただし、フィールドは他のファイルでも使用できます。たとえば、styles.xmlにはフッターが含まれます。
-ODF仕様 (より正確には、Open Document)
-世界のあらゆるものと同様に、例を最適化できます。 たとえば、content.xmlのみを変更する場合、事前にアーカイブを準備することを禁止するユーザーはいません。また、ユーザーからこのファイルを置換または追加するように求められた場合も同様です。

完全なソースをダウンロードする

Source: https://habr.com/ru/post/J69616/


All Articles