├── .buildpath
├── .gitattributes
├── .gitignore
├── Autoloader.php
├── LICENSE.txt
├── README-CN.md
├── README.md
├── composer.json
├── src
├── Api
│ ├── Base.php
│ ├── GetWord.php
│ └── ParseWord.php
├── Common
│ ├── Bind.php
│ ├── Build.php
│ ├── Common.php
│ ├── Log.php
│ ├── PartBase.php
│ └── Tree.php
├── Config
│ └── Main.php
├── Edit
│ └── Part
│ │ ├── Charts.php
│ │ ├── Comments.php
│ │ ├── Document.php
│ │ ├── Excel.php
│ │ ├── Footer.php
│ │ ├── Header.php
│ │ ├── Numbering.php
│ │ ├── Rels.php
│ │ └── Styles.php
├── Read
│ ├── Part
│ │ └── ContentTypes.php
│ └── Word.php
├── WordProcessor.php
└── XmlTemple
│ ├── XmlFromPhpword.php
│ └── image.xml
└── tests
├── Trace.php
├── function.php
└── samples
├── api
├── index.php
├── r-temple.docx
├── simple data.csv
└── temple.docx
├── block
├── index.php
├── r-temple.docx
└── temple.docx
├── break page
├── index.php
└── temple.docx
├── clone
├── index.php
├── r-temple.docx
└── temple.docx
├── clonetable
├── img.png
├── index.php
├── r-temple.docx
└── temple.docx
├── image
├── full.png
├── img.jpg
├── img2.bmp
├── img3.png
├── index.php
├── r-temple.docx
├── temple.docx
├── temple
│ └── full.xml
└── words.png
├── memory use
├── index.php
├── logo.jpg
├── word config.png
├── word result.png
└── word.gif
├── merge table cells
├── index.php
├── r-temple.docx
└── temple.docx
├── performance
├── 1750pages.docx
├── index.php
├── r-1750pages.docx
├── r-temple.docx
└── temple.docx
├── phpword
├── index.php
├── logo.jpg
├── r-temple.docx
└── temple.docx
├── remain comments
├── index.php
├── r-temple.docx
└── temple.docx
├── simple for readme
├── img.png
├── index.php
├── logo.jpg
├── r-temple.docx
├── temple.docx
├── word config.png
├── word result.png
└── word.gif
├── text
├── index.php
├── r-temple.docx
└── temple.docx
├── toc
├── index.php
├── r-temple.docx
└── temple.docx
└── wps
├── index.php
├── r-word-temple.docx
├── r-wps-temple.docx
├── word-temple.docx
└── wps-temple.docx
/.buildpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | tests/tools/ export-ignore
4 | tests/Output/ export-ignore
5 | tests/Log/ export-ignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings/*
2 | .project
3 | *.prefs
4 | temp-*
5 |
6 | tests/Output/*
7 | tests/Log/*.log
8 |
9 |
--------------------------------------------------------------------------------
/Autoloader.php:
--------------------------------------------------------------------------------
1 | load($template);
39 |
40 | //赋值
41 | $TemplateProcessor->setValue('value', 'r-value');
42 |
43 | //克隆并复制
44 | $TemplateProcessor->clones('people', 3);
45 |
46 | $TemplateProcessor->setValue('name#0', 'colin0');
47 | $TemplateProcessor->setValue('name#1', [
48 | ['text'=>'colin1','style'=>'style','type'=>MDWORD_TEXT],
49 | ['text'=>1,'type'=>MDWORD_BREAK],
50 | ['text'=>'86','style'=>'style','type'=>MDWORD_TEXT]
51 | ]);
52 | $TemplateProcessor->setValue('name#2', 'colin2');
53 |
54 | $TemplateProcessor->setValue('sex#1', 'woman');
55 |
56 | $TemplateProcessor->setValue('age#0', '280');
57 | $TemplateProcessor->setValue('age#1', '281');
58 | $TemplateProcessor->setValue('age#2', '282');
59 |
60 | //图片复制
61 | $TemplateProcessor->setImageValue('image', dirname(__FILE__).'/logo.jpg');
62 |
63 | //删除某行
64 | $TemplateProcessor->deleteP('style');
65 |
66 | //保存
67 | $rtemplate = __DIR__.'/r-temple.docx';
68 | $TemplateProcessor->saveAs($rtemplate);
69 | ```
70 |
71 | + ### 结果
72 | 
73 |
74 | + ### 动图
75 | 
76 |
77 | ## 性能情况([统计脚本](https://github.com/mkdreams/MDword/blob/main/tests/samples/performance/index.php))
78 | | 测试项 | 用时(S) |
79 | | ---- | ---- |
80 | | 1页母版赋值100次 | 0.04 |
81 | | 1页母版赋值500次 | 0.16 |
82 | | 1页母版赋值1000次 | 0.33 |
83 | | 1页母版赋值10000次 | 7.80 |
84 | | 1750页母版赋值100次 | 4.61 |
85 | | 1750页母版赋值500次 | 4.94 |
86 | | 1750页母版赋值1000次 | 5.43 |
87 | | 1750页母版赋值10000次 | 17.39 |
88 |
89 | ## 内存使用情况([统计脚本](https://github.com/mkdreams/MDword/blob/main/tests/samples/memory%20use/index.php))
90 | | 连续运行第几次 | 累积内存使用情况 | 备注 |
91 | | ---- | ---- | ---- |
92 | | 1 | 0.050590515136719 M | 首次需要加载PHP类 |
93 | | 2 | 0.050949096679688 M | |
94 | | 3 | 0.050949096679688 M | |
95 | | 4 | 0.050949096679688 M | |
96 | | 5 | 0.050949096679688 M | |
97 | | 6 | 0.050949096679688 M | |
98 | | 7 | 0.050949096679688 M | |
99 | | 8 | 0.050949096679688 M | |
100 |
101 |
102 | ## 更多案例
103 | - [简单的综合案例](https://github.com/mkdreams/MDword/tree/main/tests/samples/simple%20for%20readme)
104 | - [带式样的文字](https://github.com/mkdreams/MDword/tree/main/tests/samples/text)
105 | - [添加图片](https://github.com/mkdreams/MDword/tree/main/tests/samples/image)
106 | - [克隆](https://github.com/mkdreams/MDword/tree/main/tests/samples/clone)
107 | - [多种方式设置区块,解决无法添加批注问题](https://github.com/mkdreams/MDword/tree/main/tests/samples/block)
108 | - [PHPWORD写入到区块](https://github.com/mkdreams/MDword/tree/main/tests/samples/phpword)
109 | - [目录嵌入到表格](https://github.com/mkdreams/MDword/tree/main/tests/samples/toc)
110 | - [合并表格单元格](https://github.com/mkdreams/MDword/tree/main/tests/samples/merge%20table%20cells)
111 |
112 |
113 |
114 | ## 交流
115 | ### 请备注:MDword交流
116 | 
117 |
118 |
119 | ## [项目计划](https://github.com/mkdreams/MDword/projects/1#column-10318470)
120 |
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MDword
2 | ## [中文文档](https://github.com/mkdreams/MDword/blob/main/README-CN.md)
3 |
4 | ## Project General Name
5 | Template: a word which will be revised.
6 | Block: the part which will be replaced or cloned.
7 |
8 | ## Project Introduction
9 | Main Use: to generate word dynamically.
10 | Advantage: focus only on dynamic data and logic, rather than adjusting the style, which can modulate the template with the help of office word.
11 |
12 | ## Comparison between MDword & PHPword
13 | + ### Similarities
14 | 1. PHP Package.
15 | 2. Both can be used to generate office word.
16 |
17 | + ### Differences
18 | 1. PHPword concentrates on writing elements one by one. However,
19 | it is more powerful and efficient for Mdword just to revise them on the template.
20 | 2. For updating text styles, adding covers, headers and footers, MDword just modifies the template by office word, while PHPword complicate the task-adjusting every element.
21 | 3. Directories(Table of content) can be automatically generated.
22 |
23 | ## Tutotials
24 | + ### Installation
25 | ```
26 | // Method 1
27 | composer require mkdreams/mdword
28 | // Method 2, Autoloading Template
29 | require_once('Autoloader.php');
30 | ```
31 |
32 | + ### Add annotations or use “${value/}” to the template. Please note that there is a “/” at the end.
33 | 
34 | + ### Invocation Methods (more and richer approaches, for example: [tests\samples\simple for readme](https://github.com/mkdreams/MDword/blob/main/tests/samples/simple%20for%20readme/index.php), such as catalog, sequence number, etc.)
35 | ```
36 | // New class,load template
37 | $TemplateProcessor = new WordProcessor();
38 | $template = 'temple.docx';
39 | $TemplateProcessor->load($template);
40 |
41 | // Set Value
42 | $TemplateProcessor->setValue('value', 'r-value');
43 |
44 | // Clone
45 | $TemplateProcessor->clones('people', 3);
46 |
47 | $TemplateProcessor->setValue('name#0', 'colin0');
48 | $TemplateProcessor->setValue('name#1', [
49 | ['text'=>'colin1','style'=>'style','type'=>MDWORD_TEXT],
50 | ['text'=>1,'type'=>MDWORD_BREAK],
51 | ['text'=>'86','style'=>'style','type'=>MDWORD_TEXT]
52 | ]);
53 | $TemplateProcessor->setValue('name#2', 'colin2');
54 |
55 | $TemplateProcessor->setValue('sex#1', 'woman');
56 |
57 | $TemplateProcessor->setValue('age#0', '280');
58 | $TemplateProcessor->setValue('age#1', '281');
59 | $TemplateProcessor->setValue('age#2', '282');
60 |
61 | // set value for image
62 | $TemplateProcessor->setImageValue('image', dirname(__FILE__).'/logo.jpg');
63 |
64 | // Delete a paragraph
65 | $TemplateProcessor->deleteP('style');
66 |
67 | // Save
68 | $rtemplate = __DIR__.'/r-temple.docx';
69 | $TemplateProcessor->saveAs($rtemplate);
70 | ```
71 |
72 | + ### Result
73 | 
74 |
75 | + ### GIFs
76 | 
77 |
78 |
79 | ## More samples
80 | - [Simple Comprehhensive cases](https://github.com/mkdreams/MDword/tree/main/tests/samples/simple%20for%20readme)
81 |
82 | - [Formatted texts](https://github.com/mkdreams/MDword/tree/main/tests/samples/text)
83 |
84 | - [Add images](https://github.com/mkdreams/MDword/tree/main/tests/samples/image)
85 |
86 | - [Clone](https://github.com/mkdreams/MDword/tree/main/tests/samples/clone)
87 |
88 | - [Many ways to set blocks, solving the lack of notes](https://github.com/mkdreams/MDword/tree/main/tests/samples/block)
89 |
90 | - [Write element that is written by PHPword to the block](https://github.com/mkdreams/MDword/tree/main/tests/samples/phpword)
91 |
92 | - [Put TOC in a table](https://github.com/mkdreams/MDword/tree/main/tests/samples/toc)
93 | - [Merge table cells](https://github.com/mkdreams/MDword/tree/main/tests/samples/merge%20table%20cells)
94 |
95 | ## Communication
96 | ### Note: Exchange idea on MDword.
97 | 
98 |
99 | ## [Project Plans](https://github.com/mkdreams/MDword/projects/1#column-10318470)
100 |
101 |
102 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mkdreams/mdword",
3 | "description": "OFFICE WORD 动态数据 绑定数据 生成报告;OFFICE WORD Dynamic data binding data generation report.",
4 | "license": "Apache-2.0",
5 | "authors": [
6 | {
7 | "name": "colin",
8 | "email": "chenlincolin@gmail.com"
9 | }
10 | ],
11 | "require": {
12 | "php": ">=5.6.0"
13 | },
14 | "require-dev": {
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "MDword\\": "src"
19 | }
20 | },
21 | "minimum-stability": "dev"
22 | }
23 |
--------------------------------------------------------------------------------
/src/Api/Base.php:
--------------------------------------------------------------------------------
1 | common = new Common();
14 |
15 | $this->parseParameters();
16 | $this->wordProcessor = new WordProcessor();
17 | $this->wordProcessor->load($this->parameters['docUrl']);
18 | }
19 |
20 | private function parseParameters() {
21 | $this->parameters = [
22 | 'docUrl'=>MDWORD_TEST_DIRECTORY.'/samples/api/temple.docx',
23 | 'datas'=>[
24 | 'data1'=>[
25 | 'type'=>'json',
26 | 'dataInfo'=>[
27 | 'type'=>'local',
28 | 'data'=>json_encode([
29 | ['price'=>100,'change'=>5,'changepercent'=>0.05],
30 | ['price'=>200,'change'=>-10,'changepercent'=>-0.05],
31 | ['price'=>500,'change'=>100,'changepercent'=>0.20],
32 | ])
33 | ],
34 | ],
35 | 'data2'=>[
36 | 'type'=>'json',
37 | 'dataInfo'=>[
38 | 'type'=>'http',
39 | 'url'=>'http://restapi2.farseerbi.com/bds/ipoNews?accessToken=3eZF3JrHUtbd5kXc1CVVrLAQf7XUJVWs',
40 | 'headers'=>'',
41 | 'postData'=>['rows'=>10,'sLanguage'=>'TC','page'=>1],
42 | ],
43 | ],
44 | 'data3'=>[
45 | 'type'=>'csv',
46 | 'dataInfo'=>[
47 | 'type'=>'http',
48 | 'url'=>MDWORD_TEST_DIRECTORY.'/samples/api/simple data.csv',
49 | 'headers'=>'',
50 | 'postData'=>'',
51 | ],
52 | ],
53 | 'data4'=>[
54 | 'type'=>'xml'
55 | ],
56 | ],
57 | 'binds'=>[
58 | 'item'=>[
59 | 'dataKeyName'=>'data1',
60 | 'keyList'=>[],
61 | 'childrens'=>[
62 | 'stockprice'=>[
63 | 'keyList'=>['price'],
64 | ],
65 | 'change'=>[
66 | 'keyList'=>['change'],
67 | ],
68 | 'changepercent'=>[
69 | 'keyList'=>['changepercent'],
70 | ],
71 | ]
72 | ],
73 | 'rows'=>[
74 | 'dataKeyName'=>'data2',
75 | 'keyList'=>['rows'],
76 | 'childrens'=>[
77 | 'title'=>[
78 | 'keyList'=>['newsSubj'],
79 | ],
80 | 'company name'=>[
81 | 'keyList'=>['stockName'],
82 | ],
83 | 'date'=>[
84 | 'keyList'=>['newsDate'],
85 | ],
86 | ]
87 | ],
88 | 'rows2'=>[
89 | 'dataKeyName'=>'data3',
90 | 'keyList'=>[],
91 | 'childrens'=>[
92 | 'type2'=>[
93 | 'keyList'=>[0],
94 | ],
95 | 'title2'=>[
96 | 'keyList'=>[1],
97 | ],
98 | 'influence2'=>[
99 | 'keyList'=>[6],
100 | ],
101 | 'date2'=>[
102 | 'keyList'=>[3],
103 | ],
104 | ]
105 | ],
106 | 'two'=>2,
107 | 'box'=>'BOX',
108 | 'header'=>'MDWORD-HEADER',
109 | 'footer'=>'MDWORD-FOOTER',
110 | ]
111 | ];
112 | }
113 |
114 | protected function error($text) {
115 | header('Content-Type:application/json; charset=utf-8');
116 | $data = ['data'=>null,'messages'=>['message'=>$text,'type'=>'json'],'success'=>false];
117 | exit(json_encode($data));
118 | }
119 |
120 | protected function success($values,$text='') {
121 | header('Content-Type:application/json; charset=utf-8');
122 | $data = ['data'=>$values,'messages'=>['message'=>$text,'type'=>'json'],'success'=>true];
123 | exit(json_encode($data));
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/Api/GetWord.php:
--------------------------------------------------------------------------------
1 | parameters['binds'];
8 |
9 | $this->bind($binds);
10 | return $this->wordProcessor->saveAsContent();
11 | }
12 |
13 | private function bind($binds,$pbind = null,$pBindName = null, $first = true) {
14 | foreach($binds as $blockName => $value) {
15 | if($first) {
16 | $pbind = null;
17 | }
18 |
19 | if(is_array($value)) {
20 | if(isset($value['dataKeyName'])) {
21 | /**
22 | * @var Bind $pbind
23 | */
24 | $pbind = $this->wordProcessor->getBind($this->getData($value['dataKeyName']));
25 | }
26 |
27 | if(is_null($pbind)) {
28 | $this->wordProcessor->setValue($blockName, $value);
29 | }else{
30 | $pbind->bindValue($blockName, $value['keyList'], $pBindName);
31 | if(isset($value['childrens'])) {
32 | $this->bind($value['childrens'],$pbind,$blockName,false);
33 | }
34 | }
35 | }else{
36 | $this->wordProcessor->setValue($blockName, $value);
37 | }
38 | }
39 | }
40 |
41 | private function getData($name) {
42 | static $datas = [];
43 | if(isset($datas[$name])) {
44 | return $datas[$name];
45 | }
46 |
47 | if(isset($this->parameters['datas'][$name])) {
48 | return $this->parseData($this->parameters['datas'][$name]);
49 | }
50 |
51 | return null;
52 | }
53 |
54 |
55 | private function parseData($data) {
56 | $dataInfo = $data['dataInfo'];
57 | switch ($dataInfo['type']) {
58 | case 'local':
59 | return $this->formatConvert($dataInfo['data'],$data['type']);
60 | break;
61 | case 'http':
62 | if($dataInfo['headers'] === '' && $dataInfo['postData'] === '') {
63 | $content = file_get_contents($dataInfo['url']);
64 | }else{
65 | $content = $this->common->curlSend($dataInfo['url'],$dataInfo['headers'],$dataInfo['postData']);
66 | }
67 | return $this->formatConvert($content,$data['type']);
68 | break;
69 | case 'excel':
70 | $data = $this->common->curlSend($data['url'],$data['headers'],$data['postData']);
71 | $data = json_decode($data,true);
72 | return $data;
73 | break;
74 | }
75 | }
76 |
77 | private function formatConvert($content,$orgFormat='json') {
78 | switch ($orgFormat) {
79 | case 'json':
80 | return json_decode($content,true);
81 | break;
82 | case 'csv':
83 | return $this->parseCsv($content);
84 | break;
85 | }
86 | }
87 |
88 | private function parseCsv($content) {
89 | $rows = explode("\r", $content);
90 | foreach($rows as $index => $row) {
91 | $row = trim($row);
92 | if($row === '') {
93 | continue;
94 | }
95 | $rows[$index] = explode(',', $row);
96 | }
97 |
98 | $data = [];
99 | foreach($rows as $index => $row) {
100 | if($index === 0) {
101 | continue;
102 | }
103 |
104 | foreach ($row as $key => $value) {
105 | $data[$index][$key] = $value;
106 | }
107 | }
108 |
109 | return $data;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Api/ParseWord.php:
--------------------------------------------------------------------------------
1 | wordProcessor->getBlockList();
7 | $this->success($blocks);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Common/Bind.php:
--------------------------------------------------------------------------------
1 | wordProcessor = $wordProcessor;
20 | $this->data = $data;
21 | $this->pre = $pre;
22 | }
23 |
24 | public function bindValue($name,$keyList,$pBindName=null,$callbackOrValueType=null,$emptyCallBack=null) {
25 | //loop
26 | if(!is_null($pBindName) && isset(self::$binds[$pBindName])) {
27 | foreach(self::$binds[$pBindName] as $bind) {
28 | $bind->bindValue($name,$keyList,null,$callbackOrValueType,$emptyCallBack);
29 | }
30 |
31 | return $this;
32 | }
33 |
34 | if('INNER_VARS' !== $pBindName) {
35 | $data = $this->data;
36 | }else{
37 | $data = $this->wordProcessor->getInnerVars();
38 | }
39 | foreach($keyList as $key) {
40 | $data = $data[$key];
41 | }
42 |
43 | if(is_array($data)) {
44 | $count = count($data);
45 | $this->wordProcessor->clones($name.$this->pre,$count);
46 | $i = 0;
47 | foreach($data as $subData) {
48 | if(!isset(self::$binds[$name])) {
49 | self::$binds[$name] = [];
50 | }
51 | self::$binds[$name][] = new Bind($this->wordProcessor, $subData, $this->pre.'#'.$i++);
52 | }
53 |
54 | if($count === 0 && !is_null($emptyCallBack)) {
55 | $this->wordProcessor->cloneTo($name.$this->pre,$emptyCallBack($data,$this->data),$count);
56 | }
57 | }else{
58 | $type = MDWORD_TEXT;
59 | if(!is_null($callbackOrValueType)) {
60 | if(is_callable($callbackOrValueType)) {
61 | $data = $callbackOrValueType($data,$this->data,$this->pre);
62 | if(isset($data['overrideType'])) {
63 | $type = $data['overrideType'];
64 | $data = $data['overrideValue'];
65 | }
66 | }elseif(is_int($callbackOrValueType)) {
67 | $type = $callbackOrValueType;
68 | }
69 | }
70 | $this->wordProcessor->setValue($name.$this->pre,$data,$type);
71 | }
72 |
73 | return $this;
74 | }
75 | }
--------------------------------------------------------------------------------
/src/Common/Build.php:
--------------------------------------------------------------------------------
1 | log = new Log();
9 | }
10 |
11 | public function getDirFiles($dir,$callback=null) {
12 | if(!is_dir($dir)) {
13 | $this->log->writeLog('dir is not a dir! dir:'.$dir);
14 | return [];
15 | }
16 | $filesTemp = scandir($dir);
17 | $files = [];
18 | foreach ($filesTemp as $fileName){
19 | if($fileName != '.' && $fileName != '..'){
20 | if(!is_null($callback)) {
21 | $fileName = $callback($dir,$fileName);
22 | }
23 | $files[] = $fileName;
24 | }
25 | }
26 |
27 |
28 | return $files;
29 | }
30 |
31 | public function curlSend($url,$headers='',$post=[],$timeoutMs=30000) {
32 | $ch = curl_init();
33 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//https
34 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);//https
35 | curl_setopt($ch, CURLOPT_URL, $url);
36 | curl_setopt($ch, CURLOPT_HEADER, 0);
37 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
38 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
39 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, $timeoutMs);
40 |
41 | if(!empty($post)) {
42 | curl_setopt($ch, CURLOPT_POST, 1);//post方式提交
43 | if(is_array($post))
44 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));//要提交的信息
45 | else
46 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post);//要提交的信息
47 |
48 | }
49 |
50 | $rs = curl_exec($ch); //执行cURL抓取页面内容
51 | curl_close($ch);
52 |
53 |
54 | return $rs;
55 | }
56 | }
--------------------------------------------------------------------------------
/src/Common/Log.php:
--------------------------------------------------------------------------------
1 | logPath = dirname(dirname(dirname(__FILE__))).'/tests/Log/'.date('Ymd').'.log';
9 | }
10 |
11 | public function writeLog($content) {
12 | if(!MDWORD_DEBUG) {
13 | return ;
14 | }
15 |
16 | $result = file_put_contents($this->logPath, date('H:i').' '.$content."\r\n", FILE_APPEND);
17 | if($result === false) {
18 | die('write file error! file:'.$this->logPath);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Common/PartBase.php:
--------------------------------------------------------------------------------
1 | rootPath = dirname(__DIR__);
40 |
41 | $this->word = $word;
42 | }
43 |
44 | public function initNameSpaces() {
45 | $context = $this->DOMDocument->documentElement;
46 | $xpath = new \DOMXPath($this->DOMDocument);
47 | foreach( $xpath->query('namespace::*', $context) as $node ) {
48 | $this->xmlns[$node->localName] = $node->nodeValue;
49 | }
50 | }
51 |
52 | public function getExist($item,$name) {
53 | $itemTemps = $item->getElementsByTagName($name);
54 |
55 | if($itemTemps->length === 0) {
56 | return false;
57 | }else{
58 | return true;
59 | }
60 | }
61 |
62 | public function getVal($item,$name,$ns='w') {
63 | $itemTemps = $item->getElementsByTagName($name);
64 |
65 | if($itemTemps->length === 0) {
66 | return null;
67 | }elseif($itemTemps->length === 1) {
68 | return $this->getAttr($itemTemps->item(0),'val',$ns);
69 | }else{
70 | //todo
71 | }
72 | }
73 |
74 | public function getAttr($item,$name,$ns='w') {
75 | if(isset($this->xmlns[$ns])) {
76 | return $item->getAttributeNS($this->xmlns[$ns],$name);
77 | }else{
78 | return $item->getAttribute($name);
79 | }
80 | }
81 |
82 | public function setAttr($item,$name,$value,$ns='w') {
83 | if(isset($this->xmlns[$ns])) {
84 | return $item->setAttributeNS($this->xmlns[$ns],$name,$value);
85 | }else{
86 | return $item->setAttribute($name,$value);
87 | }
88 | }
89 |
90 | public function createElementNS($item,$name,$value,$ns='w') {
91 | if(isset($this->xmlns[$ns])) {
92 | return $this->DOMDocument->createElementNS($this->xmlns[$ns],$name,$value);
93 | }else{
94 | return $this->DOMDocument->createElement($name,$value);
95 | }
96 | }
97 |
98 | public function hasAttr($item,$name,$ns='w') {
99 | return $item->hasAttributeNS($this->xmlns[$ns],$name);
100 | }
101 |
102 | public function removeAttr($item,$name) {
103 | $item->removeAttribute($name);
104 | }
105 |
106 |
107 | public function __get($name) {
108 | return $this->$name;
109 | }
110 |
111 | public function markDelete($item) {
112 | if(!is_null($item)) {
113 | $item->setAttribute('md',(++$this->id));
114 | }
115 | }
116 |
117 | protected function removeMarkDelete($item) {
118 | if(!is_null($item)) {
119 | $item->removeAttribute('md');
120 | }
121 | }
122 |
123 | public function deleteMarked() {
124 | $this->deleteByXpath('//*[@md]');
125 | }
126 |
127 |
128 | public function deleteByXpath($xpath) {
129 | $DOMXPath = new \DOMXPath($this->DOMDocument);
130 | $context = $this->DOMDocument->documentElement;
131 | $DOMXPath->registerNamespace('w', $this->xmlns['w']);
132 | $nodes = $DOMXPath->query($xpath, $context);
133 | foreach( $nodes as $node ) {
134 | $node->parentNode->removeChild($node);
135 | }
136 | }
137 |
138 | public function insertAfter($copy,$targetNode) {
139 | if($nextSibling = $targetNode->nextSibling) {
140 | if($parentNode = $nextSibling->parentNode) {
141 | $parentNode->insertBefore($copy,$nextSibling);
142 | }
143 | }else{
144 | if($parentNode = $targetNode->parentNode) {
145 | $parentNode->appendChild($copy);
146 | }
147 | }
148 | }
149 |
150 | public function appendChild($parentNode,$targetNode) {
151 | $parentNode->appendChild($targetNode);
152 | }
153 |
154 | public function insertBefore($copy,$targetNode) {
155 | if($parentNode = $targetNode->parentNode) {
156 | $parentNode->insertBefore($copy,$targetNode);
157 | }
158 | }
159 |
160 | public function removeChild($item) {
161 | $parentNode = $item->parentNode;
162 | $parentNode->removeChild($item);
163 | }
164 |
165 | function pathRelToAbs($RelUrl, $PrefixUrl = '', $SuffixUrl = '')
166 | {
167 | $RelUrlRep = str_replace('\\', '/', $RelUrl);
168 |
169 | $UrlArr = explode('/', $RelUrlRep);
170 |
171 | $NewUrlArr = array();
172 |
173 | foreach ($UrlArr as $value) {
174 |
175 | if ($value == '..' && ! empty($NewUrlArr)) {
176 |
177 | array_pop($NewUrlArr);
178 | } else if ($value != '..' && $value != '.' && $value != '') {
179 |
180 | $NewUrlArr[] = $value;
181 | }
182 | }
183 |
184 | $UrlStr = ! empty($NewUrlArr) ? implode('/', $NewUrlArr) : '/';
185 |
186 | return $PrefixUrl . $UrlStr . $SuffixUrl;
187 | }
188 |
189 | public static function getTempDir()
190 | {
191 | $tempDir = sys_get_temp_dir();
192 |
193 | if (!empty(self::$tempDir)) {
194 | $tempDir = self::$tempDir;
195 | }
196 |
197 | return $tempDir;
198 | }
199 |
200 | protected function parserRange($range) {
201 | $rangeArr = preg_split('/\!|\:/', $range);
202 | $count = count($rangeArr);
203 | $rangInfo = [];
204 | $match = [];
205 | if($count == 2) {
206 | $rangInfo[0] = $rangeArr[0];
207 | preg_match('/([a-z]+?)[\$]*?([0-9]+?)/i', $rangeArr[1],$match);
208 | $rangInfo[1] = [];
209 | $rangInfo[2] = [];
210 | $rangInfo[1][0] = $match[1];
211 | $rangInfo[2][0] = $match[2];
212 | }elseif($count == 3) {
213 | $rangInfo[0] = $rangeArr[0];
214 | preg_match('/([a-z]+?)[\$]*?([0-9]+?)/i', $rangeArr[1],$match);
215 | $rangInfo[1] = [];
216 | $rangInfo[2] = [];
217 | $rangInfo[1][0] = $match[1];
218 | $rangInfo[2][0] = $match[2];
219 |
220 | preg_match('/([a-z]+?)[\$]*?([0-9]+?)/i', $rangeArr[2],$match);
221 | $rangInfo[1][1] = $match[1];
222 | $rangInfo[2][1] = $match[2];
223 | }else{
224 | var_dump('parserRange 数量不对!');
225 | }
226 |
227 | $rangInfo[3] = $range;
228 | return $rangInfo;
229 | }
230 |
231 | protected function initRels($xmlType=19) {
232 | $partInfo = pathinfo($this->partName);
233 | $partNameRel = $partInfo['dirname'].'/_rels/'.$partInfo['basename'].'.rels';
234 | $this->rels = new Rels($this->word, $this->word->getXmlDom($partNameRel));
235 | $this->rels->partName = $partNameRel;
236 | $this->rels->partInfo = $partInfo;
237 | $this->word->parts[$xmlType][] = ['PartName'=>$this->rels->partName,'DOMElement'=>$this->rels->DOMDocument];
238 | }
239 |
240 | public function createNodeByXml($xmlname,$callback=null)
241 | {
242 | $filename = MDWORD_SRC_DIRECTORY.'/XmlTemple/'.$xmlname.'.xml';
243 | if(is_file($filename)) {
244 | $xml = file_get_contents($filename);
245 | }else{
246 | $xml = $xmlname;
247 | }
248 |
249 | if(strpos($xml, '') !== 0) {
250 | $xml = ''
251 | .$xml.'';
252 | }
253 |
254 |
255 | $dom = new \DOMDocument();
256 | $dom->loadXML($xml,LIBXML_NOBLANKS);
257 |
258 | if($callback === null) {
259 | return $this->DOMDocument->importNode($dom->documentElement->firstChild,true);
260 | }else{
261 | return $this->DOMDocument->importNode($callback($dom->documentElement),true);
262 | }
263 | }
264 |
265 | protected function initChartRels($relArr) {
266 | $partInfo = pathinfo($relArr['PartName']);
267 | $this->rels = new Rels($this->word, $relArr['dom']);
268 | $this->rels->partName = $relArr['relName'];
269 | $this->rels->partInfo = $partInfo;
270 |
271 | $this->word->parts[19][] = ['PartName'=>$this->rels->partName,'DOMElement'=>$this->rels->DOMDocument];
272 | }
273 |
274 |
275 | protected function initCommentRange() {
276 | if(isset($this->DOMDocument->documentElement->tagList['w:commentRangeStart'])) {
277 | $commentRangeStartItems = $this->DOMDocument->documentElement->tagList['w:commentRangeStart'];
278 | }else{
279 | $commentRangeStartItems = [];
280 | }
281 | $tempBlocks = [];
282 | foreach($commentRangeStartItems as $commentRangeStartItem) {
283 | $id = $this->getAttr($commentRangeStartItem, 'id');
284 | $commentRangeEndItem = $this->getCommentRangeEnd($this->DOMDocument,$id);
285 | $name = $this->commentsblocks[$id];
286 | $traces = $this->getRangeTrace($id,$commentRangeStartItem, $commentRangeEndItem);
287 | if(!isset($tempBlocks[$name])) {
288 | $tempBlocks[$name] = [];
289 | $size = 0;
290 | }else{
291 | $size = sizeof($tempBlocks[$name])+1;
292 | }
293 |
294 | $tempOneBlock = array_map(function($trace) use ($name,$size) {
295 | $this->domIdxToName[$trace->idxBegin][] = [$size,$name];
296 | return $trace->idxBegin;
297 | }, $traces);
298 |
299 | if(strpos($id,'r') === 0) {
300 | foreach($tempOneBlock as $nodeId) {
301 | if(!isset($this->domList[$nodeId])) {
302 | continue;
303 | }
304 | $ts = $this->domList[$nodeId]->getElementsByTagName('t');
305 | foreach($ts as $t) {
306 | $t->nodeValue = '';
307 | }
308 | }
309 | }
310 |
311 | $tempBlocks[$name][] = $tempOneBlock;
312 | }
313 |
314 | return $tempBlocks;
315 | }
316 |
317 | protected function treeToList($node) {
318 | static $index = 0;
319 |
320 | if(is_null($node)) {
321 | return $index;
322 | }
323 | $node->idxBegin = $index;
324 | $node->tagList = [];
325 | $this->domList[$index++] = $node;
326 | if(($node->hasChildNodes())) {
327 | foreach($node->childNodes as $childNode) {
328 | if($childNode->nodeType !== 3) {
329 | $node->tagList[$childNode->tagName][] = $childNode;
330 | $tags = $this->treeToList($childNode);
331 | foreach($tags as $tag => $vals) {
332 | foreach($vals as $val) {
333 | $node->tagList[$tag][] = $val;
334 | if ($tag === 'a:blip') {
335 | $rId = $this->getAttr($val,'r:embed');
336 | $this->rIdToNode[$rId] = $val;
337 | }
338 | }
339 | }
340 | }
341 | }
342 | }
343 |
344 | $node->idxEnd = $index-1;
345 |
346 | return $node->tagList;
347 | }
348 |
349 | public function treeToListCallback($node,$callback) {
350 | if(is_null($node)) {
351 | return ;
352 | }
353 |
354 | if(($node->hasChildNodes())) {
355 | foreach($node->childNodes as $childNode) {
356 | if($childNode->nodeType !== 3) {
357 | $this->treeToListCallback($callback($childNode),$callback);
358 | }
359 | }
360 | }
361 | }
362 |
363 | public function getTextContent($nodes) {
364 | $text = '';
365 | $this->treeToListCallback($nodes,function($node) use (&$text) {
366 | //jump delete
367 | if($this->getAttr($node,'md',null)) {
368 | return null;
369 | }
370 |
371 | if($node->localName === 't') {
372 | $text .= $node->textContent;
373 | return null;
374 | }
375 |
376 | return $node;
377 | });
378 |
379 | return $text;
380 | }
381 |
382 |
383 | protected function replace($node, $targetNode) {
384 | if($parentNode = $targetNode->parentNode) {
385 | $parentNode->replaceChild($node, $targetNode);
386 | }
387 | }
388 |
389 | protected function getTreeToListBeginIdOldToNew($node,$beginId,$main=true) {
390 | static $beginIdOldToNew = [];
391 | static $index = 0;
392 | if($main) {
393 | $beginIdOldToNew = [];
394 | $index = $beginId;
395 | }
396 |
397 | if(!isset($beginIdOldToNew[$node->idxBegin])) {
398 | $beginIdOldToNew[$node->idxBegin] = $index;
399 | }
400 |
401 | $index++;
402 | if(($node->hasChildNodes())) {
403 | foreach($node->childNodes as $childNode) {
404 | if($childNode->nodeType !== 3) {
405 | $this->getTreeToListBeginIdOldToNew($childNode,0,false);
406 | }
407 | }
408 |
409 | }
410 |
411 | return $beginIdOldToNew;
412 | }
413 |
414 | protected function getCommentRangeEnd($parentNode,$id) {
415 | if(isset($parentNode->tagList['w:commentRangeEnd'])) {
416 | $commentRangeEndItems = $parentNode->tagList['w:commentRangeEnd'];
417 | }else{
418 | $commentRangeEndItems = [];
419 | }
420 | foreach($commentRangeEndItems as $commentRangeEndItem) {
421 | $eid = $this->getAttr($commentRangeEndItem, 'id');
422 |
423 | if($id === $eid) {
424 | return $commentRangeEndItem;
425 | }
426 | }
427 |
428 | return null;
429 | }
430 |
431 | protected function getRangeTrace($id,$commentRangeStartItem,$commentRangeEndItem) {
432 | $delTags = ['commentRangeStart'=>0];
433 | $traces = [];
434 | $startParentNode = $parentNode = $commentRangeStartItem;
435 | $parentNodeCount = 0;
436 | while($parentNode = $parentNode->parentNode) {
437 | $commentRangeEndItem = $this->getCommentRangeEnd($parentNode,$id);
438 | if(is_null($commentRangeEndItem)) {
439 | $startParentNode = $parentNode;
440 | }else{
441 | break;
442 | }
443 | $parentNodeCount++;
444 | }
445 |
446 | if(!isset($delTags[$startParentNode->localName])) {
447 | $traces[] = $startParentNode;
448 | }
449 |
450 | $nextNodeCount = 0;
451 | $nextSibling = $startParentNode->nextSibling;
452 | $nextNodeCount++;
453 | while(true) {
454 | if(is_null($nextSibling)) {
455 | break;
456 | }
457 |
458 | if($nextSibling === $commentRangeEndItem) {
459 | break;
460 | }
461 |
462 | if($this->getCommentRangeEnd($nextSibling,$id) !== null) {
463 | $childNodes = $nextSibling->childNodes;
464 | $find = false;
465 | $preCount = 0;
466 | $endCount = 0;
467 | $childNodesTemp = [];
468 | foreach ($childNodes as $childNode) {
469 | if($find === false) {
470 | $childNodesTemp[] = $childNode;
471 | }
472 |
473 | if($find === false && ($childNode === $commentRangeEndItem || $this->getCommentRangeEnd($childNode,$id) !== null)) {
474 | $find = true;
475 | continue;
476 | }
477 |
478 | if($this->isNeedSpace($childNode)) {
479 | if($find) {
480 | $endCount++;
481 | }else{
482 | $preCount++;
483 | }
484 | }
485 | }
486 |
487 | if($endCount === 0) {
488 | if(!isset($delTags[$nextSibling->localName])) {
489 | $traces[] = $nextSibling;
490 | }
491 | }elseif(count($traces) === 0) {
492 | foreach($childNodesTemp as $trace) {
493 | if(!isset($delTags[$trace->localName])) {
494 | $traces[] = $trace;
495 | }
496 | }
497 | }
498 |
499 | break;
500 | }else{
501 | if(!isset($delTags[$nextSibling->localName])) {
502 | $traces[] = $nextSibling;
503 | }
504 | }
505 |
506 | $nextSibling = $nextSibling->nextSibling;
507 | $nextNodeCount++;
508 | }
509 |
510 | return array_values($traces);
511 | }
512 |
513 | protected function isNeedSpace($node) {
514 | $ts = $node->getElementsByTagName('t');
515 | if($ts->length > 0) {
516 | return true;
517 | }else{
518 | return false;
519 | }
520 | }
521 |
522 | protected function showXml($node) {
523 | echo $this->DOMDocument->saveXML($node);exit;
524 | }
525 |
526 | protected function htmlspecialcharsBase($string,$needConvertSpecialFont = true) {
527 | $string = $this->my_html_entity_decode($string);
528 | $string = $this->filterUtf8($this->filterSpecailCodeForWord($string));
529 |
530 | $string = $this->controlCharacterPHP2OOXML($string);
531 | //preg string build by https://github.com/mkdreams/MDfontSubChar
532 |
533 | if($needConvertSpecialFont === true) {
534 | //SimSun-ExtB
535 | $string = preg_replace_callback('/(\{\%\{\d\}\%\}|)[\x{20000}-\x{2a6d6}\x{2a700}-\x{2b734}\x{2b740}-\x{2b81d}\x{2b8b8}-\x{2b8b9}\x{2bac7}-\x{2bac8}\x{2bb5f}-\x{2bb60}\x{2bb62}-\x{2bb63}\x{2bb7c}-\x{2bb7d}\x{2bb83}-\x{2bb84}\x{2bc1b}-\x{2bc1c}\x{2bd77}\x{2bd87}\x{2bdf7}\x{2be29}\x{2c029}-\x{2c02a}\x{2c0a9}\x{2c0ca}\x{2c1d5}\x{2c1d9}\x{2c1f9}\x{2c27c}\x{2c288}\x{2c2a4}\x{2c317}\x{2c35b}\x{2c361}\x{2c364}\x{2c488}\x{2c494}\x{2c497}\x{2c542}\x{2c613}\x{2c618}\x{2c621}\x{2c629}\x{2c62b}-\x{2c62d}\x{2c62f}\x{2c642}\x{2c64a}-\x{2c64b}\x{2c72c}\x{2c72f}\x{2c79f}\x{2c7c1}\x{2c7fd}\x{2c8d9}\x{2c8de}\x{2c8e1}\x{2c8f3}\x{2c907}\x{2c90a}\x{2c91d}\x{2ca02}\x{2ca0e}\x{2ca7d}\x{2caa9}\x{2cb29}\x{2cb2d}-\x{2cb2e}\x{2cb31}\x{2cb38}-\x{2cb39}\x{2cb3b}\x{2cb3f}\x{2cb41}\x{2cb4a}\x{2cb4e}\x{2cb5a}-\x{2cb5b}\x{2cb64}\x{2cb69}\x{2cb6c}\x{2cb6f}\x{2cb73}\x{2cb76}\x{2cb78}\x{2cb7c}\x{2cbb1}\x{2cbbf}-\x{2cbc0}\x{2cbce}\x{2cc56}\x{2cc5f}\x{2ccf5}-\x{2ccf6}\x{2ccfd}\x{2ccff}\x{2cd02}-\x{2cd03}\x{2cd0a}\x{2cd8b}\x{2cd8d}\x{2cd8f}-\x{2cd90}\x{2cd9f}-\x{2cda0}\x{2cda8}\x{2cdad}-\x{2cdae}\x{2cdd5}\x{2ce18}\x{2ce1a}\x{2ce23}\x{2ce26}\x{2ce2a}\x{2ce7c}\x{2ce88}\x{2ce93}]+/u', function($match){
536 | if($match[1] === '') {
537 | return '{%{0}%}'.$match[0].'{%{/0}%}';
538 | }else{
539 | return $match[0];
540 | }
541 | }, $string);
542 |
543 | //Cambria Math
544 | $string = preg_replace_callback('/(\{\%\{\d\}\%\}|)[\x{0}\x{d}\x{100}\x{102}-\x{112}\x{114}-\x{11a}\x{11c}-\x{12a}\x{12c}-\x{143}\x{145}-\x{147}\x{149}-\x{14c}\x{14e}-\x{151}\x{154}-\x{15f}\x{162}-\x{16a}\x{16c}-\x{177}\x{179}-\x{191}\x{193}-\x{1cd}\x{1cf}\x{1d1}\x{1d3}\x{1d5}\x{1d7}\x{1d9}\x{1db}\x{1dd}-\x{1f8}\x{1fa}-\x{250}\x{252}-\x{260}\x{262}-\x{2c5}\x{2c8}\x{2cc}-\x{2d8}\x{2da}-\x{2db}\x{2dd}-\x{377}\x{37a}-\x{37f}\x{384}-\x{38a}\x{38c}\x{38e}-\x{390}\x{3aa}-\x{3b0}\x{3c2}\x{3ca}-\x{400}\x{402}-\x{40f}\x{450}\x{452}-\x{52f}\x{531}-\x{556}\x{559}-\x{55f}\x{561}-\x{587}\x{589}-\x{58a}\x{58d}-\x{58f}\x{e3f}\x{1d00}-\x{1dca}\x{1dfe}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fc4}\x{1fc6}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fdd}-\x{1fef}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffe}\x{2000}-\x{200f}\x{2011}-\x{2012}\x{2017}\x{201b}\x{201f}\x{2024}\x{202f}\x{2034}\x{203c}-\x{203e}\x{2044}-\x{2046}\x{2057}\x{205e}-\x{205f}\x{2061}-\x{2063}\x{2070}-\x{2071}\x{2074}-\x{208e}\x{2090}-\x{209c}\x{20a0}-\x{20ab}\x{20ad}-\x{20b5}\x{20b8}-\x{20ba}\x{20bc}-\x{20bf}\x{20d0}-\x{20df}\x{20e1}\x{20e5}-\x{20e6}\x{20e8}-\x{20ea}\x{2100}-\x{2102}\x{2104}\x{2106}-\x{2108}\x{210a}-\x{2115}\x{2117}-\x{2120}\x{2123}-\x{214f}\x{2153}-\x{215e}\x{2183}-\x{2184}\x{2194}-\x{2195}\x{219a}-\x{2207}\x{2209}-\x{220e}\x{2210}\x{2212}-\x{2214}\x{2216}-\x{2219}\x{221b}-\x{221c}\x{2221}-\x{2222}\x{2224}\x{2226}\x{222c}-\x{222d}\x{222f}-\x{2233}\x{2238}-\x{223c}\x{223e}-\x{2247}\x{2249}-\x{224b}\x{224d}-\x{2251}\x{2253}-\x{225f}\x{2262}-\x{2263}\x{2268}-\x{226d}\x{2270}-\x{2294}\x{2296}-\x{2298}\x{229a}-\x{22a4}\x{22a6}-\x{22be}\x{22c0}-\x{2311}\x{2313}-\x{232a}\x{2330}-\x{23cf}\x{23dc}-\x{23e0}\x{246a}-\x{2473}\x{24ea}-\x{24f4}\x{24ff}\x{2592}\x{25a2}-\x{25b1}\x{25b4}-\x{25bb}\x{25be}-\x{25c5}\x{25c8}-\x{25ca}\x{25cc}-\x{25cd}\x{25d0}-\x{25e1}\x{25e6}-\x{25ff}\x{2660}-\x{2663}\x{2666}\x{2720}\x{2776}-\x{277f}\x{27c0}-\x{27ff}\x{2900}-\x{2aff}\x{2b04}\x{2b06}-\x{2b07}\x{2b0c}-\x{2b0d}\x{2b1a}\x{2c60}-\x{2c7f}\x{2e17}\x{a64c}-\x{a64d}\x{a717}-\x{a71a}\x{a720}-\x{a721}\x{fb00}-\x{fb04}\x{fb13}-\x{fb17}\x{fe00}\x{1d400}-\x{1d454}\x{1d456}-\x{1d49c}\x{1d49e}-\x{1d49f}\x{1d4a2}\x{1d4a5}-\x{1d4a6}\x{1d4a9}-\x{1d4ac}\x{1d4ae}-\x{1d4b9}\x{1d4bb}\x{1d4bd}-\x{1d4c0}\x{1d4c2}-\x{1d4c3}\x{1d4c5}-\x{1d505}\x{1d507}-\x{1d50a}\x{1d50d}-\x{1d514}\x{1d516}-\x{1d51c}\x{1d51e}-\x{1d539}\x{1d53b}-\x{1d53e}\x{1d540}-\x{1d544}\x{1d546}\x{1d54a}-\x{1d550}\x{1d552}-\x{1d6a5}\x{1d6a8}-\x{1d7cb}\x{1d7ce}-\x{1d7ff}\x{1d4c1}]+/u', function($match){
545 | if($match[1] === '') {
546 | return '{%{1}%}'.$match[0].'{%{/1}%}';
547 | }else{
548 | return $match[0];
549 | }
550 | }, $string);
551 | }
552 |
553 | return htmlspecialchars($string,ENT_COMPAT, 'UTF-8');
554 | }
555 |
556 | protected function my_html_entity_decode($string) {
557 | static $stringMd5 = '';
558 |
559 | $md5 = md5($string);
560 | if($stringMd5 !== $md5) {
561 | $stringMd5 = $md5;
562 | $string = html_entity_decode($string);
563 | $string = $this->my_html_entity_decode($string);
564 | }else{
565 | $stringMd5 = '';
566 | }
567 | return $string;
568 | }
569 |
570 | private function filterUtf8($string)
571 | {
572 | if ($string) {
573 | preg_match_all('/[\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/s', $string, $matches);
574 | return implode('', $matches[0]);
575 | }
576 | return $string;
577 | }
578 |
579 | private function filterSpecailCodeForWord($string) {
580 | $string = str_replace(array(''),'', $string);
581 | $string = preg_replace('/[\x14-\x1F]|[\x08]/i', '', $string);
582 | return $string;
583 | }
584 |
585 | /**
586 | * Convert from PHP control character to OpenXML escaped control character
587 | *
588 | * @param string $value Value to escape
589 | * @return string
590 | */
591 | public static function controlCharacterPHP2OOXML($value = '')
592 | {
593 | if (empty(self::$controlCharacters)) {
594 | self::buildControlCharacters();
595 | }
596 |
597 | return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $value);
598 | }
599 |
600 | /**
601 | * Build control characters array.
602 | *
603 | * @return void
604 | */
605 | private static function buildControlCharacters()
606 | {
607 | for ($i = 0; $i <= 19; ++$i) {
608 | if ($i != 9 && $i != 10 && $i != 13) {
609 | $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_';
610 | $replace = chr($i);
611 | self::$controlCharacters[$find] = $replace;
612 | }
613 | }
614 | }
615 |
616 | /**
617 | * @param \DOMDocument $rPr
618 | * @param \DOMDocument $default
619 | */
620 | public function mergerPr($rPr,$default) {
621 | if(is_null($rPr)) {
622 | return null;
623 | }
624 | if(is_null($default)) {
625 | return null;
626 | }
627 |
628 | foreach($default->childNodes as $node) {
629 | $localName = $node->localName;
630 | $items = $rPr->getElementsByTagName($localName);
631 | if($items->length > 0) {
632 | $item = $items->item(0);
633 | $rPrAttrs = $this->getAttributes($item);
634 | foreach($node->attributes as $attribute) {
635 | if(!isset($rPrAttrs[$attribute->nodeName]) && !isset($rPrAttrs[$attribute->nodeName.'Theme'])) {
636 | $this->setAttr($item, $attribute->localName, $attribute->value);
637 | }
638 | }
639 | }else{
640 | $copyNode = $this->createElementNS($rPr,$node->nodeName,null);
641 | foreach($node->attributes as $attribute) {
642 | $this->setAttr($copyNode, $attribute->localName, $attribute->value);
643 | }
644 | $this->appendChild($rPr, $copyNode);
645 | }
646 | }
647 |
648 | return $rPr;
649 | }
650 |
651 | private function getAttributes($node) {
652 | $attrs = [];
653 | foreach($node->attributes as $attribute) {
654 | $attrs[$attribute->nodeName] = [$attribute->prefix,$attribute->name,$attribute->value];
655 | }
656 |
657 | return $attrs;
658 | }
659 |
660 | public function free() {
661 | $bindValueFunc = new \ReflectionMethod($this,'treeToList');
662 | $statics = $bindValueFunc->getStaticVariables();
663 | $statics['index'] = 0;
664 |
665 | $bindValueFunc = new \ReflectionMethod($this,'getTreeToListBeginIdOldToNew');
666 | $statics = $bindValueFunc->getStaticVariables();
667 | $statics['beginIdOldToNew'] = [];
668 | $statics['index'] = [];
669 | }
670 |
671 | public function getIndex($pNode,$subNode) {
672 | $tag = $subNode->localName;
673 | $tagNodes = $pNode->getElementsByTagName($tag);
674 | $idx = -1;
675 | foreach($tagNodes as $index => $tagNode) {
676 | if($tagNode === $subNode) {
677 | $idx = $index;
678 | break;
679 | }
680 | }
681 |
682 | return $idx;
683 | }
684 | }
--------------------------------------------------------------------------------
/src/Common/Tree.php:
--------------------------------------------------------------------------------
1 | DOMDocument = $DOMDocument;
13 | }
14 |
15 | /**
16 | *
17 | * @param string $chartFilenameRel
18 | * @param [] $datas
19 | * @param \DOMDocument $domDocument
20 | * @param string $type
21 | */
22 | public function chartRelUpdateByType($datas,$type='str') {
23 | $refs = $this->DOMDocument->getElementsByTagName($type.'Ref');
24 | if($refs->length > 0) {
25 | for($i =0; $i < $refs->length; $i++) {
26 | $item = $refs->item($i);
27 | $f = $item->getElementsByTagName('f')->item(0);
28 | $cache = $item->getElementsByTagName($type.'Cache')->item(0);
29 |
30 | foreach($datas as $data) {
31 | $range = $f->nodeValue;
32 | $rangInfo = $this->parserRange($range);
33 | $rangTempInfo = $data[0];
34 | $value = $data[1];
35 | $action = $data[2];
36 |
37 | //表不一样,跳过
38 | if($rangTempInfo[0] !== $rangInfo[0]) {
39 | continue;
40 | }
41 | // var_dump($rangInfo,$rangTempInfo,$value,$action);
42 | //赋值
43 | if($action == 'set') {
44 | if(
45 | isset($rangInfo[1][1]) && $rangInfo[1][0] <= $rangTempInfo[1][0] && $rangTempInfo[1][0] <= $rangInfo[1][1]
46 | && $rangInfo[2][0] <= $rangTempInfo[2][0] && $rangTempInfo[2][0] <= $rangInfo[2][1]
47 | ) {
48 | $idx = $rangTempInfo[2][0] - $rangInfo[2][0];
49 | $cache->getElementsByTagName('pt')->item($idx)->firstChild->nodeValue = $value;
50 | }elseif($rangInfo[1][0] == $rangTempInfo[1][0] && $rangInfo[2][0] == $rangTempInfo[2][0]) {
51 | $idx = $rangTempInfo[2][0] - $rangInfo[2][0];
52 | $cache->getElementsByTagName('pt')->item($idx)->firstChild->nodeValue = $value;
53 | }
54 | }elseif($action == 'ext') {//扩展取值范围
55 | if(
56 | $rangInfo[1][0] === $rangTempInfo[1][0] && $rangInfo[1][1] === $rangTempInfo[1][1]
57 | && $rangInfo[2][0] === $rangTempInfo[2][0] && $rangInfo[2][1] === $rangTempInfo[2][1]
58 | ) {
59 | $f->nodeValue = $value[3];
60 | //ptCount update
61 | $ptCount = $value[2][1]-$value[2][0]+1;
62 | $cache->getElementsByTagName('ptCount')->item(0)->setAttribute('val',$ptCount);
63 |
64 | $pts = $cache->getElementsByTagName('pt');
65 |
66 |
67 | for($j = $pts->length;$j < $ptCount;$j++) {
68 | $vNode = $this->DOMDocument->createElementNS('http://schemas.openxmlformats.org/drawingml/2006/chart','v','null');
69 | $ptNode = $this->DOMDocument->createElementNS('http://schemas.openxmlformats.org/drawingml/2006/chart','pt');
70 | $ptNode->setAttribute('idx', $j);
71 | $ptNode->appendChild($vNode);
72 |
73 | $cache->appendChild($ptNode);
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Edit/Part/Comments.php:
--------------------------------------------------------------------------------
1 | DOMDocument = $DOMDocument;
13 | $this->initNameSpaces();
14 | $this->blocks = $this->getBlocks();
15 | }
16 |
17 | private function deleteBlock($block) {
18 | $beginNode = $block[0]['node'];
19 | $endNode = $block[1]['node'];
20 |
21 | $parentNode = $beginNode->parentNode;
22 |
23 | if(!is_null($beginNode)) {
24 | $parentNode->removeChild($beginNode);
25 | }
26 | if(!is_null($endNode)) {
27 | $parentNode->removeChild($endNode);
28 | }
29 | }
30 |
31 | private function getBlocks() {
32 | $items = $this->DOMDocument->getElementsByTagName('comment');
33 |
34 | $preIsDollar = 0;
35 | $beginNode = null;
36 | $endNode = null;
37 | $blockName = '';
38 |
39 | $blocks = [];
40 | foreach ($items as $item) {
41 | $text = $item->nodeValue;
42 |
43 | if(MDWORD_BIND_TYPE === 2) {
44 | $blocks[$this->getAttr($item, 'id')] = trim($text);
45 | continue;
46 | }
47 |
48 | //default MDWORD_BIND_TYPE === 1
49 | $textArr = [];
50 | preg_match_all("/./u",$text,$textArr);
51 | if(!is_array($textArr)) {
52 | continue;
53 | }
54 |
55 | foreach($textArr[0] as $word) {
56 | if($word == '') {
57 | continue;
58 | }
59 |
60 | if($word === '$') {
61 | $preIsDollar = 1;
62 | }elseif($preIsDollar == 1 && $word === '{') {
63 | $beginNode = $item;
64 | $preIsDollar = 0;
65 | }elseif(!is_null($beginNode) && $word === '}') {
66 | $endNode = $item;
67 | }elseif(!is_null($beginNode)){
68 | $blockName .= $word;
69 | $preIsDollar = 0;
70 | }else{
71 | $preIsDollar = 0;
72 | }
73 |
74 | if(!is_null($beginNode) && !is_null($endNode)) {
75 | $blocks[$this->getAttr($item, 'id')] = $blockName;
76 | $blockName = '';
77 | $beginNode = null;
78 | $endNode = null;
79 | }
80 | }
81 | }
82 |
83 | return $blocks;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Edit/Part/Excel.php:
--------------------------------------------------------------------------------
1 | word = $word;
18 |
19 | if(class_exists('PhpOffice\PhpSpreadsheet\Spreadsheet')) {
20 | $this->spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
21 | }else{
22 | $this->spreadsheet = new \PHPExcel_Reader_Excel2007();
23 | }
24 |
25 | $partName = $this->pathRelToAbs($partName);
26 | $content = $this->word->zip->getFromName($partName);
27 | $this->tempDocumentFilename = tempnam($this->getTempDir(), 'MDwordExcelPart');
28 | file_put_contents($this->tempDocumentFilename, $content);
29 |
30 | $this->word->files[] = ['type'=>'excel','PartName'=>$partName,'this'=>$this];
31 |
32 | $this->spreadsheet = $this->spreadsheet->load($this->tempDocumentFilename);
33 | }
34 |
35 | public function changeExcelValues($datas) {
36 | foreach($datas as $data) {
37 | if($data[2] === 'set') {
38 | $currentSheet = $this->spreadsheet->getSheetByName($data[0][0]);
39 | $currentSheet->setCellValue($data[0][1][0].$data[0][2][0],$data[1]);
40 | }
41 | }
42 | }
43 |
44 | public function preDealDatas ($datas) {
45 | $setDatas = [];
46 | $extDatas = [];
47 | foreach($datas as $data) {
48 | if($data[2] == 'set') {
49 | $data[0] = $this->parserRange($data[0]);
50 | $setDatas[] = $data;
51 | }else if($data[2] == 'ext') {
52 | $data[0] = $this->parserRange($data[0]);
53 | $data[1] = $this->parserRange($data[1]);
54 | $extDatas[] = $data;
55 | }
56 | }
57 |
58 | return array_merge($extDatas,$setDatas);
59 | }
60 |
61 | public function getContent() {
62 | $this->PHPExcel_Writer = new \PHPExcel_Writer_Excel2007($this->spreadsheet);
63 | $this->PHPExcel_Writer->save($this->tempDocumentFilename);
64 | $content = file_get_contents($this->tempDocumentFilename);
65 | @unlink($this->tempDocumentFilename);
66 | return $content;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Edit/Part/Footer.php:
--------------------------------------------------------------------------------
1 | DOMDocument = $DOMDocument;
15 | $this->initNameSpaces();
16 | }
17 |
18 | public function getAbstractNumById($numId) {
19 | if(is_null($this->abstractNums)) {
20 | $tempnums = $this->DOMDocument->getElementsByTagName('abstractNum');
21 | $nums = $this->DOMDocument->getElementsByTagName('num');
22 | foreach($nums as $num) {
23 | $abstractNumId = intval($this->getVal($num,'abstractNumId'));
24 | $this->numIdToAbstractNumId[intval($this->getAttr($num,'numId'))] = [$abstractNumId,$num];
25 | }
26 | $this->abstractNums = [];
27 | foreach($tempnums as $abstractNum) {
28 | $abstractNumId = $this->getAttr($abstractNum,'abstractNumId');
29 | $numInfo = ['lvls'=>[],'abstractNumId'=>$abstractNumId,'text'=>[]];
30 | $lvls = $abstractNum->getElementsByTagName('lvl');
31 | foreach($lvls as $lvl) {
32 | $ilvl = intval($this->getAttr($lvl, 'ilvl'));
33 | $start = intval($this->getVal($lvl, 'start'));
34 | $numFmt = $this->getVal($lvl, 'numFmt');
35 | $lvlText = $this->getVal($lvl, 'lvlText');
36 | $numInfo['lvls'][$ilvl] = ['lvl'=>$lvl,'start'=>$start,'isLgl'=>$this->getExist($lvl,'isLgl'),'numFmt'=>$numFmt,'lvlText'=>$lvlText];
37 | }
38 |
39 | $this->abstractNums[$abstractNumId] = $numInfo;
40 | }
41 | }
42 |
43 | return $this->abstractNums[$this->numIdToAbstractNumId[$numId][0]];
44 | }
45 |
46 | public function getText($numId,$ilvl) {
47 | static $numIdRecored = [];
48 | $num = $this->getAbstractNumById($numId);
49 | $lvlInfo = $num['lvls'][$ilvl];
50 | $lvl = $lvlInfo['lvl'];
51 |
52 | if(is_null($lvl)) {
53 | return '';
54 | }
55 |
56 | $abstractNumId = $num['abstractNumId'];
57 | $start = $lvlInfo['start'];
58 | $numFmt = $lvlInfo['numFmt'];
59 | $lvlText = $lvlInfo['lvlText'];
60 |
61 | if(!isset($numIdRecored[$numId])) {
62 | $numIdIsFirst = true;
63 | }else{
64 | $numIdIsFirst = false;
65 | }
66 | $numIdRecored[$numId] = true;
67 | if($numIdIsFirst === true) {
68 | $numNode = $this->numIdToAbstractNumId[$numId][1];
69 | $lvlOverrides = $numNode->getElementsByTagName('lvlOverride');
70 | if($lvlOverrides->length > 0) {
71 | foreach($lvlOverrides as $lvlOverride) {
72 | $ilvlTemp = $this->getAttr($lvlOverride,'ilvl');
73 | $startOverrideTemp = $this->getVal($lvlOverride,'startOverride');
74 | $this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['index'] = $startOverrideTemp;
75 | }
76 | }
77 | }
78 |
79 | foreach($this->abstractNums[$abstractNumId]['text'] as $ilvlTemp => $val) {
80 | if($ilvlTemp > $ilvl) {
81 | unset($this->abstractNums[$abstractNumId]['text'][$ilvlTemp]);
82 | }
83 | }
84 |
85 | if(!isset($this->abstractNums[$abstractNumId]['text'][$ilvl])) {
86 | $this->abstractNums[$abstractNumId]['text'][$ilvl] = [];
87 | $this->abstractNums[$abstractNumId]['text'][$ilvl]['index'] = $start;
88 | }
89 |
90 | $this->abstractNums[$abstractNumId]['text'][$ilvl]['text'] = $this->getTextByIndex($numFmt,$this->abstractNums[$abstractNumId]['text'][$ilvl]['index']);
91 | $text = preg_replace_callback('/\%(\d+)/i',function($match) use($abstractNumId,$num,$ilvl){
92 | $ilvlTemp = $match[1]-1;
93 | if(!isset($this->abstractNums[$abstractNumId]['text'][$ilvlTemp])) {
94 | $this->abstractNums[$abstractNumId]['text'][$ilvlTemp] = [];
95 | $this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['index'] = $num['lvls'][$ilvlTemp]['start'];
96 | $this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['text'] = $this->getTextByIndex($num['lvls'][$ilvlTemp]['numFmt'],$this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['index']);
97 | $this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['index']++;
98 | }
99 |
100 | if($num['lvls'][$ilvl]['isLgl'] === true) {
101 | return $this->getTextByIndex($num['lvls'][$ilvl]['numFmt'],$this->abstractNums[$abstractNumId]['text'][$ilvl]['index']);
102 | }else{
103 | return $this->abstractNums[$abstractNumId]['text'][$ilvlTemp]['text'];
104 | }
105 | },$lvlText);
106 |
107 | $this->abstractNums[$abstractNumId]['text'][$ilvl]['index']++;
108 |
109 | return $text;
110 | }
111 |
112 | private function getTextByIndex($numFmt,$index) {
113 | //$enum = array('bullet', 'decimal', 'upperRoman', 'lowerRoman', 'upperLetter', 'lowerLetter');
114 | switch($numFmt) {
115 | case 'decimal':
116 | return $index;
117 | break;
118 | case 'upperLetter':
119 | return chr(64+$index);
120 | break;
121 | case 'lowerLetter':
122 | return chr(96+$index);
123 | break;
124 | case 'upperRoman':
125 | $nf = new \NumberFormatter('@numbers=roman', \NumberFormatter::DECIMAL);
126 | return $nf->format($index);
127 | case 'lowerRoman':
128 | $nf = new \NumberFormatter('@numbers=roman', \NumberFormatter::DECIMAL);
129 | return strtolower($nf->format($index));
130 | case 'chineseCountingThousand':
131 | $nf = new \NumberFormatter('zh_CN', \NumberFormatter::SPELLOUT);
132 | return $nf->format($index);
133 | break;
134 | case 'ideographTraditional':
135 | $list = ["","甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
136 | return isset($list[$index])?$list[$index]:($index+1);
137 | break;
138 | case 'ideographZodiac':
139 | $list = ["","子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
140 | return isset($list[$index])?$list[$index]:($index+1);
141 | break;
142 | case 'chineseLegalSimplified':
143 | $list = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
144 | return isset($list[$index])?$list[$index]:($index+1);
145 | break;
146 | default:
147 | return $index+1;
148 | break;
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Edit/Part/Rels.php:
--------------------------------------------------------------------------------
1 | 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
18 | 1 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings',
19 | 2 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
20 | 3 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
21 | 4 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
22 | 5 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering',
23 | 6 => 'http://schemas.microsoft.com/office/2011/relationships/commentsExtended',
24 | 7 => 'http://schemas.microsoft.com/office/2011/relationships/people',
25 | 8 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
26 | 9 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable',
27 | 10 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings',
28 | 11 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes',
29 | 12 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml',
30 | 13 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes',
31 | 14 => 'http://schemas.microsoft.com/office/2016/09/relationships/commentsIds',
32 | 15 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package',
33 | 16 => 'http://schemas.microsoft.com/office/2011/relationships/chartColorStyle',
34 | 17 => 'http://schemas.microsoft.com/office/2011/relationships/chartStyle',
35 | 18 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer',
36 | 19 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header',
37 | 20 => 'http://schemas.microsoft.com/office/2018/08/relationships/commentsExtensible',
38 | 21 => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
39 | )//--RELATIONSHIPTYPES--
40 | ;
41 |
42 | public function __construct($word,\DOMDocument $DOMDocument) {
43 | parent::__construct($word);
44 | $this->DOMDocument = $DOMDocument;
45 |
46 | $Relationships = $this->DOMDocument->getElementsByTagName('Relationship');
47 | foreach ($Relationships as $Relationship) {
48 | if(!in_array($type = $this->getAttr($Relationship, 'Type'), $this->relationshipTypes)) {
49 | $this->relationshipTypes[] = $type;
50 | }
51 |
52 | $rid = $this->getAttr($Relationship, 'Id');
53 | $this->ridToTarget[$rid] = $Relationship;
54 |
55 | $id = intval(str_replace('rId', '', $rid));
56 | if($id > $this->rIdMax) {
57 | $this->rIdMax = $id;
58 | }
59 |
60 | if($this->relationshipTypes[2] === $this->getAttr($Relationship, 'Type')) {
61 | $md5 = md5($this->word->zip->getFromName('word/'.$this->getAttr($Relationship, 'Target')));
62 | $this->imageMd5ToRid[$md5][] = $rid;
63 | }
64 | }
65 |
66 | if(MDWORD_DEBUG) {
67 | $build = new Build();
68 | $build->replace('RELATIONSHIPTYPES', $this->relationshipTypes, __FILE__);
69 | }
70 | }
71 |
72 | public function replace($rid,$file) {
73 | $Relationships = $this->DOMDocument->getElementsByTagName('Relationship');
74 | foreach ($Relationships as $Relationship) {
75 | if($Relationship->getAttribute('Id') === $rid) {
76 | $type = $this->getAttr($Relationship, 'Type');
77 | $oldTarget = $Relationship->getAttribute('Target');
78 | switch ($type) {
79 | case $this->relationshipTypes[2]:
80 | $imageInfo = @getimagesize($file);
81 | if($imageInfo === false) {
82 | $this->word->log->writeLog('image not invalid! image:'.$file);
83 | return false;
84 | }
85 |
86 | $mimeArr = explode('/', $imageInfo['mime'],2);
87 | $Extension = $mimeArr[1];
88 |
89 | $target = 'media/replace'.pathinfo($file, PATHINFO_FILENAME).'.'.$Extension;
90 | $this->word->Content_Types->addDefault($Extension, $imageInfo['mime']);
91 | break;
92 | }
93 |
94 | //删除旧文件
95 | $oldValue = $this->partInfo['dirname'].'/'.$oldTarget;
96 | $this->word->zip->deleteName($oldValue);
97 |
98 | //替换
99 | $Relationship->setAttribute('Target',$target);
100 | $target = $this->partInfo['dirname'].'/'.$target;
101 | $this->word->zip->addFromString($target, file_get_contents($file));
102 | }
103 | }
104 | }
105 |
106 | public function cloneRels($rid) {
107 | return $this->insert($rid,'clone');
108 | }
109 |
110 | public function insert($file,$fileType) {
111 | $this->rIdMax = $this->rIdMax + 1;
112 |
113 | switch ($fileType) {
114 | case MDWORD_IMG:
115 | $type = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image';
116 | $imageInfo = @getimagesize($file);
117 | if($imageInfo === false) {
118 | return false;
119 | }
120 | $mimeArr = explode('/', $imageInfo['mime'],2);
121 | $Extension = $mimeArr[1];
122 |
123 | $target = 'media/image'.$this->rIdMax.'.'.$Extension;
124 | $rId = 'rId'.$this->rIdMax;
125 | $Relationship = $this->createNodeByXml('');
126 | if($this->DOMDocument->getElementsByTagName('Relationships')->length == 0){
127 | $Relationships = $this->createNodeByXml('');
128 | $this->DOMDocument->encoding = 'UTF-8';
129 | $this->DOMDocument->appendChild($Relationships);
130 | }
131 | $this->DOMDocument->getElementsByTagName('Relationships')->item(0)->appendChild($Relationship);
132 |
133 | $target = $this->partInfo['dirname'].'/'.$target;
134 | $this->word->zip->addFromString($target, file_get_contents($file));
135 |
136 | $this->word->Content_Types->addDefault($Extension, $imageInfo['mime']);
137 | return ['rId'=>$rId,'imageInfo'=>$imageInfo];
138 | break;
139 | case 'clone':
140 | $rid = $file;
141 | $Relationship = $this->ridToTarget[$rid];
142 | $type = $this->getAttr($Relationship, 'Type');
143 | $oldTarget = $Relationship->getAttribute('Target');
144 | $c = $this->word->zip->getFromName($this->partInfo['dirname'].'/'.$oldTarget);
145 | $target = preg_replace_callback('/\d+?(?=\.)/i',function($match) {
146 | return $this->rIdMax;
147 | },$oldTarget);
148 | $rId = 'rId'.$this->rIdMax;
149 |
150 | $Relationship = $this->createNodeByXml('');
151 | if($this->DOMDocument->getElementsByTagName('Relationships')->length == 0){
152 | $Relationships = $this->createNodeByXml('');
153 | $this->DOMDocument->encoding = 'UTF-8';
154 | $this->DOMDocument->appendChild($Relationships);
155 | }
156 | $this->DOMDocument->getElementsByTagName('Relationships')->item(0)->appendChild($Relationship);
157 | $target = $this->partInfo['dirname'].'/'.$target;
158 | $this->word->zip->addFromString($target, $c);
159 | if($type === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header') {
160 | $this->word->Content_Types->addOverride('/'.$target, 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml');
161 | }elseif($type === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer') {
162 | $this->word->Content_Types->addOverride('/'.$target, 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml');
163 | }
164 | return ['rId'=>$rId,'partName'=>$target,'xml'=>$c];
165 | break;
166 | }
167 | }
168 |
169 | public function getTarget($rid=null) {
170 | $Relationships = $this->DOMDocument->getElementsByTagName('Relationship');
171 | foreach ($Relationships as $Relationship) {
172 | if(is_null($rid)) {
173 | return $this->partInfo['dirname'].'/'.$Relationship->getAttribute('Target');
174 | }
175 |
176 | if($Relationship->getAttribute('Id') === $rid) {
177 | return $this->partInfo['dirname'].'/'.$Relationship->getAttribute('Target');
178 | }
179 | }
180 |
181 | return null;
182 | }
183 |
184 | public function setNewChartRels($chartCount){
185 | $Relationships = $this->DOMDocument->getElementsByTagName('Relationship');
186 | $tempMaxId = 0;
187 | $chartNum = 0;
188 | foreach ($Relationships as $Relationship) {
189 | $target = $Relationship->getAttribute('Target');
190 | if(strpos($target,'charts') !== false){
191 | $chartNum++;
192 | // $Relationship->parentNode->removeChild($Relationship);
193 | }
194 | $rId = $Relationship->getAttribute('Id');
195 | preg_match('/(\d+)/',$rId,$match);
196 | if($match[1] > $tempMaxId){
197 | $tempMaxId = $match[1];
198 | }
199 | }
200 | $Relationships2 = $this->DOMDocument->getElementsByTagName('Relationship');
201 | $chartRid = [];
202 | for($i = 0 ; $i < $chartCount ; $i++){
203 | $chartNum++;
204 | $tempMaxId++;
205 | $copy = clone $Relationships[0];
206 | $copy->setAttribute('Id','rId'.$tempMaxId);
207 | $chartRid[] = $tempMaxId;
208 | $copy->setAttribute('Type',$this->relationshipTypes[0]);
209 | $copy->setAttribute('Target','charts/chart'.$chartNum.'.xml');
210 | $Relationships[0]->parentNode->appendChild($copy);
211 | }
212 | return $chartRid;
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/Edit/Part/Styles.php:
--------------------------------------------------------------------------------
1 | DOMDocument = $DOMDocument;
14 | $this->initNameSpaces();
15 | }
16 |
17 | public function getStyleById($id) {
18 | if(is_null($this->styles)) {
19 | $tempStyles = $this->DOMDocument->getElementsByTagName('style');
20 | $this->styles = [];
21 | foreach($tempStyles as $style) {
22 | $styleId = $this->getAttr($style, 'styleId');
23 | $this->styles[$styleId] = $style;
24 | }
25 | }
26 |
27 | return $this->styles[$id];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Read/Part/ContentTypes.php:
--------------------------------------------------------------------------------
1 | 'application/vnd.openxmlformats-package.relationships+xml',
16 | 1 => 'application/xml',
17 | 2 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',
18 | 3 => 'application/vnd.openxmlformats-officedocument.customXmlProperties+xml',
19 | 4 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml',
20 | 5 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml',
21 | 6 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml',
22 | 7 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml',
23 | 8 => 'application/vnd.openxmlformats-officedocument.theme+xml',
24 | 9 => 'application/vnd.openxmlformats-package.core-properties+xml',
25 | 10 => 'application/vnd.openxmlformats-officedocument.extended-properties+xml',
26 | 11 => 'application/octet-stream',
27 | 12 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml',
28 | 13 => 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml',
29 | 14 => 'image/png',
30 | 15 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml',
31 | 16 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml',
32 | 17 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml',
33 | 18 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.people+xml',
34 | 19 => 'rels',
35 | 20 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml',
36 | 21 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml',
37 | 22 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml',
38 | 23 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml',
39 | 24 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
40 | 25 => 'application/vnd.ms-office.chartstyle+xml',
41 | 26 => 'application/vnd.ms-office.chartcolorstyle+xml',
42 | 27 => 'image/jpeg',
43 | 28 => 'image/x-wmf',
44 | 29 => 'application/vnd.openxmlformats-officedocument.oleObject',
45 | 30 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml',
46 | 31 => 'image/x-ms-bmp',
47 | )//--CONTENTTYPES--
48 | ;
49 | /**
50 | * @param \DOMDocument $DOMDocument
51 | */
52 | public function __construct(\DOMDocument $DOMDocument) {
53 | parent::__construct();
54 |
55 | $this->DOMDocument = $DOMDocument;
56 | $this->parse();
57 | }
58 |
59 | public function parse() {
60 | $Types = $this->DOMDocument->getElementsByTagName('Types')->item(0);
61 | $childrens = $Types->getElementsByTagName('*');
62 | foreach($childrens as $children) {
63 | $this->paseItem($children);
64 | }
65 |
66 | if(MDWORD_DEBUG) {
67 | $build = new Build();
68 | $build->replace('CONTENTTYPES', $this->contentTypes, __FILE__);
69 | }
70 | }
71 |
72 | public function getPartNames() {
73 | return $this->partNames;
74 | }
75 |
76 | private function paseItem(\DOMElement $item) {
77 | $ContentType = $item->getAttribute('ContentType');
78 | $pos = array_search($ContentType,$this->contentTypes);
79 | if($pos === false) {
80 | $this->contentTypes[] = $ContentType;
81 | $pos = sizeof($this->contentTypes) - 1;
82 | }
83 |
84 | switch ($item->tagName) {
85 | case 'Default':
86 | $Extension = $item->getAttribute('Extension');
87 | $this->defaults[$Extension] = ['Extension'=>$Extension,'ContentType'=>$pos];
88 | break;
89 | case 'Override':
90 | $PartName = ltrim($item->getAttribute('PartName'),'/');
91 | $this->overrides[$PartName] = ['PartName'=>$PartName,'ContentType'=>$pos];
92 | break;
93 | }
94 | }
95 |
96 | public function addDefault($Extension,$ContentType) {
97 | if(!isset($this->defaults[$Extension])) {
98 | $pos = array_search($ContentType,$this->contentTypes);
99 | if($pos === false) {
100 | $this->word->log->writeLog('content type not find! type:'.$ContentType);
101 | }else{
102 | $this->defaults[$Extension] = ['Extension'=>$Extension,'ContentType'=>$pos];
103 | }
104 |
105 | $node = $this->createNodeByXml('');
106 | $Types = $this->DOMDocument->getElementsByTagName('Types')->item(0);
107 | $this->insertBefore($node, $Types->firstChild);
108 | }
109 | }
110 |
111 | public function addOverride($PartName='word/document.xml',$ContentTypeIdx=2) {
112 | if(is_string($ContentTypeIdx)) {
113 | $pos = array_search($ContentTypeIdx,$this->contentTypes);
114 | if($pos === false) {
115 | $this->contentTypes[] = $ContentTypeIdx;
116 | $pos = sizeof($this->contentTypes) - 1;
117 | }
118 | $ContentTypeIdx = $pos;
119 | }
120 | $this->overrides[$PartName] = ['PartName'=>$PartName,'ContentType'=>$ContentTypeIdx];
121 |
122 | $overrides = $this->DOMDocument->getElementsByTagName('Override');
123 | $lastOverride = $overrides->item($overrides->length-1);
124 | $copy = clone $lastOverride;
125 | $this->setAttr($copy,'PartName',$PartName,'');
126 | $this->setAttr($copy,'ContentType',$this->contentTypes[$ContentTypeIdx],'');
127 | $this->insertAfter($copy,$lastOverride);
128 | }
129 |
130 | public function setContent_types($newOverrides){
131 | $this->overrides = array_merge($this->overrides,$newOverrides);
132 | $xlsDefault = ['Extension' => 'xlsx','ContentType' =>11];
133 | foreach($this->defaults as $default){
134 | $defaultArr[] = $default['Extension'];
135 | }
136 | if(!array_search('xlsx',$defaultArr)){
137 | $this->defaults[] = $xlsDefault;
138 | $defaults = $this->DOMDocument->getElementsByTagName('Default');
139 | $copy = clone $defaults[0];
140 | $copy->setAttribute('Extension',$xlsDefault['Extension']);
141 | $copy->setAttribute('ContentType',$this->contentTypes[$xlsDefault['ContentType']]);
142 | $defaults[0]->parentNode->appendChild($copy);
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/Read/Word.php:
--------------------------------------------------------------------------------
1 | log = new Log();
66 | }
67 |
68 |
69 | public function load($archive) {
70 | if(!is_file($archive)) {
71 | throw new \Exception('Template not exist!');
72 | }
73 |
74 | $this->tempDocumentFilename = tempnam($this->getTempDir(), 'MDword');
75 | if (false === $this->tempDocumentFilename) {
76 | throw new \Exception('temp path make failed!');
77 | }
78 |
79 |
80 | if (false === file_put_contents($this->tempDocumentFilename, file_get_contents($archive)) || !is_file($this->tempDocumentFilename)) {
81 | throw new \Exception($archive.' copy file failed!');
82 | }
83 |
84 | $this->zip = new \ZipArchive();
85 | $this->zip->open($this->tempDocumentFilename);
86 |
87 | $this->read();
88 |
89 | //update needUpdateParts
90 | if(isset($this->parts[22]) && isset($this->blocks[22])) {
91 | foreach($this->blocks[22] as $partName => $value) {
92 | $this->needUpdateParts[$partName] = ['func'=>'getHeaderEdit','partName'=>$partName];
93 | }
94 | }
95 |
96 | $this->needUpdateParts['word/document.xml'] = ['func'=>'getDocumentEdit','partName'=>'word/document.xml'];
97 |
98 | if(isset($this->parts[23]) && isset($this->blocks[23])) {
99 | foreach($this->blocks[23] as $partName => $value) {
100 | $this->needUpdateParts[$partName] = ['func'=>'getFooterEdit','partName'=>$partName];
101 | }
102 | }
103 | }
104 |
105 | private function read() {
106 | $this->Content_Types = new ContentTypes($this->getXmlDom('[Content_Types].xml'));
107 | $this->Content_Types->word = $this;
108 | foreach ($this->Content_Types->overrides as $part) {
109 | if(in_array($part['ContentType'], [2,22,23])) {//document footer header
110 | $standardXmlFunc = function($xml) use($part) {
111 | $xml = $this->standardXml($xml,$part['ContentType'],$part['PartName']);
112 | return $xml;
113 | };
114 | }else{
115 | $standardXmlFunc = null;
116 | }
117 |
118 | if($part['ContentType'] === 14) {//image/png
119 | $this->parts[$part['ContentType']][] = ['PartName'=>$part['PartName'],'DOMElement'=>$part['PartName']];
120 | }else{
121 | $this->parts[$part['ContentType']][] = ['PartName'=>$part['PartName'],'DOMElement'=>$this->getXmlDom($part['PartName'],$standardXmlFunc)];
122 | }
123 | }
124 | }
125 |
126 | public static function getTempDir()
127 | {
128 | $tempDir = sys_get_temp_dir();
129 |
130 | if (!empty(self::$tempDir)) {
131 | $tempDir = self::$tempDir;
132 | }
133 |
134 | return $tempDir;
135 | }
136 |
137 | public function save($remainComments = false)
138 | {
139 | //clean before update
140 | $this->deleteComments($remainComments);
141 |
142 | //update Toc
143 | $this->documentEdit->updateToc();
144 |
145 | $this->deleteComments($remainComments);
146 |
147 | foreach($this->parts as $type => $list ) {
148 | foreach($list as $part) {
149 | if(is_object($part['DOMElement'])) {
150 | if(empty($part['DOMElement']->documentElement)){
151 | continue;
152 | }
153 | //delete space before break page
154 | if($type === 2) {
155 | $this->zip->addFromString($part['PartName'], $this->SimSunExtBSupport($this->autoDeleteSpaceBeforeBreakPage($part['DOMElement']->saveXML())));
156 | }else{
157 | //add MDword flag
158 | if($type === 9) {
159 | $description = $part['DOMElement']->getElementsByTagName('description')->item(0);
160 | if(is_null($description)) {
161 | $el = $part['DOMElement']->createElement('dc:description','Made with MDword');
162 | if($coreProperties = $part['DOMElement']->getElementsByTagName('coreProperties')->item(0)) {
163 | $coreProperties->appendChild($el);
164 | }
165 | }else{
166 | $description->nodeValue='Made with MDword';
167 | }
168 |
169 | }
170 | $this->zip->addFromString($part['PartName'], $this->SimSunExtBSupport($part['DOMElement']->saveXML()));
171 | }
172 | }
173 | }
174 | }
175 |
176 | foreach($this->files as $part) {
177 | if(isset($part['this'])) {
178 | $this->zip->addFromString($part['PartName'], $part['this']->getContent());
179 | }else{
180 | $this->zip->deleteName($part['PartName']);
181 | }
182 | }
183 |
184 | $this->zip->addFromString('[Content_Types].xml', $this->Content_Types->DOMDocument->saveXML());
185 |
186 | if (false === $this->zip->close()) {
187 | throw new \Exception('Could not close zip file.');
188 | }
189 |
190 | // trigger_error('debug',E_USER_ERROR);exit;
191 |
192 | if(MDWORD_DEBUG === true) {
193 | $this->zip->open($this->tempDocumentFilename);
194 | $this->zip->extractTo(MDWORD_GENERATED_DIRECTORY);
195 | $this->zip->close();
196 | }
197 |
198 | return $this->tempDocumentFilename;
199 | }
200 |
201 | public function saveForTrace()
202 | {
203 | foreach($this->parts as $type => $list ) {
204 | foreach($list as $part) {
205 | if(is_object($part['DOMElement'])) {
206 | //delete document end space
207 | if($type === 2) {
208 | $this->zip->addFromString($part['PartName'], $this->autoDeleteSpaceBeforeBreakPage($part['DOMElement']->saveXML()));
209 | }else{
210 | $this->zip->addFromString($part['PartName'], $part['DOMElement']->saveXML());
211 | }
212 | }
213 | }
214 | }
215 |
216 | foreach($this->files as $part) {
217 | if(isset($part['this'])) {
218 | $this->zip->addFromString($part['PartName'], $part['this']->getContent());
219 | }else{
220 | $this->zip->deleteName($part['PartName']);
221 | }
222 | }
223 |
224 | $this->zip->addFromString('[Content_Types].xml', $this->Content_Types->DOMDocument->saveXML());
225 |
226 |
227 | $this->zip->open($this->tempDocumentFilename);
228 | $this->zip->extractTo(MDWORD_GENERATED_DIRECTORY);
229 |
230 | return $this->tempDocumentFilename;
231 | }
232 |
233 |
234 | public function standardXml($xml,$ContentType,$PartName) {
235 | $xml = preg_replace_callback('/\$[^$]*?\{[\s\S]+?\}/i', function($match){
236 | return preg_replace('/\s/', '', strip_tags($match[0]));
237 | }, $xml);
238 |
239 | static $commentId = 0;
240 | $nameToCommendId = [];
241 | $xml = preg_replace_callback('/(<[w|m]\:r[> ](?:(?!<[w|m]:r[> ])[\S\s])*?<[w|m]\:t[ ]{0,1}[^>]*?>)([^><]*?)(<\/[w|m]\:t>[\s\S]*?<\/[w|m]\:r>)/i', function($matchs) use(&$commentId,&$nameToCommendId,$ContentType,$PartName){
242 | return preg_replace_callback('/\$\{([\s\S]+?)\}/i', function($match) use(&$commentId,&$nameToCommendId,$matchs,$ContentType,$PartName){
243 | $name = $match[1];
244 | $length = strlen($name);
245 | if($name[$length-1] === '/') {
246 | $name = trim($name,'/');
247 | $this->blocks[$ContentType][$PartName]['r'.$commentId] = $name;
248 | return $matchs[3].''.$matchs[1].$match[0].$matchs[3].''.$matchs[1];
249 | //end
250 | }elseif($name[0] === '/') {
251 | $name = trim($name,'/');
252 | return $match[0].$matchs[3].''.$matchs[1];
253 | //start
254 | }else{
255 | $name = trim($name,'/');
256 | $this->blocks[$ContentType][$PartName]['r'.$commentId] = $name;
257 | $nameToCommendId[$name] = $commentId;
258 | return $matchs[3].''.$matchs[1].$match[0];
259 | }
260 |
261 | }, $matchs[0]);
262 | }, $xml);
263 |
264 | return $xml;
265 | }
266 |
267 | private function autoDeleteSpaceBeforeBreakPage($xml) {
268 | ini_set("pcre.backtrack_limit",-1);//回溯bug fixed,-1代表不做限制
269 | $xml = preg_replace_callback('/\<\/w\:p\>(?:\(?:(?!\<\/w\:t\>|\|\)*?\]*?\>\\\<\/w\:r\>\<\/w\:p\>/i', function($match) {
270 |
271 | preg_match_all('/\]*?\>(.+?)\<\/w\:p\>/i', $match[0],$subMatch);
272 | if(!empty($subMatch[1])) {
273 | return implode('', $subMatch[1]).'';
274 | }
275 |
276 | return '';
277 | }, $xml);
278 |
279 | $xml = preg_replace('/\\/','',$xml);
280 |
281 | return $xml;
282 | }
283 |
284 | private function SimSunExtBSupport($xml) {
285 | $fontMap = [
286 | 0=>'',
287 | 1=>''
288 | ];
289 |
290 | return preg_replace_callback('/(]+?>|>))(?:(?!]+?>|>)).)*?\{%\{\d\}%\}(?:(?!]+?>|>)).)*?(<\/w:r>)/i',function($match) use($fontMap) {
291 | preg_match('/(]+?>|>)[\s\S]+?)()([\s\S]*<\/w:rPr>)|(]+?>|>)[\s\S]+?)<\/w:rPr>/i',$match[0],$fontMatch);
292 | if(isset($fontMatch[0])) {
293 | if(isset($fontMatch[4])) {
294 | $rPrB = $fontMatch[4];
295 | $rPrE = '';
296 | $orgFont = '';
297 | }else{
298 | $rPrB = $fontMatch[1];
299 | $orgFont = $fontMatch[2];
300 | $rPrE = $fontMatch[3];
301 | }
302 | }else{
303 | $rPrB = '';
304 | $orgFont = '';
305 | $rPrE = '';
306 | }
307 |
308 | return preg_replace_callback('/\{%\{(\d)\}%\}([\s\S]+?)\{%\{\/\d\}%\}/i',function($match2) use($fontMap,$match,$orgFont,$rPrB,$rPrE) {
309 | $font = $fontMap[intval($match2[1])];
310 | return ''.
311 | $match[1].$rPrB.$font.$rPrE.''.$match2[2].''
312 | .$match[1].($orgFont===''?$rPrB.$rPrE:$rPrB.$orgFont.$rPrE).'';
313 | },$match[0]);
314 | },$xml);
315 | }
316 |
317 | private function deleteComments($remainComments = true) {
318 | if($remainComments) {
319 | $edit = $this->commentsEdit[0];
320 | $willDeleted = [];
321 | $usedCommentIds = $this->wordProcessor->getUsedCommentIds();
322 | $edit->treeToListCallback($edit->DOMDocument,function($node) use($edit,$usedCommentIds,&$willDeleted) {
323 | if($node->localName == 'comment' && isset($usedCommentIds[$edit->getAttr($node,'id')])) {
324 | $willDeleted[] = $node;
325 | }else{
326 | return $node;
327 | }
328 | });
329 |
330 | foreach($willDeleted as $node) {
331 | $edit->removeChild($node);
332 | }
333 | }else{
334 | $parts = [];
335 | if(isset($this->parts[15])) {
336 | $parts = array_merge($parts,$this->parts[15]);
337 | unset($this->parts[15]);
338 | }
339 | if(isset($this->parts[16])) {
340 | $parts = array_merge($parts,$this->parts[16]);
341 | unset($this->parts[16]);
342 | }
343 | if(isset($this->parts[17])) {
344 | $parts = array_merge($parts,$this->parts[17]);
345 | unset($this->parts[17]);
346 | }
347 | foreach($parts as $part) {
348 | $this->zip->deleteName($part['PartName']);
349 | }
350 | }
351 |
352 | foreach($this->needUpdateParts as $part) {
353 | $func = $part['func'];
354 | switch ($func) {
355 | case 'getHeaderEdit':
356 | $this->deleteMded($this->headerEdits[$part['partName']],$remainComments);
357 | break;
358 | case 'getDocumentEdit':
359 | $this->deleteMded($this->wordProcessor->getDocumentEdit(),$remainComments);
360 | break;
361 | case 'getFooterEdit':
362 | $this->deleteMded($this->footerEdits[$part['partName']],$remainComments);
363 | break;
364 | }
365 | }
366 |
367 | //test
368 | // echo $this->documentEdit->DOMDocument->saveXML();exit;
369 | }
370 |
371 | private function deleteMded($edit,$remainComments=false) {
372 | $deleteTags = [
373 | 'commentRangeStart'=>1,
374 | 'commentRangeEnd'=>1,
375 | 'commentReference'=>1
376 | ];
377 |
378 | $willDeleted = [];
379 | $usedCommentIds = $this->wordProcessor->getUsedCommentIds();
380 | if($remainComments) {
381 | $edit->treeToListCallback($edit->DOMDocument,function($node) use($edit,$deleteTags,$usedCommentIds,&$willDeleted) {
382 | if($edit->getAttr($node,'md',null)) {
383 | $willDeleted[] = $node;
384 | }else{
385 | $id = $edit->getAttr($node,'id');
386 | if(isset($deleteTags[$node->localName]) && $id[0] !== 'r' && isset($usedCommentIds[$id])) {
387 | $willDeleted[] = $node;
388 | }else{
389 | return $node;
390 | }
391 | }
392 | });
393 | }else{
394 | $edit->treeToListCallback($edit->DOMDocument,function($node) use($edit,$deleteTags,$usedCommentIds,&$willDeleted) {
395 | if($edit->getAttr($node,'md',null) || isset($deleteTags[$node->localName])) {
396 | $willDeleted[] = $node;
397 | }else{
398 | return $node;
399 | }
400 | });
401 | }
402 |
403 | foreach($willDeleted as $node) {
404 | $edit->removeChild($node);
405 | }
406 | }
407 |
408 | /**
409 | *
410 | * @param string $filename
411 | * @return \DOMDocument
412 | */
413 | public function getXmlDom($filename,$standardXmlFunc=null) {
414 | if(is_null($filename) && !is_null($standardXmlFunc)) {
415 | $xml = $standardXmlFunc();
416 | }else{
417 | $xml = $this->zip->getFromName($filename);
418 | if(!is_null($standardXmlFunc)) {
419 | $xml = $standardXmlFunc($xml);
420 | }
421 | }
422 | $domDocument = new \DOMDocument();
423 | $domDocument->loadXML($xml);
424 | if(MDWORD_DEBUG === true) {
425 | $domDocument->formatOutput = true;
426 | }
427 |
428 | return $domDocument;
429 | }
430 |
431 | protected function getZipFiles() {
432 | static $pathIndx = null;
433 |
434 | if(is_null($pathIndx)) {
435 | for($i=0;$i < $this->zip->numFiles; $i++) {
436 | $name = $this->zip->getNameIndex($i);
437 | $nameArr = explode('/', $name);
438 |
439 | $item = &$pathIndx;
440 | foreach ($nameArr as $pathOrName) {
441 | if(!isset($item[$pathOrName])) {
442 | $item[$pathOrName] = [];
443 | }
444 | $item = &$item[$pathOrName];
445 | }
446 | }
447 | unset($item);
448 | }
449 |
450 | return $pathIndx;
451 | }
452 |
453 | public function getChartParts(){
454 | if(isset($this->parts[13])){
455 | $chartPartsArr[13] = $this->getPartsDetail($this->parts[13]);
456 | }
457 | if(isset($this->parts[25])){
458 | $chartPartsArr[25] = $this->parts[25];
459 | }
460 | if(isset($this->parts[26])){
461 | $chartPartsArr[26] = $this->parts[26];
462 | }
463 | return $chartPartsArr;
464 | }
465 |
466 | public function getPartsDetail($parts){
467 | foreach($parts as &$part){
468 | $partInfo = pathinfo($part['PartName']);
469 | $partNameRel = $partInfo['dirname'].'/_rels/'.$partInfo['basename'].'.rels';
470 | $chartRelDom = $this->getXmlDom($partNameRel);
471 | if(!empty($chartRelDom)){
472 | $part['chartRelDom'] = $chartRelDom;
473 | }
474 | $Relationships = $chartRelDom->getElementsByTagName('Relationship');
475 | foreach($Relationships as $Relationship){
476 | $target = $Relationship->getAttribute('Target');
477 | if(strpos($target,'xlsx') != false){
478 | $part['embeddings']['xml'] = $this->zip->getFromName(str_replace('../','word/',$target));
479 | preg_match('/embeddings\/([\s\S]+)/',$target,$match);
480 | $part['embeddings']['name'] = $match[1];
481 | }
482 | }
483 | }
484 | return $parts;
485 | }
486 |
487 | public function setChartParts($chartparts){
488 | foreach($chartparts as $key=>&$part){
489 | if($key==13){
490 | if(!empty($this->parts[13])){
491 | foreach($this->parts[13] as $keypart){
492 | $partName = $keypart['PartName'];
493 | preg_match('/(\d+)/',$partName,$match);
494 | $chartKey = $match[1];
495 | }
496 | foreach($part as &$p){
497 | $chartKey++;
498 | preg_match('/(\d+)/',$p['PartName'],$partmatch);
499 | $p['PartName'] = str_replace($partmatch[1],$chartKey,$p['PartName']);
500 | $styleKey[$partmatch[1]] = $chartKey;
501 | }
502 | foreach($chartparts[25] as &$stylepart){
503 | preg_match('/(\d+)/',$stylepart['PartName'],$styleMatch);
504 | $stylepart['PartName'] = str_replace($styleMatch[1],$styleKey[$styleMatch[1]],$stylepart['PartName']);
505 | }
506 | foreach($chartparts[26] as &$colorepart){
507 | preg_match('/(\d+)/',$colorepart['PartName'],$colorMatch);
508 | $colorepart['PartName'] = str_replace($colorMatch[1],$styleKey[$colorMatch[1]],$colorepart['PartName']);
509 | }
510 | $this->parts[13] = array_merge($this->parts[13],$chartparts[13]);
511 | }else{
512 | $this->parts[13] = $chartparts[13];
513 | }
514 | }else{
515 | $this->parts[$key] = empty($this->parts[$key])?$chartparts[$key]:array_merge($this->parts[$key],$chartparts[$key]);
516 | }
517 | }
518 | }
519 |
520 | public function getContentTypes(){
521 | $contentTypes = $this->Content_Types->overrides;
522 |
523 | return $contentTypes;
524 | }
525 |
526 | public function getChartEmbeddings(){
527 | $fileList = $this->getZipFiles();
528 | $relArr = $fileList['word']['charts']['_rels'];
529 | $embeddingsList = $fileList['word']['embeddings'];
530 | //$this->documentEdit->setChartRelValue($relArr);
531 | foreach($embeddingsList as $embeddingKey=>$val){
532 | $embeddingsXml[$embeddingKey] = $this->zip->getFromName('word/embeddings/'.$embeddingKey);
533 | }
534 | return $embeddingsXml;
535 | }
536 |
537 | public function setContentTypes(){
538 | foreach($this->parts[13] as $part){
539 | $partArr['PartName'] = $part['PartName'];
540 | $partArr['ContentType'] = 13;
541 | if(array_search($partArr,$this->Content_Types->overrides) === false){
542 | $newOverrides[] = $partArr;
543 | }
544 | }
545 | foreach($this->parts[25] as $part){
546 | $partArr['PartName'] = $part['PartName'];
547 | $partArr['ContentType'] = 25;
548 | if(array_search($partArr,$this->Content_Types->overrides) === false){
549 | $newOverrides[] = $partArr;
550 | }
551 | }
552 | foreach($this->parts[26] as $part){
553 | $partArr['PartName'] = $part['PartName'];
554 | $partArr['ContentType'] = 26;
555 | if(array_search($partArr,$this->Content_Types->overrides) === false){
556 | $newOverrides[] = $partArr;
557 | }
558 | }
559 | $this->Content_Types->setContent_types($newOverrides);
560 | $this->writeContentTypeXml();
561 |
562 | }
563 |
564 | public function writeContentTypeXml(){
565 | $Relationships = $this->Content_Types->DOMDocument->getElementsByTagName('Override');
566 | foreach($this->Content_Types->overrides as $key=>$overrides){
567 | if(!isset($Relationships[$key])){
568 | $copy = clone $Relationships[0];
569 | $copy->setAttribute('PartName','/'.$overrides['PartName']);
570 | $copy->setAttribute('ContentType',$this->Content_Types->contentTypes[$overrides['ContentType']]);
571 | $Relationships[0]->parentNode->appendChild($copy);
572 | }
573 | }
574 | $this->zip->addFromString('[Content_Types].xml', $this->Content_Types->DOMDocument->saveXML());
575 | }
576 |
577 |
578 | public function updateChartRel(){
579 | foreach($this->parts[13] as $part){
580 | if(isset($part['chartRelDom'])){
581 | $Relationships = $part['chartRelDom']->getElementsByTagName('Relationship');
582 | preg_match('/chart(\d+)/',$part['PartName'],$match);
583 | foreach($Relationships as $Relationship){
584 | $target = $Relationship->getAttribute('Target');
585 | $Relationship->setAttribute('Target',preg_replace('/(\d+)/',$match[1],$target));
586 | }
587 | $relArr['PartName'] = $part['PartName'];
588 | $relArr['relName'] = 'word/charts/_rels/'.$match[0].'.xml.rels';
589 | $relArr['dom'] = $part['chartRelDom'];
590 |
591 | $this->documentEdit->setChartRel($relArr);
592 | }
593 | }
594 | }
595 |
596 | public function free() {
597 | unset($this->Content_Types->word);
598 | unset($this->Content_Types);
599 |
600 | unset($this->zip,$this->log);
601 | $this->documentEdit->free();
602 |
603 | unset($this->documentEdit->DOMDocument);
604 | unset($this->documentEdit->commentsEdit);
605 |
606 | unset($this->commentsEdit);
607 | unset($this->stylesEdit);
608 |
609 | //fixed bug:not use unset
610 | // unset($this->documentEdit);
611 | $this->documentEdit = null;
612 | }
613 | }
614 |
--------------------------------------------------------------------------------
/src/WordProcessor.php:
--------------------------------------------------------------------------------
1 | wordProcessor = $this;
27 | $reader->load($zip);
28 | $this->words[++$this->wordsIndex] = $reader;
29 |
30 | $this->words[$this->wordsIndex]->commentsEdit = [];
31 | foreach ($this->words[$this->wordsIndex]->parts[15] as $part15) {
32 | $comments = $part15['DOMElement'];
33 | $Comment = new Comments($this->words[$this->wordsIndex],$comments);
34 | $Comment->partName = $part15['PartName'];
35 | $Comment->word = $this->words[$this->wordsIndex];
36 | $this->words[$this->wordsIndex]->commentsEdit[] = $Comment;
37 | }
38 |
39 | return $this->words[$this->wordsIndex];
40 | }
41 |
42 | /**
43 | * @return \MDword\Common\Bind
44 | */
45 | public function getBind($data) {
46 | $bind = new Bind($this,$data);
47 | return $bind;
48 | }
49 |
50 | public function setValue($name, $value, $type=MDWORD_TEXT) {
51 | $updateCount = 0;
52 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
53 | $func = $part['func'];
54 | $partName = $part['partName'];
55 | /**
56 | * @var Document $documentEdit
57 | */
58 | $documentEdit = $this->$func($partName);
59 | $updateCount += $documentEdit->setValue($name, $value, $type);
60 | }
61 |
62 | return $updateCount;
63 | }
64 |
65 | public function setValues($values,$pre='') {
66 | $updateCount = 0;
67 | foreach ($values as $index => $valueArr) {
68 | foreach($valueArr as $name => $value) {
69 | if(is_array($value)) {
70 | $updateCount += $this->setValues($value,'#'.$index);
71 | }else{
72 | $updateCount += $this->setValue($name.$pre.'#'.$index, $value);
73 | }
74 | }
75 | }
76 | return $updateCount;
77 | }
78 |
79 | public function getInnerVars() {
80 | return ['levels'=>$this->words[$this->wordsIndex]->documentEdit->getLevels()];
81 | }
82 |
83 | /**
84 | * delete section include the block。(only in document.xml)
85 | * @param string $name
86 | */
87 | public function deleteSection($name) {
88 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
89 | $func = $part['func'];
90 | $partName = $part['partName'];
91 | /**
92 | * @var Document $documentEdit
93 | */
94 | $documentEdit = $this->$func($partName);
95 | $documentEdit->setValue($name, 'section',MDWORD_DELETE);
96 | }
97 | }
98 |
99 | /**
100 | * delete p include the block
101 | * @param string $name
102 | */
103 | public function deleteP($name) {
104 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
105 | $func = $part['func'];
106 | $partName = $part['partName'];
107 | /**
108 | * @var Document $documentEdit
109 | */
110 | $documentEdit = $this->$func($partName);
111 | $documentEdit->setValue($name, 'p',MDWORD_DELETE);
112 | }
113 | }
114 |
115 | /**
116 | * delete tbl include the block
117 | * @param string $name
118 | */
119 | public function deleteTbl($name) {
120 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
121 | $func = $part['func'];
122 | $partName = $part['partName'];
123 | /**
124 | * @var Document $documentEdit
125 | */
126 | $documentEdit = $this->$func($partName);
127 | $documentEdit->setValue($name, 'tbl',MDWORD_DELETE);
128 | }
129 | }
130 |
131 | /**
132 | * delete tr include the block
133 | * @param string $name
134 | */
135 | public function deleteTr($name) {
136 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
137 | $func = $part['func'];
138 | $partName = $part['partName'];
139 | /**
140 | * @var Document $documentEdit
141 | */
142 | $documentEdit = $this->$func($partName);
143 | $documentEdit->setValue($name, 'tr',MDWORD_DELETE);
144 | }
145 | }
146 |
147 | /**
148 | * delete block
149 | * @param string $name
150 | */
151 | public function delete($name) {
152 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
153 | $func = $part['func'];
154 | $partName = $part['partName'];
155 | /**
156 | * @var Document $documentEdit
157 | */
158 | $documentEdit = $this->$func($partName);
159 | $documentEdit->setValue($name, '',MDWORD_TEXT);
160 | }
161 | }
162 |
163 | public function setImageValue($name, $value) {
164 | if(strlen($name) === 32) {//media md5
165 | $includeImageEdits = [];
166 |
167 | $documentEdit = $this->getDocumentEdit();
168 | $rids = $documentEdit->getRidByMd5($name);
169 | if(!empty($rids)) {
170 | $includeImageEdits[] = [$documentEdit,$rids];
171 | }
172 |
173 | foreach ($this->words[$this->wordsIndex]->parts[22] as $part) {
174 | $partName = $part['PartName'];
175 | $headerEdit = $this->getHeaderEdit($partName);
176 | $rids = $headerEdit->getRidByMd5($name);
177 | if(!empty($rids)) {
178 | $includeImageEdits[] = [$headerEdit,$rids];
179 | $this->words[$this->wordsIndex]->needUpdateParts[$partName] = ['func'=>'getHeaderEdit','partName'=>$partName];
180 | }
181 | }
182 |
183 | foreach ($this->words[$this->wordsIndex]->parts[23] as $part) {
184 | $partName = $part['PartName'];
185 | $footerEdit = $this->getFooterEdit($partName);
186 | $rids = $footerEdit->getRidByMd5($name);
187 | if(!empty($rids)) {
188 | $includeImageEdits[] = [$footerEdit,$rids];
189 | $this->words[$this->wordsIndex]->needUpdateParts[$partName] = ['func'=>'getFooterEdit','partName'=>$partName];
190 | }
191 | }
192 |
193 | $edit = null;
194 | foreach($includeImageEdits as list($edit,$rids)) {
195 | /**
196 | * @var Document $edit
197 | */
198 | if (is_array($value)) {
199 | $edit->setValue($rids, $value, MDWORD_TEXT);
200 | }else{
201 | $edit->setValue($rids, $value, MDWORD_IMG);
202 | }
203 | }
204 |
205 | return ;
206 | }
207 |
208 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
209 | $func = $part['func'];
210 | $partName = $part['partName'];
211 | /**
212 | * @var Document $documentEdit
213 | */
214 | $documentEdit = $this->$func($partName);
215 | $documentEdit->setValue($name, $value,MDWORD_IMG);
216 | }
217 | }
218 |
219 | /**
220 | * @param string $name
221 | * ['text','link']
222 | * @param array $value
223 | */
224 | public function setLinkValue($name, $value) {
225 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
226 | $func = $part['func'];
227 | $partName = $part['partName'];
228 | /**
229 | * @var Document $documentEdit
230 | */
231 | $documentEdit = $this->$func($partName);
232 | $documentEdit->setValue($name, $value[0],MDWORD_TEXT);
233 | $documentEdit->setValue($name, $value[1],MDWORD_LINK);
234 | }
235 | }
236 |
237 | // /**
238 | // * @param string $name
239 | // * @param array $datas
240 | // * change value ['A1',9,'set']
241 | // * extention range ['$A$1:$A$5','$A$1:$A$10','ext']
242 | // */
243 | // public function setExcelValues($name='',$datas=[]) {
244 | // $documentEdit = $this->getDocumentEdit();
245 | // $documentEdit->setValue($name, $datas, 'excel');
246 | // }
247 |
248 | /**
249 | * clone p
250 | * @param string $name
251 | * @param int $count
252 | */
253 | public function cloneP($name,$count=1) {
254 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
255 | $func = $part['func'];
256 | $partName = $part['partName'];
257 | /**
258 | * @var Document $documentEdit
259 | */
260 | $documentEdit = $this->$func($partName);
261 | $documentEdit->setValue($name, $count, MDWORD_CLONEP);
262 | }
263 | }
264 | /**
265 | * clone section
266 | * @param string $name
267 | * @param int|array $count 1|[1,$nameTo]
268 | */
269 | public function cloneSection($name,$count=1) {
270 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
271 | $func = $part['func'];
272 | $partName = $part['partName'];
273 | /**
274 | * @var Document $documentEdit
275 | */
276 | $documentEdit = $this->$func($partName);
277 | $documentEdit->setValue($name, $count, MDWORD_CLONESECTION);
278 | }
279 | }
280 | /**
281 | * clone
282 | * @param string $name
283 | * @param int $count
284 | */
285 | public function clones($name,$count=1) {
286 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
287 | $func = $part['func'];
288 | $partName = $part['partName'];
289 | /**
290 | * @var Document $documentEdit
291 | */
292 | $documentEdit = $this->$func($partName);
293 | $documentEdit->setValue($name, $count, MDWORD_CLONE);
294 | }
295 | }
296 | /**
297 | * clone
298 | * @param string $name
299 | * @param int $count
300 | */
301 | public function cloneTo($nameTo,$name) {
302 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
303 | $func = $part['func'];
304 | $partName = $part['partName'];
305 | /**
306 | * @var Document $documentEdit
307 | */
308 | $documentEdit = $this->$func($partName);
309 | $documentEdit->setValue($nameTo, $name, MDWORD_CLONETO);
310 | }
311 | }
312 |
313 |
314 | public function setBreakValue($name, $value) {
315 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
316 | $func = $part['func'];
317 | $partName = $part['partName'];
318 | /**
319 | * @var Document $documentEdit
320 | */
321 | $documentEdit = $this->$func($partName);
322 | $documentEdit->setValue($name, $value,MDWORD_BREAK);
323 | }
324 | }
325 |
326 |
327 | public function setBreakPageValue($name, $value=1) {
328 | foreach($this->words[$this->wordsIndex]->needUpdateParts as $part) {
329 | $func = $part['func'];
330 | $partName = $part['partName'];
331 | /**
332 | * @var Document $documentEdit
333 | */
334 | $documentEdit = $this->$func($partName);
335 | $documentEdit->setValue($name, $value,MDWORD_PAGE_BREAK);
336 | }
337 | }
338 |
339 | /**
340 | * update toc
341 | */
342 | public function updateToc() {
343 | $documentEdit = $this->getDocumentEdit();
344 | $documentEdit->updateToc();
345 | }
346 |
347 | /**
348 | * @var Document
349 | */
350 | public function getHeaderEdit($partName='word/header1.xml') {
351 | if(!isset($this->words[$this->wordsIndex]->headerEdits[$partName])) {
352 | $index = $this->getPartsIndexByPartName(22,$partName);
353 | $document = $this->words[$this->wordsIndex]->parts[22][$index]['DOMElement'];
354 | $blocks = $this->words[$this->wordsIndex]->blocks[22][$partName];//header not add comment
355 | $blocks = $blocks?$blocks:[];
356 | $headerEdit = new Header($this->words[$this->wordsIndex],$document,$blocks);
357 | $this->words[$this->wordsIndex]->headerEdits[$partName] = $headerEdit;
358 | $this->words[$this->wordsIndex]->headerEdits[$partName]->partName = $this->words[$this->wordsIndex]->parts[22][$index]['PartName'];
359 | }else{
360 | $headerEdit = $this->words[$this->wordsIndex]->headerEdits[$partName];
361 | }
362 |
363 | return $headerEdit;
364 | }
365 |
366 | public function getDocumentEdit($partName='word/document.xml') {
367 | if(is_null($this->words[$this->wordsIndex]->documentEdit)) {
368 | $index = $this->getPartsIndexByPartName(2,$partName);
369 | $document = $this->words[$this->wordsIndex]->parts[2][$index]['DOMElement'];
370 | if(isset($this->words[$this->wordsIndex]->blocks[2]) && isset($this->words[$this->wordsIndex]->blocks[2][$partName])) {
371 | $blocks = $this->words[$this->wordsIndex]->blocks[2][$partName];
372 | }else{
373 | $blocks = [];
374 | }
375 |
376 | foreach($this->words[$this->wordsIndex]->commentsEdit as $comments) {
377 | if($comments->partName === 'word/comments.xml') {
378 | if(isset($comments->blocks)) {
379 | $blocks = $this->my_array_merge($blocks,$comments->blocks);
380 | }
381 | }
382 | }
383 | $documentEdit = new Document($this->words[$this->wordsIndex],$document,$blocks);
384 | $this->words[$this->wordsIndex]->documentEdit = $documentEdit;
385 | $this->words[$this->wordsIndex]->documentEdit->partName = $this->words[$this->wordsIndex]->parts[2][$index]['PartName'];
386 | }else{
387 | $documentEdit = $this->words[$this->wordsIndex]->documentEdit;
388 | }
389 |
390 | return $documentEdit;
391 | }
392 |
393 | /**
394 | * @var Document
395 | */
396 | public function getFooterEdit($partName='word/footer1.xml') {
397 | if(!isset($this->words[$this->wordsIndex]->footerEdits[$partName])) {
398 | $index = $this->getPartsIndexByPartName(23,$partName);
399 | $document = $this->words[$this->wordsIndex]->parts[23][$index]['DOMElement'];
400 | $blocks = $this->words[$this->wordsIndex]->blocks[23][$partName];//footer not add comment
401 | $blocks = $blocks?$blocks:[];
402 | // var_dump($partName,$document,$blocks);
403 | $footerEdit = new Footer($this->words[$this->wordsIndex],$document,$blocks);
404 | $this->words[$this->wordsIndex]->footerEdits[$partName] = $footerEdit;
405 | $this->words[$this->wordsIndex]->footerEdits[$partName]->partName = $this->words[$this->wordsIndex]->parts[23][$index]['PartName'];
406 | }else{
407 | $footerEdit = $this->words[$this->wordsIndex]->footerEdits[$partName];
408 | }
409 | return $footerEdit;
410 | }
411 |
412 | private function getPartsIndexByPartName($type,$partName) {
413 | static $data = [];
414 |
415 | if(!isset($data[$partName])) {
416 | $data = [];
417 | foreach($this->words[$this->wordsIndex]->parts as $part) {
418 | foreach($part as $index => $val) {
419 | $data[$val['PartName']] = $index;
420 | }
421 | }
422 | }
423 |
424 | return $data[$partName];
425 | }
426 |
427 | /**
428 | * @return Styles
429 | */
430 | public function getStylesEdit() {
431 | $stylesEdit = $this->words[$this->wordsIndex]->stylesEdit;
432 | if(is_null($stylesEdit)) {
433 | $document = $this->words[$this->wordsIndex]->parts[4][0]['DOMElement'];
434 | $stylesEdit = new Styles($this->words[$this->wordsIndex],$document);
435 | $this->words[$this->wordsIndex]->stylesEdit = $stylesEdit;
436 | $this->words[$this->wordsIndex]->stylesEdit->partName = $this->words[$this->wordsIndex]->parts[4][0]['PartName'];
437 | }
438 |
439 | return $stylesEdit;
440 | }
441 |
442 | /**
443 | * @return Numbering
444 | */
445 | public function getNumberingEdit() {
446 | $numberingEdit = $this->words[$this->wordsIndex]->numberingEdit;
447 | if(is_null($numberingEdit) && isset($this->words[$this->wordsIndex]->parts[12])) {
448 | $document = $this->words[$this->wordsIndex]->parts[12][0]['DOMElement'];
449 | $numberingEdit = new Numbering($this->words[$this->wordsIndex],$document);
450 | $this->words[$this->wordsIndex]->numberingEdit = $numberingEdit;
451 | $this->words[$this->wordsIndex]->numberingEdit->partName = $this->words[$this->wordsIndex]->parts[12][0]['PartName'];
452 | }
453 |
454 | return $numberingEdit;
455 | }
456 |
457 | public function saveAs($fileName,$remainComments=false,$autoClean=true)
458 | {
459 | $tempFileName = $this->words[$this->wordsIndex]->save($remainComments);
460 | if (file_exists($fileName)) {
461 | unlink($fileName);
462 | }
463 |
464 | file_put_contents($fileName, file_get_contents($tempFileName));
465 | unlink($tempFileName);
466 |
467 | if($autoClean === true) {
468 | $this->free();
469 | }
470 | }
471 |
472 | public function saveAsContent() {
473 | $tempFileName = $this->words[$this->wordsIndex]->save();
474 | $content = file_get_contents($tempFileName);
475 | unlink($tempFileName);
476 | return $content;
477 | }
478 |
479 | public function saveAsToPathForTrace($dir,$baseName)
480 | {
481 | static $idx = 0;
482 | $word = $this->words[$this->wordsIndex];
483 | $tempFileName = $word->saveForTrace();
484 |
485 | $fileName = $dir.'/'.$baseName.'-'.str_pad($idx++,3,"0",STR_PAD_LEFT).'.docx';
486 |
487 | if (file_exists($fileName)) {
488 | unlink($fileName);
489 | }
490 | copy($tempFileName, $fileName);
491 |
492 | $WordProcessor = new WordProcessor();
493 | $WordProcessor->isForTrace = true;
494 | $WordProcessor->load($fileName);
495 | $WordProcessor->saveAs($fileName);
496 | }
497 |
498 | public function setChartValue($name,$fileName)
499 | {
500 | $reader = new Word();
501 | $reader->load($fileName);
502 | $this->words[++$this->wordsIndex] = $reader;
503 | $documentEdit = $this->getDocumentEdit();
504 |
505 | $documentChart = $documentEdit->getDocumentChart();
506 |
507 | $chartparts = $this->words[$this->wordsIndex]->getChartParts();
508 | $embeddings = $this->words[$this->wordsIndex]->getChartEmbeddings();
509 |
510 | $this->words[--$this->wordsIndex]->setChartParts($chartparts);
511 | $documentEdit = $this->getDocumentEdit();
512 | $documentEdit->setDocumentChart($name,$documentChart);
513 | $this->words[$this->wordsIndex]->updateChartRel();
514 | $this->words[$this->wordsIndex]->setContentTypes();
515 | $this->setEmbeddings();
516 |
517 | }
518 |
519 | public function setEmbeddings(){
520 | $this->words[$this->wordsIndex]->parts[13];
521 | foreach($this->words[$this->wordsIndex]->parts[13] as $part){
522 | if(!empty($part['embeddings'])){
523 | preg_match('/(\d+)/',$part['PartName'],$match);
524 | $fileName = preg_replace('/(\d+)/',$match[1],$part['embeddings']['name']);
525 | $this->words[$this->wordsIndex]->zip->addFromString('word/embeddings/'.$fileName, $part['embeddings']['xml']);
526 | }
527 | }
528 | }
529 |
530 | public function my_array_merge($arr,$arr2) {
531 | foreach($arr2 as $key => $val) {
532 | $arr[$key] = $val;
533 | }
534 |
535 | return $arr;
536 | }
537 |
538 | public function getMedies() {
539 | $word = $this->words[$this->wordsIndex];
540 | $numFiles = $word->zip->numFiles;
541 | $showList = [];
542 | for ($i = 0; $i < $numFiles; $i++) {
543 | $name = $word->zip->getNameIndex($i);
544 | if(strpos($name, 'media') > 0) {
545 | $content = $word->zip->getFromIndex($i);
546 | $showList['medias'][] = [
547 | 'md5' => md5($content),
548 | 'name' => $word->zip->getNameIndex($i),
549 | 'content' => $content,
550 | ];
551 | }
552 | }
553 |
554 | return $showList['medias'];
555 | }
556 |
557 | public function showMedies() {
558 | $showList = $this->getMedies();
559 |
560 | foreach($showList as $medias) {
561 | foreach($medias as $media) {
562 | var_dump($media);
563 | echo '.')
';
564 | }
565 |
566 | }
567 | }
568 |
569 | public function getBlockList() {
570 | /**
571 | * @var Word $word
572 | */
573 | $word = $this->words[$this->wordsIndex];
574 | $tree = [];
575 | foreach($word->parts as $type => $parts) {
576 |
577 | switch ($type) {
578 | case 2:
579 | $func = 'getDocumentEdit';
580 | break;
581 | case 22:
582 | $func = 'getHeaderEdit';
583 | break;
584 | case 23:
585 | $func = 'getFooterEdit';
586 | break;
587 | default:
588 | continue 2;
589 | break;
590 | }
591 |
592 | foreach($parts as $part) {
593 | /**
594 | * @var Document $document
595 | */
596 | $document = $this->$func($part['PartName']);
597 | $tree[$type][] = $document->getBlockTree();
598 | }
599 | }
600 |
601 | return $tree;
602 | }
603 |
604 | public function getUsedCommentIds() {
605 | /**
606 | * @var Word $word
607 | */
608 | $word = $this->words[$this->wordsIndex];
609 | static $r = null;
610 |
611 | if($r !== null) {
612 | return $r;
613 | }
614 |
615 | $r = [];
616 |
617 | foreach($word->parts as $type => $parts) {
618 |
619 | switch ($type) {
620 | case 2:
621 | $func = 'getDocumentEdit';
622 | break;
623 | case 22:
624 | $func = 'getHeaderEdit';
625 | break;
626 | case 23:
627 | $func = 'getFooterEdit';
628 | break;
629 | default:
630 | continue 2;
631 | break;
632 | }
633 |
634 | foreach($parts as $part) {
635 | /**
636 | * @var Document $document
637 | */
638 | $document = $this->$func($part['PartName']);
639 | foreach($document->commentsblocks as $id => $comment) {
640 | if(isset($document->usedBlock[$comment])) {
641 | $r[$id] = 1;
642 | }
643 | }
644 | }
645 | }
646 |
647 | return $r;
648 | }
649 |
650 | public function free(){
651 | foreach($this->words as $key => $word) {
652 | $word->free();
653 | unset($this->words[$key]);
654 | }
655 | }
656 |
657 | //删除tmp下文件
658 | public function deleteTmpFile(){
659 | $word = $this->words[$this->wordsIndex];
660 | $word->deleteTmpFile();
661 | }
662 | }
663 |
--------------------------------------------------------------------------------
/src/XmlTemple/XmlFromPhpword.php:
--------------------------------------------------------------------------------
1 | phpWord = $phpWord;
21 | $this->document = $document;
22 |
23 | // Create parts
24 | $this->parts = array(
25 | 'ContentTypes' => '[Content_Types].xml',
26 | 'Rels' => '_rels/.rels',
27 | 'DocPropsApp' => 'docProps/app.xml',
28 | 'DocPropsCore' => 'docProps/core.xml',
29 | 'DocPropsCustom' => 'docProps/custom.xml',
30 | 'RelsDocument' => 'word/_rels/document.xml.rels',
31 | 'Document' => 'word/document.xml',
32 | 'Comments' => 'word/comments.xml',
33 | 'Styles' => 'word/styles.xml',
34 | 'Numbering' => 'word/numbering.xml',
35 | 'Settings' => 'word/settings.xml',
36 | 'WebSettings' => 'word/webSettings.xml',
37 | 'FontTable' => 'word/fontTable.xml',
38 | 'Theme' => 'word/theme/theme1.xml',
39 | 'RelsPart' => '',
40 | 'Header' => '',
41 | 'Footer' => '',
42 | 'Footnotes' => '',
43 | 'Endnotes' => '',
44 | 'Chart' => '',
45 | );
46 | foreach (array_keys($this->parts) as $partName) {
47 | $partClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Part\\' . $partName;
48 | if (class_exists($partClass)) {
49 | /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $part Type hint */
50 | $part = new $partClass();
51 | $part->setParentWriter($this);
52 | $this->writerParts[strtolower($partName)] = $part;
53 | }
54 | }
55 | }
56 |
57 | private function writeStyles() {
58 | $skipTags = ['Normal'=>1,'Footnote Reference'=>1];
59 | $stylesEdit = $this->document->word->wordProcessor->getStylesEdit();
60 |
61 | $stylesXml = $this->getPartXml('styles');
62 | $stylesNode = $stylesEdit->createNodeByXml($stylesXml,function($documentElement) {
63 | return $documentElement;
64 | });
65 |
66 | $names = [];
67 | $styles = $stylesNode->getElementsByTagName('style');
68 | foreach($styles as $key => $style) {
69 | $nameNode = $style->getElementsByTagName('name')->item(0);
70 | $name = $stylesEdit->getAttr($nameNode, 'val');
71 | if(isset($skipTags[$name])) {
72 | continue;
73 | }
74 |
75 | $newName = 'PHPWORD'.$key.$name;
76 | $names[$name] = $newName;
77 | $stylesEdit->setAttr($nameNode, 'val', $newName);
78 |
79 | $copy = clone $style;
80 | $stylesEdit->DOMDocument->documentElement->appendChild($copy);
81 | }
82 |
83 | return $names;
84 | }
85 |
86 | private function writeImages() {
87 | $sectionMedia = Media::getElements('section');
88 | $rids = [];
89 | foreach($sectionMedia as $media) {
90 | if($media['type'] === 'image') {
91 | $refInfo = $this->document->updateRef($media['source'],null,MDWORD_IMG);
92 | $rids['r:id="rId'.($media['rID']+6).'"'] = 'r:id="'.$refInfo['rId'].'"';
93 | }
94 | }
95 |
96 | return $rids;
97 | }
98 |
99 | private function getPartXml($partName) {
100 | $xml = $this->writerParts[$partName]->write();
101 | return $xml;
102 | }
103 |
104 | public function createNodesByBodyXml() {
105 | $styleNames = $this->writeStyles();
106 | $imageRids = $this->writeImages();
107 |
108 | $xml = $this->getPartXml('document');
109 | $xml = str_replace(
110 | array_merge(array_keys($styleNames),array_keys($imageRids)),
111 | array_merge(array_values($styleNames),array_values($imageRids)),
112 | $xml);
113 | $body = $this->document->createNodeByXml($xml,function($documentElement) {
114 | return $documentElement->getElementsByTagName('body')->item(0);
115 | });
116 |
117 | $sectPrs = $body->getElementsByTagName('sectPr');
118 | if($sectPrs->length > 0) {
119 | $this->document->markDelete($sectPrs->item($sectPrs->length-1));
120 | }
121 |
122 | return $body->childNodes;
123 | }
124 |
125 | /**
126 | * Save document by name.
127 | *
128 | * @param string $filename
129 | */
130 | public function save($filename = null)
131 | {
132 | $filename = $this->getTempFile($filename);
133 | $zip = $this->getZipArchive($filename);
134 | $phpWord = $this->getPhpWord();
135 | // var_dump($this->parts);exit;
136 |
137 | // Content types
138 | $this->contentTypes['default'] = array(
139 | 'rels' => 'application/vnd.openxmlformats-package.relationships+xml',
140 | 'xml' => 'application/xml',
141 | );
142 |
143 | // Add section media files
144 | $sectionMedia = Media::getElements('section');
145 | if (!empty($sectionMedia)) {
146 | $this->addFilesToPackage($zip, $sectionMedia);
147 | $this->registerContentTypes($sectionMedia);
148 | foreach ($sectionMedia as $element) {
149 | $this->relationships[] = $element;
150 | }
151 | }
152 |
153 | // Add header/footer media files & relations
154 | $this->addHeaderFooterMedia($zip, 'header');
155 | $this->addHeaderFooterMedia($zip, 'footer');
156 |
157 | // Add header/footer contents
158 | $rId = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements
159 | $sections = $phpWord->getSections();
160 | // var_dump($this->parts);exit;
161 | foreach ($sections as $section) {
162 | $this->addHeaderFooterContent($section, $zip, 'header', $rId);
163 | $this->addHeaderFooterContent($section, $zip, 'footer', $rId);
164 | }
165 |
166 | $this->addNotes($zip, $rId, 'footnote');
167 | $this->addNotes($zip, $rId, 'endnote');
168 | $this->addComments($zip, $rId);
169 | $this->addChart($zip, $rId);
170 |
171 | // var_dump($this->parts);exit;
172 | // echo $this->writerParts['document']->write();exit;
173 | echo $this->writerParts['styles']->write();exit;
174 |
175 | // Write parts
176 | foreach ($this->parts as $partName => $fileName) {
177 | if ($fileName != '') {
178 | $zip->addFromString($fileName, $this->getWriterPart($partName)->write());
179 | }
180 | }
181 |
182 | // Close zip archive and cleanup temp file
183 | $zip->close();
184 | $this->cleanupTempFile();
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/XmlTemple/image.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/tests/Trace.php:
--------------------------------------------------------------------------------
1 | setValue('$codeName', \$code, 'text', false);
26 | \$this->word->wordProcessor->saveAsToPathForTrace('$dir', '$baseName');
27 | CODE;
28 | $build->replace('SAVE-ANIMALCODE', $SAVEANIMALCODE, MDWORD_SRC_DIRECTORY.'/Edit/Part/Document.php');
29 |
30 | require $runSampleFile;
31 |
32 | $build->replace('SAVE-ANIMALCODE', '', MDWORD_SRC_DIRECTORY.'/Edit/Part/Document.php');
33 |
34 | $words = $common->getDirFiles($dir,function($dir,$wordName) {
35 | return base64_encode(file_get_contents($dir.DIRECTORY_SEPARATOR.$wordName));
36 | });
37 |
38 | $imagesStream = $common->CurlSend($wordToImageApi,'',['words'=>json_encode($words)],180000);
39 | $images = json_decode($imagesStream,true);
40 | $frames = $durations = [];
41 | if(!is_array($images['images'])) {
42 | echo $imagesStream;exit;
43 | }
44 | foreach($images['images'] as $image) {
45 | $frames[] = base64_decode($image);
46 | $durations[] = 500;
47 | }
48 |
49 | $gc = new GifCreator();
50 | $gifBinary = $gc->create($frames, $durations, 0);
51 |
52 | file_put_contents($dir.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.$baseName.'.gif',$gifBinary);
53 |
54 |
55 | function getCodeByTrace($file) {
56 | $traces = debug_backtrace(null,5);
57 | $code = '';
58 | foreach($traces as $trace) {
59 | if($trace['file'] == $file) {
60 | foreach ($trace['args'] as $key => $arg) {
61 | if(is_array($arg)) {
62 | $trace['args'][$key] = preg_replace('/\s/i', '', var_export($arg,true));
63 | }
64 | }
65 | $code = $trace['line'].' '.$trace['class'].$trace['type'].$trace['function'].'('.implode(',', $trace['args']).')';
66 | break;
67 | }
68 | }
69 |
70 | return $code;
71 | }
72 |
73 | /**
74 | * from https://www.cnblogs.com/lixihuan/p/11906094.html
75 | * Create an animated GIF from multiple images
76 | */
77 | class GifCreator
78 | {
79 | /**
80 | * @var string The gif string source (old: this->GIF)
81 | */
82 | private $gif;
83 |
84 | /**
85 | * @var string Encoder version (old: this->VER)
86 | */
87 | private $version;
88 |
89 | /**
90 | * @var boolean Check the image is build or not (old: this->IMG)
91 | */
92 | private $imgBuilt;
93 |
94 | /**
95 | * @var array Frames string sources (old: this->BUF)
96 | */
97 | private $frameSources;
98 |
99 | /**
100 | * @var integer Gif loop (old: this->LOP)
101 | */
102 | private $loop;
103 |
104 | /**
105 | * @var integer Gif dis (old: this->DIS)
106 | */
107 | private $dis;
108 |
109 | /**
110 | * @var integer Gif color (old: this->COL)
111 | */
112 | private $colour;
113 |
114 | /**
115 | * @var array (old: this->ERR)
116 | */
117 | private $errors;
118 |
119 | // Methods
120 | // ===================================================================================
121 |
122 | /**
123 | * Constructor
124 | */
125 | public function __construct()
126 | {
127 | $this->reset();
128 |
129 | // Static data
130 | $this->version = 'GifCreator: Under development';
131 | $this->errors = array(
132 | 'ERR00' => 'Does not supported function for only one image.',
133 | 'ERR01' => 'Source is not a GIF image.',
134 | 'ERR02' => 'You have to give resource image variables, image URL or image binary sources in $frames array.',
135 | 'ERR03' => 'Does not make animation from animated GIF source.',
136 | );
137 | }
138 |
139 | /**
140 | * Create the GIF string (old: GIFEncoder)
141 | *
142 | * @param array $frames An array of frame: can be file paths, resource image variables, binary sources or image URLs
143 | * @param array $durations An array containing the duration of each frame
144 | * @param integer $loop Number of GIF loops before stopping animation (Set 0 to get an infinite loop)
145 | *
146 | * @return string The GIF string source
147 | */
148 | public function create($frames = array(), $durations = array(), $loop = 0)
149 | {
150 | if (!is_array($frames) && !is_array($durations)) {
151 |
152 | throw new \Exception($this->version.': '.$this->errors['ERR00']);
153 | }
154 |
155 | $this->loop = ($loop > -1) ? $loop : 0;
156 | $this->dis = 2;
157 |
158 | for ($i = 0; $i < count($frames); $i++) {
159 |
160 | if (is_resource($frames[$i])) { // Resource var
161 |
162 | $resourceImg = $frames[$i];
163 |
164 | ob_start();
165 | imagegif($frames[$i]);
166 | $this->frameSources[] = ob_get_contents();
167 | ob_end_clean();
168 |
169 | } elseif (is_string($frames[$i])) { // File path or URL or Binary source code
170 |
171 | if (@file_exists($frames[$i]) || filter_var($frames[$i], FILTER_VALIDATE_URL)) { // File path
172 |
173 | $frames[$i] = file_get_contents($frames[$i]);
174 | }
175 |
176 | $resourceImg = imagecreatefromstring($frames[$i]);
177 |
178 | ob_start();
179 | imagegif($resourceImg);
180 | $this->frameSources[] = ob_get_contents();
181 | ob_end_clean();
182 |
183 | } else { // Fail
184 |
185 | throw new \Exception($this->version.': '.$this->errors['ERR02']);
186 | }
187 |
188 | if ($i == 0) {
189 |
190 | $colour = imagecolortransparent($resourceImg);
191 | }
192 |
193 | if (substr($this->frameSources[$i], 0, 6) != 'GIF87a' && substr($this->frameSources[$i], 0, 6) != 'GIF89a') {
194 |
195 | throw new \Exception($this->version.': '.$i.' '.$this->errors['ERR01']);
196 | }
197 |
198 | for ($j = (13 + 3 * (2 << (ord($this->frameSources[$i] { 10 }) & 0x07))), $k = TRUE; $k; $j++) {
199 |
200 | switch ($this->frameSources[$i] { $j }) {
201 |
202 | case '!':
203 |
204 | if ((substr($this->frameSources[$i], ($j + 3), 8)) == 'NETSCAPE') {
205 |
206 | throw new \Exception($this->version.': '.$this->errors['ERR03'].' ('.($i + 1).' source).');
207 | }
208 |
209 | break;
210 |
211 | case ';':
212 |
213 | $k = false;
214 | break;
215 | }
216 | }
217 |
218 | unset($resourceImg);
219 | }
220 |
221 | if (isset($colour)) {
222 |
223 | $this->colour = $colour;
224 |
225 | } else {
226 |
227 | $red = $green = $blue = 0;
228 | $this->colour = ($red > -1 && $green > -1 && $blue > -1) ? ($red | ($green << 8) | ($blue << 16)) : -1;
229 | }
230 |
231 | $this->gifAddHeader();
232 | //d(count($this->frameSources));
233 | for ($i = 0; $i < count($this->frameSources); $i++) {
234 | $this->addGifFrames($i, $durations[$i]);
235 | }
236 |
237 | $this->gifAddFooter();
238 |
239 | return $this->gif;
240 | }
241 |
242 | // Internals
243 | // ===================================================================================
244 |
245 | /**
246 | * Add the header gif string in its source (old: GIFAddHeader)
247 | */
248 | public function gifAddHeader()
249 | {
250 | $cmap = 0;
251 |
252 | if (ord($this->frameSources[0] { 10 }) & 0x80) {
253 |
254 | $cmap = 3 * (2 << (ord($this->frameSources[0] { 10 }) & 0x07));
255 |
256 | $this->gif .= substr($this->frameSources[0], 6, 7);
257 | $this->gif .= substr($this->frameSources[0], 13, $cmap);
258 | $this->gif .= "!\377\13NETSCAPE2.0\3\1".$this->encodeAsciiToChar($this->loop)."\0";
259 | }
260 | }
261 |
262 | /**
263 | * Add the frame sources to the GIF string (old: GIFAddFrames)
264 | *
265 | * @param integer $i
266 | * @param integer $d
267 | */
268 | public function addGifFrames($i, $d)
269 | {
270 |
271 | $Locals_str = 13 + 3 * (2 << (ord($this->frameSources[ $i ] { 10 }) & 0x07));
272 |
273 | $Locals_end = strlen($this->frameSources[$i]) - $Locals_str - 1;
274 | $Locals_tmp = substr($this->frameSources[$i], $Locals_str, $Locals_end);
275 |
276 | $Global_len = 2 << (ord($this->frameSources[0 ] { 10 }) & 0x07);
277 | $Locals_len = 2 << (ord($this->frameSources[$i] { 10 }) & 0x07);
278 |
279 | $Global_rgb = substr($this->frameSources[0], 13, 3 * (2 << (ord($this->frameSources[0] { 10 }) & 0x07)));
280 | $Locals_rgb = substr($this->frameSources[$i], 13, 3 * (2 << (ord($this->frameSources[$i] { 10 }) & 0x07)));
281 |
282 | $Locals_ext = "!\xF9\x04".chr(($this->dis << 2) + 0).chr(($d >> 0 ) & 0xFF).chr(($d >> 8) & 0xFF)."\x0\x0";
283 |
284 | if ($this->colour > -1 && ord($this->frameSources[$i] { 10 }) & 0x80) {
285 |
286 | for ($j = 0; $j < (2 << (ord($this->frameSources[$i] { 10 } ) & 0x07)); $j++) {
287 |
288 | if (ord($Locals_rgb { 3 * $j + 0 }) == (($this->colour >> 16) & 0xFF) &&
289 | ord($Locals_rgb { 3 * $j + 1 }) == (($this->colour >> 8) & 0xFF) &&
290 | ord($Locals_rgb { 3 * $j + 2 }) == (($this->colour >> 0) & 0xFF)
291 | ) {
292 | $Locals_ext = "!\xF9\x04".chr(($this->dis << 2) + 1).chr(($d >> 0) & 0xFF).chr(($d >> 8) & 0xFF).chr($j)."\x0";
293 | break;
294 | }
295 | }
296 | }
297 |
298 | switch ($Locals_tmp { 0 }) {
299 |
300 | case '!':
301 |
302 | $Locals_img = substr($Locals_tmp, 8, 10);
303 | $Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);
304 |
305 | break;
306 |
307 | case ',':
308 |
309 | $Locals_img = substr($Locals_tmp, 0, 10);
310 | $Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);
311 |
312 | break;
313 | }
314 |
315 | if (ord($this->frameSources[$i] { 10 }) & 0x80 && $this->imgBuilt) {
316 |
317 | if ($Global_len == $Locals_len) {
318 |
319 | if ($this->gifBlockCompare($Global_rgb, $Locals_rgb, $Global_len)) {
320 |
321 | $this->gif .= $Locals_ext.$Locals_img.$Locals_tmp;
322 |
323 | } else {
324 |
325 | $byte = ord($Locals_img { 9 });
326 | $byte |= 0x80;
327 | $byte &= 0xF8;
328 | $byte |= (ord($this->frameSources[0] { 10 }) & 0x07);
329 | $Locals_img { 9 } = chr($byte);
330 | $this->gif .= $Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp;
331 | }
332 |
333 | } else {
334 |
335 | $byte = ord($Locals_img { 9 });
336 | $byte |= 0x80;
337 | $byte &= 0xF8;
338 | $byte |= (ord($this->frameSources[$i] { 10 }) & 0x07);
339 | $Locals_img { 9 } = chr($byte);
340 | $this->gif .= $Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp;
341 | }
342 |
343 | } else {
344 |
345 | $this->gif .= $Locals_ext.$Locals_img.$Locals_tmp;
346 | }
347 |
348 | $this->imgBuilt = true;
349 | }
350 |
351 | /**
352 | * Add the gif string footer char (old: GIFAddFooter)
353 | */
354 | public function gifAddFooter()
355 | {
356 | $this->gif .= ';';
357 | }
358 |
359 | /**
360 | * Compare two block and return the version (old: GIFBlockCompare)
361 | *
362 | * @param string $globalBlock
363 | * @param string $localBlock
364 | * @param integer $length
365 | *
366 | * @return integer
367 | */
368 | public function gifBlockCompare($globalBlock, $localBlock, $length)
369 | {
370 | for ($i = 0; $i < $length; $i++) {
371 |
372 | if ($globalBlock { 3 * $i + 0 } != $localBlock { 3 * $i + 0 } ||
373 | $globalBlock { 3 * $i + 1 } != $localBlock { 3 * $i + 1 } ||
374 | $globalBlock { 3 * $i + 2 } != $localBlock { 3 * $i + 2 }) {
375 |
376 | return 0;
377 | }
378 | }
379 |
380 | return 1;
381 | }
382 |
383 | /**
384 | * Encode an ASCII char into a string char (old: GIFWord)
385 | *
386 | * $param integer $char ASCII char
387 | *
388 | * @return string
389 | */
390 | public function encodeAsciiToChar($char)
391 | {
392 | return (chr($char & 0xFF).chr(($char >> 8) & 0xFF));
393 | }
394 |
395 | /**
396 | * Reset and clean the current object
397 | */
398 | public function reset()
399 | {
400 | $this->frameSources;
401 | $this->gif = 'GIF89a'; // the GIF header
402 | $this->imgBuilt = false;
403 | $this->loop = 0;
404 | $this->dis = 2;
405 | $this->colour = -1;
406 | }
407 |
408 | // Getter / Setter
409 | // ===================================================================================
410 |
411 | /**
412 | * Get the final GIF image string (old: GetAnimation)
413 | *
414 | * @return string
415 | */
416 | public function getGif()
417 | {
418 | return $this->gif;
419 | }
420 | }
--------------------------------------------------------------------------------
/tests/function.php:
--------------------------------------------------------------------------------
1 | $v) {
19 | if($k === 0) {
20 | continue;
21 | }
22 |
23 | $line = '';
24 | foreach($v as $str) {
25 | $line .= '| '.$str.' ';
26 | }
27 | $line .= '|';
28 |
29 | $lines[] = $line;
30 | }
31 |
32 | return implode("\r\n",$lines);
33 | }
34 |
--------------------------------------------------------------------------------
/tests/samples/api/index.php:
--------------------------------------------------------------------------------
1 | getBlockList();
10 |
11 | // $getWord = new GetWord();
12 | // $content = $getWord->build();
13 | // file_put_contents($rtemplate, $content);
--------------------------------------------------------------------------------
/tests/samples/api/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/api/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/api/simple data.csv:
--------------------------------------------------------------------------------
1 | 类型,标题,链接,日期,来源,作者,内容属性,内容关注点,原/转载
2 | 网络新闻,网络新闻 - 张家口全力建设国家级可再生能源示范区 可再生能源发电总量占比近半,http://finance.eastmoney.com/a/202009071621903636.html,2020/9/7,东方财富网,DF524,正面,-,-
3 | 报刊,报刊 - 可再生能源发电总量占比近半,http://hbrb.hebnews.cn/pc/paper/c/202009/07/content_53373.html,2020/9/7,河北日报,,中性,-,-
4 | 财经,财经 - 【美股插水】 道指收市泻807点 纳指重挫5% 收市后纳指期货再插1%(不断更新),https://inews.hket.com/article/2739564/%E3%80%90%E7%BE%8E%E8%82%A1%E6%8F%92%E6%B0%B4%E3%80%91%20%E9%81%93%E6%8C%87%E6%94%B6%E5%B8%82%E7%80%89807%E9%BB%9E%E3%80%80%E7%B4%8D%E6%8C%87%E9%87%8D%E6%8C%AB5-%E3%80%80%E6%94%B6%E5%B8%82%E5%BE%8C%E7%B4%8D%E6%8C%87%E6%9C%9F%E8%B2%A8%E5%86%8D%E6%8F%921-%EF%BC%88%E4%B8%8D%E6%96%B7%E6%9B%B4%E6%96%B0%EF%BC%89,2020/9/4,香港经济日报即时新闻,,负面,-,-
5 |
--------------------------------------------------------------------------------
/tests/samples/api/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/api/temple.docx
--------------------------------------------------------------------------------
/tests/samples/block/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | $TemplateProcessor->setValue('two', 2);
14 | $TemplateProcessor->setValue('box', 'BOX');
15 |
16 | $TemplateProcessor->setValue('header', 'MDWORD-HEADER');
17 |
18 | $TemplateProcessor->setValue('footer', 'MDWORD-FOOTER');
19 |
20 | $datas = [
21 | ['price'=>100,'change'=>5,'changepercent'=>0.05],
22 | ['price'=>200,'change'=>-10,'changepercent'=>-0.05],
23 | ['price'=>500,'change'=>100,'changepercent'=>0.20],
24 | ];
25 | $bind = $TemplateProcessor->getBind($datas);
26 | $bind->bindValue('item',[])
27 | ->bindValue('stockprice',['price'],'item')
28 | ->bindValue('change',['change'],'item',function($value) {
29 | return [['type'=>MDWORD_TEXT,'text'=>$value]];
30 | })
31 | ->bindValue('changepercent',['changepercent'],'item',function($value) {
32 | return [['type'=>MDWORD_TEXT,'text'=>($value*100).'%']];
33 | })
34 | ;
35 |
36 | $TemplateProcessor->setValue('b', 'B');
37 | $TemplateProcessor->setValue('2', 'two');
38 |
39 | $TemplateProcessor->saveAs($rtemplate);
40 |
41 |
--------------------------------------------------------------------------------
/tests/samples/block/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/block/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/block/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/block/temple.docx
--------------------------------------------------------------------------------
/tests/samples/break page/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | $TemplateProcessor->setValue('value', 'r-value');
14 | $TemplateProcessor->setValue('value', 'r-value2');
15 |
16 | $TemplateProcessor->setImageValue('image', dirname(__FILE__).'/logo.jpg');
17 |
18 | $TemplateProcessor->clones('people', 3);
19 |
20 | $TemplateProcessor->setValue('name#0', 'colin0');
21 | $TemplateProcessor->setValue('name#1', [['type'=>MDWORD_PAGE_BREAK]]);
22 | $TemplateProcessor->setValue('name#2', 'colin2');
23 |
24 | $TemplateProcessor->setValue('sex#1', 'woman');
25 |
26 | $TemplateProcessor->setValue('age#0', '280');
27 | $TemplateProcessor->setValue('age#1', '281');
28 | $TemplateProcessor->setValue('age#2', '282');
29 |
30 | $TemplateProcessor->deleteP('style');
31 |
32 | $TemplateProcessor->saveAs($rtemplate);
33 |
34 |
--------------------------------------------------------------------------------
/tests/samples/break page/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/break page/temple.docx
--------------------------------------------------------------------------------
/tests/samples/clone/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | $datas = [
14 | ['title'=>'MDword','date'=>date('Y-m-d'),'link'=>'https://github.com/mkdreams/MDword','content'=>'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'],
15 | ['title'=>'Wake up India, you\'re harming yourself','date'=>'2020-08-05','link'=>'http://epaper.chinadaily.com.cn/a/202008/06/WS5f2b56e4a3107831ec7540e6.html','content'=>'On Tuesday, reports said that the Indian government had announced a ban on Baidu and Weibo, two popular smartphone apps developed in China.
Combined with the recent ban on short video sharing apps such as TikTok and Kwai, and social media app WeChat, India has now blocked its residents from using almost all popular Chinese apps.
That apart, in the past few months, India has provoked border clashes with China, set limitations on Chinese enterprises and imposed higher tariffs on some products imported from China.'],
16 | ];
17 | $bind = $TemplateProcessor->getBind($datas);
18 | $bind->bindValue('item',[])
19 | ->bindValue('title',['title'],'item')
20 | ->bindValue('date',['date'],'item')
21 | ->bindValue('link',['link'],'item',function($value) {
22 | return [['type'=>MDWORD_LINK,'text'=>$value,'link'=>$value]];
23 | })
24 | ->bindValue('content',['content'],'item',function($value) {
25 | $valueArr = explode('
', $value);
26 | $texts = [];
27 | foreach($valueArr as $text) {
28 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>$text];
29 | $texts[] = ['type'=>MDWORD_BREAK,'text'=>2];
30 | }
31 |
32 | return $texts;
33 | })
34 | ;
35 |
36 | $bind = $TemplateProcessor->getBind($datas);
37 | $bind->bindValue('tableitem',[])
38 | ->bindValue('tabletitle',['title'],'tableitem',function($value,$row) {
39 | return [['type'=>MDWORD_LINK,'text'=>$value,'link'=>$row['link']]];
40 | })
41 | ->bindValue('tabledate',['date'],'tableitem')
42 | ->bindValue('tablecontent',['content'],'tableitem',function($value) {
43 | $valueArr = explode('
', $value);
44 | $texts = [];
45 | $textsCount = count($valueArr)-1;
46 | foreach($valueArr as $key => $text) {
47 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>' '.$text];
48 | if($key < $textsCount) {
49 | $texts[] = ['type'=>MDWORD_BREAK,'text'=>1];
50 | }
51 | }
52 |
53 | return $texts;
54 | })
55 | ;
56 |
57 |
58 |
59 | $TemplateProcessor->cloneP('titletext',2);
60 | $TemplateProcessor->setValue('titletext#0',[['type'=>MDWORD_TEXT,'text'=>'clone titletext#0','pstyle'=>'titlestyle-1','style'=>'titlestyle-1']]);
61 | $TemplateProcessor->setValue('titletext#1',[['type'=>MDWORD_TEXT,'text'=>'clone titletext#1','pstyle'=>'titlestyle-1','style'=>'titlestyle-1']]);
62 | $TemplateProcessor->setValue('titletext-sub',[['type'=>MDWORD_TEXT,'text'=>'titletext-sub','pstyle'=>'titlestyle-2']]);
63 |
64 | $TemplateProcessor->cloneSection('section2',2);
65 | $TemplateProcessor->setValue('section2#0','section2#012345');
66 | $TemplateProcessor->setValue('section2#1','section2#1');
67 | $TemplateProcessor->setValue('header2#1','header2#1');
68 | $TemplateProcessor->deleteSection('section2#0');
69 | $TemplateProcessor->saveAs($rtemplate);
70 |
71 |
--------------------------------------------------------------------------------
/tests/samples/clone/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/clone/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/clone/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/clone/temple.docx
--------------------------------------------------------------------------------
/tests/samples/clonetable/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/clonetable/img.png
--------------------------------------------------------------------------------
/tests/samples/clonetable/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | $datas = [
14 | ['title'=>'有效成分1信息','A1'=>'中文通用名','B1'=>'中文通用名1','C1'=>'生物活性','D1'=>'生物活性1','F1'=>'结构式','F2'=> dirname(__FILE__).'/img.png'],
15 | ['title'=>'有效成分2信息','A1'=>'中文通用名','B1'=>'中文通用名2','C1'=>'生物活性','D1'=>'生物活性2','F1'=>'结构式','F2'=> dirname(__FILE__).'/img.png'],
16 | ];
17 |
18 |
19 | $TemplateProcessor->clones('table',count($datas));
20 | foreach($datas as $key => $data) {
21 | foreach($data as $vk => $v) {
22 | if($vk === 'F2') {
23 | $TemplateProcessor->setValue($vk.'#'.$key,[['type'=>MDWORD_IMG,'text'=>$v]]);
24 | }else{
25 | $TemplateProcessor->setValue($vk.'#'.$key,[['type'=>MDWORD_TEXT,'text'=>$v]]);
26 | }
27 | }
28 | }
29 |
30 | $TemplateProcessor->saveAs($rtemplate);
31 |
32 |
--------------------------------------------------------------------------------
/tests/samples/clonetable/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/clonetable/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/clonetable/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/clonetable/temple.docx
--------------------------------------------------------------------------------
/tests/samples/image/full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/full.png
--------------------------------------------------------------------------------
/tests/samples/image/img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/img.jpg
--------------------------------------------------------------------------------
/tests/samples/image/img2.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/img2.bmp
--------------------------------------------------------------------------------
/tests/samples/image/img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/img3.png
--------------------------------------------------------------------------------
/tests/samples/image/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 |
14 | $TemplateProcessor->clones('row',3);
15 | $TemplateProcessor->setValue('rowIndex#0',0);
16 | $TemplateProcessor->setValue('rowIndex#1',1);
17 | $TemplateProcessor->setValue('rowIndex#2',2);
18 | $TemplateProcessor->setValue('rowIndex#2',3);
19 | $TemplateProcessor->setValue('rowIndex#2',4);
20 |
21 | $TemplateProcessor->setImageValue('rowImage#0',dirname(__FILE__).'/img.jpg');
22 | $TemplateProcessor->setImageValue('rowImage#1',dirname(__FILE__).'/img2.bmp');
23 |
24 | $rows = [
25 | ['index'=>2,'image'=> dirname(__FILE__).'/img.jpg'],
26 | ['index'=>3,'image'=> dirname(__FILE__).'/img3.png'],
27 | ['index'=>4,'image'=> dirname(__FILE__).'/img3.png'],
28 | ];
29 |
30 | $bind = $TemplateProcessor->getBind($rows);
31 | $bind->bindValue('row#2',[])
32 | ->bindValue('rowIndex#2',['index'],'row#2')
33 | ->bindValue('rowImage#2',['image'],'row#2',MDWORD_IMG)
34 | ;
35 |
36 | $TemplateProcessor->setImageValue('rowImage#2#0',dirname(__FILE__).'/img.jpg');
37 |
38 | $TemplateProcessor->setValue('image insert', [['text' => dirname(__FILE__).'/words.png','type' => MDWORD_IMG,'width'=>300]]);
39 |
40 | $TemplateProcessor->setImageValue('image replace', dirname(__FILE__).'/img.jpg');
41 |
42 | //replace image by md5
43 | $Medies = $TemplateProcessor->getMedies();
44 | foreach($Medies as $Medie) {
45 | if('a2a9f8b099d7c5764da685b628468052' === $Medie['md5']) {
46 | $TemplateProcessor->setImageValue($Medie['md5'], dirname(__FILE__).'/img.jpg');
47 | }
48 | }
49 |
50 | $TemplateProcessor->setValue("full", [['text' => dirname(__FILE__).'/full.png','temple'=>file_get_contents(dirname(__FILE__).'/temple/full.xml'),'type' => MDWORD_IMG,'width'=>'100%']]);
51 | $TemplateProcessor->setValue("full2", [
52 | ['type' => MDWORD_TEXT,'text'=> "This is text."],
53 | ['type' => MDWORD_PAGE_BREAK,'replace'=>false],
54 | ['text' => dirname(__FILE__).'/full.png','temple'=>file_get_contents(dirname(__FILE__).'/temple/full.xml'),'type' => MDWORD_IMG,'width'=>'100%'],
55 | ['type' => MDWORD_PAGE_BREAK,'replace'=>false],
56 | ['text' => dirname(__FILE__).'/full.png','temple'=>file_get_contents(dirname(__FILE__).'/temple/full.xml'),'type' => MDWORD_IMG,'width'=>'100%'],
57 | ['type' => MDWORD_PAGE_BREAK,'replace'=>false],
58 | ['type' => MDWORD_TEXT,'text'=> "This is text2."],
59 | ['type' => MDWORD_PAGE_BREAK,'replace'=>false],
60 | ['type' => MDWORD_TEXT,'text'=> "This is below."],
61 | ['text' => dirname(__FILE__).'/full.png','temple'=>file_get_contents(dirname(__FILE__).'/temple/full.xml'),'type' => MDWORD_IMG,'width'=>'100%'],
62 | ]);
63 |
64 |
65 | $TemplateProcessor->saveAs($rtemplate);
66 |
67 |
--------------------------------------------------------------------------------
/tests/samples/image/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/image/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/temple.docx
--------------------------------------------------------------------------------
/tests/samples/image/temple/full.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | left
6 |
7 |
8 | top
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 0
52 |
53 |
54 | 0
55 |
56 |
57 |
--------------------------------------------------------------------------------
/tests/samples/image/words.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/image/words.png
--------------------------------------------------------------------------------
/tests/samples/memory use/index.php:
--------------------------------------------------------------------------------
1 | $v) {
15 | $markdown .= "\r\n| ".($k+1)." | ".$v." M | ".($k==0?"首次需要加载PHP类":(""))." |";
16 | }
17 | echo $markdown;
18 | // var_dump($nowMemorys);
19 |
20 |
21 | function main($template,$rtemplate,$bm) {
22 | $TemplateProcessor = new WordProcessor();
23 | $TemplateProcessor->load($template);
24 |
25 | //simple set value
26 | $TemplateProcessor->setValue('address', 'addr');
27 |
28 | //simple set value
29 | $TemplateProcessor->setValue('value', 'r-value');
30 | $TemplateProcessor->setValue('value', 'r-value2');
31 |
32 | //image
33 | $TemplateProcessor->setImageValue('image', dirname(__FILE__).'/logo.jpg');
34 |
35 |
36 | //table
37 | $TemplateProcessor->clones('people', 3);
38 |
39 | $TemplateProcessor->setValue('name#0', 'colin0');
40 | $TemplateProcessor->setValue('name#1', [['text'=>'colin1','style'=>'style','type'=>MDWORD_TEXT]]);
41 | $TemplateProcessor->setValue('name#2', 'colin2');
42 |
43 | $TemplateProcessor->setValue('sex#1', 'woman');
44 |
45 | $TemplateProcessor->setValue('age#0', '280');
46 | $TemplateProcessor->setValue('age#1', '281');
47 | $TemplateProcessor->setValue('age#2', '282');
48 |
49 | // list
50 | $TemplateProcessor->cloneP('item',3);
51 | $TemplateProcessor->setValue('item#0','ITEM1');
52 | $TemplateProcessor->setValue('item#1','ITEM2');
53 | $TemplateProcessor->setValue('item#2','ITEM3');
54 |
55 | // TOC and bind data
56 | $redWords = 'WORD';
57 | $datas = [
58 | ['title'=>'MDword Github','date'=>date('Y-m-d'),'link'=>'https://github.com/mkdreams/MDword','content'=>'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'],
59 | ['title'=>'Wake up India, you\'re harming yourself','date'=>'2020-08-05','link'=>'http://epaper.chinadaily.com.cn/a/202008/06/WS5f2b56e4a3107831ec7540e6.html','content'=>'On Tuesday, reports said that the Indian government had announced a ban on Baidu and Weibo, two popular smartphone apps developed in China.
Combined with the recent ban on short video sharing apps such as TikTok and Kwai, and social media app WeChat, India has now blocked its residents from using almost all popular Chinese apps.
That apart, in the past few months, India has provoked border clashes with China, set limitations on Chinese enterprises and imposed higher tariffs on some products imported from China.'],
60 | ];
61 | $bind = $TemplateProcessor->getBind($datas);
62 |
63 | $bind->bindValue('news',[])
64 | ->bindValue('title',['title'],'news')
65 | ->bindValue('date',['date'],'news')
66 | ->bindValue('link',['link'],'news',function($value) {
67 | return [['type'=>MDWORD_LINK,'text'=>$value,'link'=>$value]];
68 | })
69 | ->bindValue('content',['content'],'news',function($value) use($redWords) {
70 | $valueArr = explode('
', $value);
71 | $texts = [];
72 | foreach($valueArr as $text) {
73 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>$text];
74 | $tempTexts = explode($redWords,$text);
75 | foreach($tempTexts as $idx => $tempText) {
76 |
77 | if($idx != 0) {
78 | $texts[] = ['type'=>MDWORD_TEXT,'style'=>'red','text'=>$redWords];
79 | }
80 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>$tempText];
81 | }
82 |
83 | $texts[] = ['type'=>MDWORD_BREAK,'text'=>2];
84 | }
85 |
86 | return $texts;
87 | })
88 | ;
89 | $bind::$binds = [];
90 |
91 | $TemplateProcessor->deleteP('style');
92 | $TemplateProcessor->deleteP('red');
93 | $TemplateProcessor->saveAs($rtemplate);
94 | return (memory_get_usage()-$bm)/1024/1024;
95 | }
96 |
97 |
98 |
--------------------------------------------------------------------------------
/tests/samples/memory use/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/memory use/logo.jpg
--------------------------------------------------------------------------------
/tests/samples/memory use/word config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/memory use/word config.png
--------------------------------------------------------------------------------
/tests/samples/memory use/word result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/memory use/word result.png
--------------------------------------------------------------------------------
/tests/samples/memory use/word.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/memory use/word.gif
--------------------------------------------------------------------------------
/tests/samples/merge table cells/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | //table
14 | $TemplateProcessor->clones('people', 5);
15 |
16 | $TemplateProcessor->setValue('name#0', 'colin0');
17 | $TemplateProcessor->setValue('name#1', [['text' => 'colin1', 'table_style' => ['vMerge'=>3,'gridSpan'=>2], 'type' => MDWORD_TEXT]]);
18 | $TemplateProcessor->setValue('name#2', 'colin2');
19 |
20 | $TemplateProcessor->setValue('sex#1', 'woman');
21 |
22 | $TemplateProcessor->setValue('age#0', [['text' => '280', 'table_style' => ['gridSpan'=>2], 'type' => MDWORD_TEXT]]);
23 | $TemplateProcessor->setValue('age#2', [['text' => '282', 'table_style' => ['gridSpan'=>2], 'type' => MDWORD_TEXT]]);
24 |
25 |
26 | $TemplateProcessor->setValue('vMerge', [['text' => 'vMerge', 'table_style' => ['vMerge'=>3], 'type' => MDWORD_TEXT]]);
27 | $TemplateProcessor->setValue('vMerge2', [['text' => 'vMerge2', 'table_style' => ['vMerge'=>2], 'type' => MDWORD_TEXT]]);
28 | $TemplateProcessor->setValue('gridSpan', [['text' => 'gridSpan', 'table_style' => ['gridSpan'=>2], 'type' => MDWORD_TEXT]]);
29 |
30 | //delete table
31 | $TemplateProcessor->deleteTbl('row1');
32 |
33 | $TemplateProcessor->saveAs($rtemplate);
34 |
--------------------------------------------------------------------------------
/tests/samples/merge table cells/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/merge table cells/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/merge table cells/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/merge table cells/temple.docx
--------------------------------------------------------------------------------
/tests/samples/performance/1750pages.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/performance/1750pages.docx
--------------------------------------------------------------------------------
/tests/samples/performance/index.php:
--------------------------------------------------------------------------------
1 | load($template);
25 | $b = microtime(true);
26 | $TemplateProcessor->clones("test",$n);
27 | $datas = [];
28 | for($i = 0;$i<$n;$i++) {
29 | $TemplateProcessor->setValue('test#'.$i, $text);
30 | }
31 | $TemplateProcessor->saveAs($rtemplate);
32 | $e = microtime(true);
33 | $markdownTableDatas[] = ["1页母版赋值".$n."次",number_format($e-$b,2)];
34 | }
35 |
36 | $template = __DIR__.'/1750pages.docx';
37 | $rtemplate = __DIR__.'/r-1750pages.docx';
38 |
39 | foreach($ns as $n) {
40 | $b = microtime(true);
41 | $TemplateProcessor = new WordProcessor();
42 | $TemplateProcessor->load($template);
43 | $TemplateProcessor->cloneP('test', $n);
44 | for($i=0;$i<$n;$i++) {
45 | $TemplateProcessor->setValue('test#'.$i, 'MDword'.$i);
46 | }
47 | $TemplateProcessor->saveAs($rtemplate);
48 | $e = microtime(true);
49 | $markdownTableDatas[] = ["1750页母版赋值".$n."次",number_format($e-$b,2)];
50 | }
51 |
52 |
53 | echo markdownTable($markdownTableDatas);
54 | return ;
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/tests/samples/performance/r-1750pages.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/performance/r-1750pages.docx
--------------------------------------------------------------------------------
/tests/samples/performance/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/performance/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/performance/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/performance/temple.docx
--------------------------------------------------------------------------------
/tests/samples/phpword/index.php:
--------------------------------------------------------------------------------
1 | addSection();
13 | $section->addText(
14 | '"Learn from yesterday, live for today, hope for tomorrow. '
15 | . 'The important thing is not to stop questioning." '
16 | . '(Albert Einstein)'
17 | );
18 |
19 | $section->addText(
20 | '"Great achievement is usually born of great sacrifice, '
21 | . 'and is never the result of selfishness." '
22 | . '(Napoleon Hill)',
23 | array('name' => 'Tahoma', 'size' => 10)
24 | );
25 |
26 | // Adding Text element with font customized using named font style...
27 | $fontStyleName = 'oneUserDefinedStyle';
28 | $phpWord->addFontStyle(
29 | $fontStyleName,
30 | array('name' => 'Tahoma', 'size' => 10, 'color' => 'red', 'bold' => true)
31 | );
32 | $section->addText(
33 | '"The greatest accomplishment is not in never falling, '
34 | . 'but in rising again after you fall." '
35 | . '(Vince Lombardi)',
36 | $fontStyleName
37 | );
38 |
39 | // Adding Text element with font customized using explicitly created font style object...
40 | $fontStyle = new \PhpOffice\PhpWord\Style\Font();
41 | $fontStyle->setBold(true);
42 | $fontStyle->setName('Tahoma');
43 | $fontStyle->setSize(13);
44 | $myTextElement = $section->addText('"Believe you can and you\'re halfway there." (Theodor Roosevelt)');
45 | $myTextElement->setFontStyle($fontStyle);
46 |
47 |
48 | $section->addImage(dirname(__FILE__).'/logo.jpg');
49 |
50 | $TemplateProcessor = new WordProcessor();
51 | $TemplateProcessor->load($template);
52 | $TemplateProcessor->setValue('name', $phpWord, MDWORD_PHPWORD);
53 | $TemplateProcessor->saveAs($rtemplate);
54 |
55 |
--------------------------------------------------------------------------------
/tests/samples/phpword/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/phpword/logo.jpg
--------------------------------------------------------------------------------
/tests/samples/phpword/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/phpword/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/phpword/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/phpword/temple.docx
--------------------------------------------------------------------------------
/tests/samples/remain comments/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | //simple set value
14 | $TemplateProcessor->setValue('name', 'colin');
15 | // $TemplateProcessor->setValue('header', 'HEADER');
16 | // $TemplateProcessor->setValue('footer', 'FOOTER111');
17 |
18 | $TemplateProcessor->saveAs($rtemplate,true);
19 |
20 |
--------------------------------------------------------------------------------
/tests/samples/remain comments/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/remain comments/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/remain comments/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/remain comments/temple.docx
--------------------------------------------------------------------------------
/tests/samples/simple for readme/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/img.png
--------------------------------------------------------------------------------
/tests/samples/simple for readme/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | //simple set value
14 | $TemplateProcessor->setValue('value', 'r-value');
15 | $TemplateProcessor->setValue('value', 'r-value2');
16 |
17 | //image
18 | $TemplateProcessor->setImageValue('image', dirname(__FILE__) . '/logo.jpg');
19 |
20 | //table
21 | $TemplateProcessor->clones('people', 3);
22 |
23 | $TemplateProcessor->setValue('name#0', 'colin0');
24 | $TemplateProcessor->setValue('name#1', [['text' => 'colin1', 'style' => 'style', 'type' => MDWORD_TEXT]]);
25 | $TemplateProcessor->setValue('name#2', 'colin2');
26 |
27 | $TemplateProcessor->setValue('sex#1', 'woman');
28 |
29 | $TemplateProcessor->setValue('age#0', '280');
30 | $TemplateProcessor->setValue('age#1', '281');
31 | $TemplateProcessor->setValue('age#2', '282');
32 |
33 | //list
34 | $TemplateProcessor->cloneP('item', 3);
35 | $TemplateProcessor->setValue('item#0', 'ITEM1');
36 | $TemplateProcessor->setValue('item#1', 'ITEM2');
37 | $TemplateProcessor->setValue('item#2', 'ITEM3');
38 |
39 | //TOC and bind data
40 | $redWords = 'WORD';
41 | $datas = [
42 | [
43 | 'title' => 'MDword Github', 'date' => date('Y-m-d'),
44 | 'link' => 'https://github.com/mkdreams/MDword',
45 | 'images' => dirname(__FILE__) . '/logo.jpg,' . dirname(__FILE__) . '/img.png',
46 | 'content' => 'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'
47 | ],
48 | [
49 | 'title' => 'Wake up India, you\'re harming yourself',
50 | 'date' => '2020-08-05',
51 | 'link' => 'http://epaper.chinadaily.com.cn/a/202008/06/WS5f2b56e4a3107831ec7540e6.html',
52 | 'images' => dirname(__FILE__) . '/logo.jpg',
53 | 'content' => 'On Tuesday, reports said that the Indian government had announced a ban on Baidu and Weibo, two popular smartphone apps developed in China.
Combined with the recent ban on short video sharing apps such as TikTok and Kwai, and social media app WeChat, India has now blocked its residents from using almost all popular Chinese apps.
That apart, in the past few months, India has provoked border clashes with China, set limitations on Chinese enterprises and imposed higher tariffs on some products imported from China.'
54 | ],
55 | ];
56 | $bind = $TemplateProcessor->getBind($datas);
57 | $bind->bindValue('news', [])
58 | ->bindValue('title', ['title'], 'news')
59 | ->bindValue('date', ['date'], 'news')
60 | ->bindValue('link', ['link'], 'news', function ($value) {
61 | return [['type' => MDWORD_LINK, 'text' => $value, 'link' => $value]];
62 | })
63 | ->bindValue('images', ['images'], 'news', function ($value) {
64 | $images = explode(',', $value);
65 | $texts = [];
66 | foreach ($images as $key => $image) {
67 | if ($key === 0) {
68 | $texts[] = ['type' => MDWORD_IMG, 'text' => $image, 'style' => 'imgstyle'];
69 | } else {
70 | $texts[] = ['type' => MDWORD_IMG, 'text' => $image];
71 | }
72 | }
73 | return $texts;
74 | })
75 | ->bindValue('content', ['content'], 'news', function ($value) use ($redWords) {
76 | $valueArr = explode('
', $value);
77 | $texts = [];
78 | foreach ($valueArr as $text) {
79 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $text];
80 | $tempTexts = explode($redWords, $text);
81 | foreach ($tempTexts as $idx => $tempText) {
82 |
83 | if ($idx != 0) {
84 | $texts[] = ['type' => MDWORD_TEXT, 'style' => 'red', 'text' => $redWords];
85 | }
86 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $tempText];
87 | }
88 |
89 | $texts[] = ['type' => MDWORD_BREAK, 'text' => 2];
90 | }
91 |
92 | return $texts;
93 | });
94 |
95 | $numDatas = [
96 | [
97 | 'title'=>'title-1',
98 | 'content'=>'content-1'
99 | ],
100 | [
101 | 'title'=>'title-2',
102 | 'sub'=>[
103 | [
104 | 'title'=>'subTitle-2-1',
105 | 'content'=>'content-2-1',
106 | ],
107 | [
108 | 'title'=>'subTitle-2-2',
109 | 'content'=>'content-2-2',
110 | ],
111 | ]
112 | ],
113 | [
114 | 'title'=>'title-3',
115 | 'sub'=>[
116 | [
117 | 'title'=>'subTitle-3-1',
118 | 'content'=>'content-3-1',
119 | ],
120 | [
121 | 'title'=>'subTitle-3-2',
122 | 'content'=>'content-3-2',
123 | ],
124 | ]
125 | ],
126 | ];
127 |
128 | $TemplateProcessor->cloneP('num',count($numDatas));
129 | foreach($numDatas as $idx => $numData) {
130 | $TemplateProcessor->cloneP('num'.'#'.$idx,3);
131 |
132 | $TemplateProcessor->setValue('num'.'#'.$idx.'#0',[['text' => $numData['title'], 'pstyle' => 'numstyle-level-1', 'style' => 'numstyle-level-1', 'type' => MDWORD_TEXT]]);
133 |
134 | if(isset($numData['content'])) {
135 | $TemplateProcessor->setValue('num'.'#'.$idx.'#1',[['text' => $numData['content'], 'pstyle' => 'numstyle-level-3', 'style' => 'numstyle-level-3', 'type' => MDWORD_TEXT]]);
136 | }else{
137 | $TemplateProcessor->deleteP('num'.'#'.$idx.'#1');
138 | }
139 |
140 | $subName = 'num'.'#'.$idx.'#2';
141 | if(isset($numData['sub'])) {
142 | $TemplateProcessor->cloneP($subName,count($numData['sub']));
143 |
144 | foreach($numData['sub'] as $subIdx => $subData) {
145 | $TemplateProcessor->cloneP($subName.'#'.$subIdx,2);
146 |
147 | $TemplateProcessor->setValue($subName.'#'.$subIdx.'#0',[['text' => $subData['title'], 'pstyle' => 'numstyle-level-2', 'style' => 'numstyle-level-2', 'type' => MDWORD_TEXT]]);
148 | $TemplateProcessor->setValue($subName.'#'.$subIdx.'#1',[['text' => $subData['content'], 'pstyle' => 'numstyle-level-3', 'style' => 'numstyle-level-3', 'type' => MDWORD_TEXT]]);
149 | }
150 | }else{
151 | $TemplateProcessor->deleteP($subName);
152 | }
153 | }
154 |
155 | //links
156 | $TemplateProcessor->setValue('plink', [
157 | ['type'=>MDWORD_LINK,'text' => 'colin1','link'=>'https://baidu.com?v=1'],
158 | ['type' =>MDWORD_LINK,'text' => 'colin2', 'style' => 'style','link'=>'https://baidu.com?v=2'],
159 | ['type'=>MDWORD_LINK,'text' => 'colin3','link'=>'https://baidu.com?v=3'],
160 | ]);
161 |
162 | $TemplateProcessor->deleteP('numstyle');
163 |
164 |
165 | $TemplateProcessor->deleteP('style');
166 | $TemplateProcessor->deleteP('red');
167 | $TemplateProcessor->deleteP('imgstyle');
168 |
169 | $TemplateProcessor->saveAs($rtemplate);
170 |
--------------------------------------------------------------------------------
/tests/samples/simple for readme/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/logo.jpg
--------------------------------------------------------------------------------
/tests/samples/simple for readme/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/simple for readme/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/temple.docx
--------------------------------------------------------------------------------
/tests/samples/simple for readme/word config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/word config.png
--------------------------------------------------------------------------------
/tests/samples/simple for readme/word result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/word result.png
--------------------------------------------------------------------------------
/tests/samples/simple for readme/word.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/simple for readme/word.gif
--------------------------------------------------------------------------------
/tests/samples/text/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | $datas = [
14 | ['price'=>100,'change'=>5,'changepercent'=>0.05],
15 | ['price'=>200,'change'=>-10,'changepercent'=>-0.05],
16 | ['price'=>500,'change'=>100,'changepercent'=>0.20],
17 | ];
18 | $bind = $TemplateProcessor->getBind($datas);
19 | $bind->bindValue('item',[])
20 | ->bindValue('stockprice',['price'],'item')
21 | ->bindValue('change',['change'],'item',function($value) {
22 | if($value > 0) {
23 | $style = 'red';
24 | }else{
25 | $style = 'green';
26 | }
27 | return [['type'=>MDWORD_TEXT,'text'=>$value,'style'=>$style]];
28 | })
29 | ->bindValue('changepercent',['changepercent'],'item',function($value) {
30 | if($value > 0) {
31 | $style = 'red';
32 | }else{
33 | $style = 'green';
34 | }
35 | return [['type'=>MDWORD_TEXT,'text'=>($value*100).'%','style'=>$style]];
36 | })
37 | ;
38 |
39 | $TemplateProcessor->saveAs($rtemplate);
40 |
41 |
--------------------------------------------------------------------------------
/tests/samples/text/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/text/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/text/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/text/temple.docx
--------------------------------------------------------------------------------
/tests/samples/toc/index.php:
--------------------------------------------------------------------------------
1 | load($template);
12 |
13 | //TOC and bind data
14 | $redWords = 'WORD';
15 | $datas = [
16 | ['title'=>'MDword Github','date'=>date('Y-m-d'),'link'=>'https://github.com/mkdreams/MDword','content'=>'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'],
17 | ['title'=>'Wake up India, you\'re harming yourself','date'=>'2020-08-05','link'=>'http://epaper.chinadaily.com.cn/a/202008/06/WS5f2b56e4a3107831ec7540e6.html','content'=>'On Tuesday, reports said that the Indian government had announced a ban on Baidu and Weibo, two popular smartphone apps developed in China.
Combined with the recent ban on short video sharing apps such as TikTok and Kwai, and social media app WeChat, India has now blocked its residents from using almost all popular Chinese apps.
That apart, in the past few months, India has provoked border clashes with China, set limitations on Chinese enterprises and imposed higher tariffs on some products imported from China.'],
18 | ['title'=>'MDword Github','date'=>date('Y-m-d'),'link'=>'https://github.com/mkdreams/MDword','content'=>'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'],
19 | ];
20 | $bind = $TemplateProcessor->getBind($datas);
21 | $bind->bindValue('news',[])
22 | ->bindValue('title',['title'],'news')
23 | ->bindValue('date',['date'],'news')
24 | ->bindValue('link',['link'],'news',function($value) {
25 | return [['type'=>MDWORD_LINK,'text'=>$value,'link'=>$value]];
26 | })
27 | ->bindValue('content',['content'],'news',function($value) use($redWords) {
28 | $valueArr = explode('
', $value);
29 | $texts = [];
30 | foreach($valueArr as $text) {
31 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>$text];
32 | $tempTexts = explode($redWords,$text);
33 | foreach($tempTexts as $idx => $tempText) {
34 |
35 | if($idx != 0) {
36 | $texts[] = ['type'=>MDWORD_TEXT,'style'=>'red','text'=>$redWords];
37 | }
38 | $texts[] = ['type'=>MDWORD_TEXT,'text'=>$tempText];
39 | }
40 |
41 | $texts[] = ['type'=>MDWORD_BREAK,'text'=>2];
42 | }
43 |
44 | return $texts;
45 | })
46 | ;
47 |
48 |
49 | //toc table
50 | $InnerVars = $TemplateProcessor->getInnerVars();
51 |
52 | $TemplateProcessor->clones('table_item', 3);
53 |
54 | $TemplateProcessor->setValue('table_title#0',['text'=>$InnerVars['levels'][1]['text'],'name'=>$InnerVars['levels'][1]['name']],MDWORD_REF);
55 | $TemplateProcessor->setValue('table_title#1',['text'=>$InnerVars['levels'][2]['text'],'name'=>$InnerVars['levels'][2]['name']],MDWORD_REF);
56 | $TemplateProcessor->setValue('table_title#2',['text'=>$InnerVars['levels'][3]['text'],'name'=>$InnerVars['levels'][3]['name']],MDWORD_REF);
57 |
58 | $TemplateProcessor->setValue('table_page#0',['name'=>$InnerVars['levels'][1]['name']],MDWORD_PAGEREF);
59 | $TemplateProcessor->setValue('table_page#1',['name'=>$InnerVars['levels'][2]['name']],MDWORD_PAGEREF);
60 | $TemplateProcessor->setValue('table_page#2',['name'=>$InnerVars['levels'][3]['name']],MDWORD_PAGEREF);
61 |
62 | //page
63 | $TemplateProcessor->setValue('now_page',[],MDWORD_NOWPAGE);
64 | $TemplateProcessor->setValue('total_page',[],MDWORD_TOTALPAGE);
65 |
66 | $TemplateProcessor->setValue('toc_page',['name'=>'bookmarket_toc'],MDWORD_PAGEREF);
67 |
68 |
69 | $TemplateProcessor->saveAs($rtemplate);
70 |
71 |
--------------------------------------------------------------------------------
/tests/samples/toc/r-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/toc/r-temple.docx
--------------------------------------------------------------------------------
/tests/samples/toc/temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/toc/temple.docx
--------------------------------------------------------------------------------
/tests/samples/wps/index.php:
--------------------------------------------------------------------------------
1 | 'MDword Github', 'date' => date('Y-m-d'),
12 | 'content' => 'OFFICE WORD 动态数据 绑定数据 生成报告
OFFICE WORD Dynamic data binding data generation report.'
13 | ],
14 | [
15 | 'title' => 'Wake up India, you\'re harming yourself',
16 | 'content' => 'On Tuesday, reports said that the Indian government had announced a ban on Baidu and Weibo, two popular smartphone apps developed in China.
Combined with the recent ban on short video sharing apps such as TikTok and Kwai, and social media app WeChat, India has now blocked its residents from using almost all popular Chinese apps.
That apart, in the past few months, India has provoked border clashes with China, set limitations on Chinese enterprises and imposed higher tariffs on some products imported from China.'
17 | ],
18 | ];
19 |
20 | $template = __DIR__ . '/wps-temple.docx';
21 | $rtemplate = __DIR__ . '/r-wps-temple.docx';
22 | $TemplateProcessor = new WordProcessor();
23 | $TemplateProcessor->load($template);
24 | $bind = $TemplateProcessor->getBind($datas);
25 | $bind->bindValue('news', [])
26 | ->bindValue('title', ['title'], 'news')
27 | ->bindValue('content', ['content'], 'news', function ($value) use ($redWords) {
28 | $valueArr = explode('
', $value);
29 | $texts = [];
30 | foreach ($valueArr as $text) {
31 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $text];
32 | $tempTexts = explode($redWords, $text);
33 | foreach ($tempTexts as $idx => $tempText) {
34 |
35 | if ($idx != 0) {
36 | $texts[] = ['type' => MDWORD_TEXT, 'style' => 'red', 'text' => $redWords];
37 | }
38 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $tempText];
39 | }
40 |
41 | $texts[] = ['type' => MDWORD_BREAK, 'text' => 2];
42 | }
43 |
44 | $texts[] = ['type' => MDWORD_PAGE_BREAK];
45 | return $texts;
46 | });
47 | $TemplateProcessor->saveAs($rtemplate);
48 |
49 | $template = __DIR__ . '/word-temple.docx';
50 | $rtemplate = __DIR__ . '/r-word-temple.docx';
51 | $TemplateProcessor = new WordProcessor();
52 | $TemplateProcessor->load($template);
53 | $bind = $TemplateProcessor->getBind($datas);
54 | $bind->bindValue('news', [])
55 | ->bindValue('title', ['title'], 'news')
56 | ->bindValue('content', ['content'], 'news', function ($value) use ($redWords) {
57 | $valueArr = explode('
', $value);
58 | $texts = [];
59 | foreach ($valueArr as $text) {
60 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $text];
61 | $tempTexts = explode($redWords, $text);
62 | foreach ($tempTexts as $idx => $tempText) {
63 |
64 | if ($idx != 0) {
65 | $texts[] = ['type' => MDWORD_TEXT, 'style' => 'red', 'text' => $redWords];
66 | }
67 | $texts[] = ['type' => MDWORD_TEXT, 'text' => $tempText];
68 | }
69 |
70 | $texts[] = ['type' => MDWORD_BREAK, 'text' => 2];
71 | }
72 |
73 | $texts[] = ['type' => MDWORD_PAGE_BREAK];
74 | return $texts;
75 | });
76 | $TemplateProcessor->saveAs($rtemplate);
77 |
--------------------------------------------------------------------------------
/tests/samples/wps/r-word-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/wps/r-word-temple.docx
--------------------------------------------------------------------------------
/tests/samples/wps/r-wps-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/wps/r-wps-temple.docx
--------------------------------------------------------------------------------
/tests/samples/wps/word-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/wps/word-temple.docx
--------------------------------------------------------------------------------
/tests/samples/wps/wps-temple.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkdreams/MDword/6e7b1f00bcecf7bbfe94e59568b1ca4288afa1ca/tests/samples/wps/wps-temple.docx
--------------------------------------------------------------------------------