├── .gitignore
├── LICENSE
├── README.md
├── api.md
├── baby
├── step10_xml_parse_modify_generate.js
├── step11_buildexcel.js
├── step12_buildexcel_data.js
├── step13_buildexcel_data.js
├── step1_open_save.js
├── step2_transplant_style.js
├── step3_read_minimal.js
├── step4_edit_slide.js
├── step5_add_shape.js
├── step6_add_slide.js
├── step7_diff.js
├── step8_frankenstein_copy.js
├── step9_xml_parse_generate.js
└── t.js
├── examples
└── example.js
├── lib
├── chart.js
├── charts.js
├── defaults.js
├── fragment.js
├── fragments
│ ├── Microsoft_Excel_Sheet1.xlsx
│ ├── js
│ │ ├── chart.js
│ │ └── chartframe.js
│ └── xml
│ │ ├── chart.xml
│ │ └── chartframe.xml
├── msexcel-builder.js
├── officechart.js
├── pptx.js
├── presentation.js
├── shape.js
├── shapeProperties.js
├── slide.js
├── util
│ └── clone.js
└── xmlnode.js
├── package.json
├── test
├── chart.js
├── example.js
├── files
│ ├── minimal.pptx
│ ├── parts3-a.pptx
│ ├── parts3-b.pptx
│ └── parts3.pptx
├── synthesize.js
└── xmlnode.js
└── xml2js.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | lab/
30 | .idea/
31 | .DS_Store
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | ==========================
3 |
4 | Version 3, 29 June 2007
5 |
6 | Copyright © 2007 Free Software Foundation, Inc. <>
7 |
8 | Everyone is permitted to copy and distribute verbatim copies of this license
9 | document, but changing it is not allowed.
10 |
11 | ## Preamble
12 |
13 | The GNU General Public License is a free, copyleft license for software and other
14 | kinds of works.
15 |
16 | The licenses for most software and other practical works are designed to take away
17 | your freedom to share and change the works. By contrast, the GNU General Public
18 | License is intended to guarantee your freedom to share and change all versions of a
19 | program--to make sure it remains free software for all its users. We, the Free
20 | Software Foundation, use the GNU General Public License for most of our software; it
21 | applies also to any other work released this way by its authors. You can apply it to
22 | your programs, too.
23 |
24 | When we speak of free software, we are referring to freedom, not price. Our General
25 | Public Licenses are designed to make sure that you have the freedom to distribute
26 | copies of free software (and charge for them if you wish), that you receive source
27 | code or can get it if you want it, that you can change the software or use pieces of
28 | it in new free programs, and that you know you can do these things.
29 |
30 | To protect your rights, we need to prevent others from denying you these rights or
31 | asking you to surrender the rights. Therefore, you have certain responsibilities if
32 | you distribute copies of the software, or if you modify it: responsibilities to
33 | respect the freedom of others.
34 |
35 | For example, if you distribute copies of such a program, whether gratis or for a fee,
36 | you must pass on to the recipients the same freedoms that you received. You must make
37 | sure that they, too, receive or can get the source code. And you must show them these
38 | terms so they know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps: (1) assert
41 | copyright on the software, and (2) offer you this License giving you legal permission
42 | to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains that there is
45 | no warranty for this free software. For both users' and authors' sake, the GPL
46 | requires that modified versions be marked as changed, so that their problems will not
47 | be attributed erroneously to authors of previous versions.
48 |
49 | Some devices are designed to deny users access to install or run modified versions of
50 | the software inside them, although the manufacturer can do so. This is fundamentally
51 | incompatible with the aim of protecting users' freedom to change the software. The
52 | systematic pattern of such abuse occurs in the area of products for individuals to
53 | use, which is precisely where it is most unacceptable. Therefore, we have designed
54 | this version of the GPL to prohibit the practice for those products. If such problems
55 | arise substantially in other domains, we stand ready to extend this provision to
56 | those domains in future versions of the GPL, as needed to protect the freedom of
57 | users.
58 |
59 | Finally, every program is threatened constantly by software patents. States should
60 | not allow patents to restrict development and use of software on general-purpose
61 | computers, but in those that do, we wish to avoid the special danger that patents
62 | applied to a free program could make it effectively proprietary. To prevent this, the
63 | GPL assures that patents cannot be used to render the program non-free.
64 |
65 | The precise terms and conditions for copying, distribution and modification follow.
66 |
67 | ## TERMS AND CONDITIONS
68 |
69 | ### 0. Definitions.
70 |
71 | “This License” refers to version 3 of the GNU General Public License.
72 |
73 | “Copyright” also means copyright-like laws that apply to other kinds of
74 | works, such as semiconductor masks.
75 |
76 | “The Program” refers to any copyrightable work licensed under this
77 | License. Each licensee is addressed as “you”. “Licensees” and
78 | “recipients” may be individuals or organizations.
79 |
80 | To “modify” a work means to copy from or adapt all or part of the work in
81 | a fashion requiring copyright permission, other than the making of an exact copy. The
82 | resulting work is called a “modified version” of the earlier work or a
83 | work “based on” the earlier work.
84 |
85 | A “covered work” means either the unmodified Program or a work based on
86 | the Program.
87 |
88 | To “propagate” a work means to do anything with it that, without
89 | permission, would make you directly or secondarily liable for infringement under
90 | applicable copyright law, except executing it on a computer or modifying a private
91 | copy. Propagation includes copying, distribution (with or without modification),
92 | making available to the public, and in some countries other activities as well.
93 |
94 | To “convey” a work means any kind of propagation that enables other
95 | parties to make or receive copies. Mere interaction with a user through a computer
96 | network, with no transfer of a copy, is not conveying.
97 |
98 | An interactive user interface displays “Appropriate Legal Notices” to the
99 | extent that it includes a convenient and prominently visible feature that (1)
100 | displays an appropriate copyright notice, and (2) tells the user that there is no
101 | warranty for the work (except to the extent that warranties are provided), that
102 | licensees may convey the work under this License, and how to view a copy of this
103 | License. If the interface presents a list of user commands or options, such as a
104 | menu, a prominent item in the list meets this criterion.
105 |
106 | ### 1. Source Code.
107 |
108 | The “source code” for a work means the preferred form of the work for
109 | making modifications to it. “Object code” means any non-source form of a
110 | work.
111 |
112 | A “Standard Interface” means an interface that either is an official
113 | standard defined by a recognized standards body, or, in the case of interfaces
114 | specified for a particular programming language, one that is widely used among
115 | developers working in that language.
116 |
117 | The “System Libraries” of an executable work include anything, other than
118 | the work as a whole, that (a) is included in the normal form of packaging a Major
119 | Component, but which is not part of that Major Component, and (b) serves only to
120 | enable use of the work with that Major Component, or to implement a Standard
121 | Interface for which an implementation is available to the public in source code form.
122 | A “Major Component”, in this context, means a major essential component
123 | (kernel, window system, and so on) of the specific operating system (if any) on which
124 | the executable work runs, or a compiler used to produce the work, or an object code
125 | interpreter used to run it.
126 |
127 | The “Corresponding Source” for a work in object code form means all the
128 | source code needed to generate, install, and (for an executable work) run the object
129 | code and to modify the work, including scripts to control those activities. However,
130 | it does not include the work's System Libraries, or general-purpose tools or
131 | generally available free programs which are used unmodified in performing those
132 | activities but which are not part of the work. For example, Corresponding Source
133 | includes interface definition files associated with source files for the work, and
134 | the source code for shared libraries and dynamically linked subprograms that the work
135 | is specifically designed to require, such as by intimate data communication or
136 | control flow between those subprograms and other parts of the work.
137 |
138 | The Corresponding Source need not include anything that users can regenerate
139 | automatically from other parts of the Corresponding Source.
140 |
141 | The Corresponding Source for a work in source code form is that same work.
142 |
143 | ### 2. Basic Permissions.
144 |
145 | All rights granted under this License are granted for the term of copyright on the
146 | Program, and are irrevocable provided the stated conditions are met. This License
147 | explicitly affirms your unlimited permission to run the unmodified Program. The
148 | output from running a covered work is covered by this License only if the output,
149 | given its content, constitutes a covered work. This License acknowledges your rights
150 | of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not convey, without
153 | conditions so long as your license otherwise remains in force. You may convey covered
154 | works to others for the sole purpose of having them make modifications exclusively
155 | for you, or provide you with facilities for running those works, provided that you
156 | comply with the terms of this License in conveying all material for which you do not
157 | control copyright. Those thus making or running the covered works for you must do so
158 | exclusively on your behalf, under your direction and control, on terms that prohibit
159 | them from making any copies of your copyrighted material outside their relationship
160 | with you.
161 |
162 | Conveying under any other circumstances is permitted solely under the conditions
163 | stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
164 |
165 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
166 |
167 | No covered work shall be deemed part of an effective technological measure under any
168 | applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
169 | adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
170 | of such measures.
171 |
172 | When you convey a covered work, you waive any legal power to forbid circumvention of
173 | technological measures to the extent such circumvention is effected by exercising
174 | rights under this License with respect to the covered work, and you disclaim any
175 | intention to limit operation or modification of the work as a means of enforcing,
176 | against the work's users, your or third parties' legal rights to forbid circumvention
177 | of technological measures.
178 |
179 | ### 4. Conveying Verbatim Copies.
180 |
181 | You may convey verbatim copies of the Program's source code as you receive it, in any
182 | medium, provided that you conspicuously and appropriately publish on each copy an
183 | appropriate copyright notice; keep intact all notices stating that this License and
184 | any non-permissive terms added in accord with section 7 apply to the code; keep
185 | intact all notices of the absence of any warranty; and give all recipients a copy of
186 | this License along with the Program.
187 |
188 | You may charge any price or no price for each copy that you convey, and you may offer
189 | support or warranty protection for a fee.
190 |
191 | ### 5. Conveying Modified Source Versions.
192 |
193 | You may convey a work based on the Program, or the modifications to produce it from
194 | the Program, in the form of source code under the terms of section 4, provided that
195 | you also meet all of these conditions:
196 |
197 | * **a)** The work must carry prominent notices stating that you modified it, and giving a
198 | relevant date.
199 | * **b)** The work must carry prominent notices stating that it is released under this
200 | License and any conditions added under section 7. This requirement modifies the
201 | requirement in section 4 to “keep intact all notices”.
202 | * **c)** You must license the entire work, as a whole, under this License to anyone who
203 | comes into possession of a copy. This License will therefore apply, along with any
204 | applicable section 7 additional terms, to the whole of the work, and all its parts,
205 | regardless of how they are packaged. This License gives no permission to license the
206 | work in any other way, but it does not invalidate such permission if you have
207 | separately received it.
208 | * **d)** If the work has interactive user interfaces, each must display Appropriate Legal
209 | Notices; however, if the Program has interactive interfaces that do not display
210 | Appropriate Legal Notices, your work need not make them do so.
211 |
212 | A compilation of a covered work with other separate and independent works, which are
213 | not by their nature extensions of the covered work, and which are not combined with
214 | it such as to form a larger program, in or on a volume of a storage or distribution
215 | medium, is called an “aggregate” if the compilation and its resulting
216 | copyright are not used to limit the access or legal rights of the compilation's users
217 | beyond what the individual works permit. Inclusion of a covered work in an aggregate
218 | does not cause this License to apply to the other parts of the aggregate.
219 |
220 | ### 6. Conveying Non-Source Forms.
221 |
222 | You may convey a covered work in object code form under the terms of sections 4 and
223 | 5, provided that you also convey the machine-readable Corresponding Source under the
224 | terms of this License, in one of these ways:
225 |
226 | * **a)** Convey the object code in, or embodied in, a physical product (including a
227 | physical distribution medium), accompanied by the Corresponding Source fixed on a
228 | durable physical medium customarily used for software interchange.
229 | * **b)** Convey the object code in, or embodied in, a physical product (including a
230 | physical distribution medium), accompanied by a written offer, valid for at least
231 | three years and valid for as long as you offer spare parts or customer support for
232 | that product model, to give anyone who possesses the object code either (1) a copy of
233 | the Corresponding Source for all the software in the product that is covered by this
234 | License, on a durable physical medium customarily used for software interchange, for
235 | a price no more than your reasonable cost of physically performing this conveying of
236 | source, or (2) access to copy the Corresponding Source from a network server at no
237 | charge.
238 | * **c)** Convey individual copies of the object code with a copy of the written offer to
239 | provide the Corresponding Source. This alternative is allowed only occasionally and
240 | noncommercially, and only if you received the object code with such an offer, in
241 | accord with subsection 6b.
242 | * **d)** Convey the object code by offering access from a designated place (gratis or for
243 | a charge), and offer equivalent access to the Corresponding Source in the same way
244 | through the same place at no further charge. You need not require recipients to copy
245 | the Corresponding Source along with the object code. If the place to copy the object
246 | code is a network server, the Corresponding Source may be on a different server
247 | (operated by you or a third party) that supports equivalent copying facilities,
248 | provided you maintain clear directions next to the object code saying where to find
249 | the Corresponding Source. Regardless of what server hosts the Corresponding Source,
250 | you remain obligated to ensure that it is available for as long as needed to satisfy
251 | these requirements.
252 | * **e)** Convey the object code using peer-to-peer transmission, provided you inform
253 | other peers where the object code and Corresponding Source of the work are being
254 | offered to the general public at no charge under subsection 6d.
255 |
256 | A separable portion of the object code, whose source code is excluded from the
257 | Corresponding Source as a System Library, need not be included in conveying the
258 | object code work.
259 |
260 | A “User Product” is either (1) a “consumer product”, which
261 | means any tangible personal property which is normally used for personal, family, or
262 | household purposes, or (2) anything designed or sold for incorporation into a
263 | dwelling. In determining whether a product is a consumer product, doubtful cases
264 | shall be resolved in favor of coverage. For a particular product received by a
265 | particular user, “normally used” refers to a typical or common use of
266 | that class of product, regardless of the status of the particular user or of the way
267 | in which the particular user actually uses, or expects or is expected to use, the
268 | product. A product is a consumer product regardless of whether the product has
269 | substantial commercial, industrial or non-consumer uses, unless such uses represent
270 | the only significant mode of use of the product.
271 |
272 | “Installation Information” for a User Product means any methods,
273 | procedures, authorization keys, or other information required to install and execute
274 | modified versions of a covered work in that User Product from a modified version of
275 | its Corresponding Source. The information must suffice to ensure that the continued
276 | functioning of the modified object code is in no case prevented or interfered with
277 | solely because modification has been made.
278 |
279 | If you convey an object code work under this section in, or with, or specifically for
280 | use in, a User Product, and the conveying occurs as part of a transaction in which
281 | the right of possession and use of the User Product is transferred to the recipient
282 | in perpetuity or for a fixed term (regardless of how the transaction is
283 | characterized), the Corresponding Source conveyed under this section must be
284 | accompanied by the Installation Information. But this requirement does not apply if
285 | neither you nor any third party retains the ability to install modified object code
286 | on the User Product (for example, the work has been installed in ROM).
287 |
288 | The requirement to provide Installation Information does not include a requirement to
289 | continue to provide support service, warranty, or updates for a work that has been
290 | modified or installed by the recipient, or for the User Product in which it has been
291 | modified or installed. Access to a network may be denied when the modification itself
292 | materially and adversely affects the operation of the network or violates the rules
293 | and protocols for communication across the network.
294 |
295 | Corresponding Source conveyed, and Installation Information provided, in accord with
296 | this section must be in a format that is publicly documented (and with an
297 | implementation available to the public in source code form), and must require no
298 | special password or key for unpacking, reading or copying.
299 |
300 | ### 7. Additional Terms.
301 |
302 | “Additional permissions” are terms that supplement the terms of this
303 | License by making exceptions from one or more of its conditions. Additional
304 | permissions that are applicable to the entire Program shall be treated as though they
305 | were included in this License, to the extent that they are valid under applicable
306 | law. If additional permissions apply only to part of the Program, that part may be
307 | used separately under those permissions, but the entire Program remains governed by
308 | this License without regard to the additional permissions.
309 |
310 | When you convey a copy of a covered work, you may at your option remove any
311 | additional permissions from that copy, or from any part of it. (Additional
312 | permissions may be written to require their own removal in certain cases when you
313 | modify the work.) You may place additional permissions on material, added by you to a
314 | covered work, for which you have or can give appropriate copyright permission.
315 |
316 | Notwithstanding any other provision of this License, for material you add to a
317 | covered work, you may (if authorized by the copyright holders of that material)
318 | supplement the terms of this License with terms:
319 |
320 | * **a)** Disclaiming warranty or limiting liability differently from the terms of
321 | sections 15 and 16 of this License; or
322 | * **b)** Requiring preservation of specified reasonable legal notices or author
323 | attributions in that material or in the Appropriate Legal Notices displayed by works
324 | containing it; or
325 | * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
326 | modified versions of such material be marked in reasonable ways as different from the
327 | original version; or
328 | * **d)** Limiting the use for publicity purposes of names of licensors or authors of the
329 | material; or
330 | * **e)** Declining to grant rights under trademark law for use of some trade names,
331 | trademarks, or service marks; or
332 | * **f)** Requiring indemnification of licensors and authors of that material by anyone
333 | who conveys the material (or modified versions of it) with contractual assumptions of
334 | liability to the recipient, for any liability that these contractual assumptions
335 | directly impose on those licensors and authors.
336 |
337 | All other non-permissive additional terms are considered “further
338 | restrictions” within the meaning of section 10. If the Program as you received
339 | it, or any part of it, contains a notice stating that it is governed by this License
340 | along with a term that is a further restriction, you may remove that term. If a
341 | license document contains a further restriction but permits relicensing or conveying
342 | under this License, you may add to a covered work material governed by the terms of
343 | that license document, provided that the further restriction does not survive such
344 | relicensing or conveying.
345 |
346 | If you add terms to a covered work in accord with this section, you must place, in
347 | the relevant source files, a statement of the additional terms that apply to those
348 | files, or a notice indicating where to find the applicable terms.
349 |
350 | Additional terms, permissive or non-permissive, may be stated in the form of a
351 | separately written license, or stated as exceptions; the above requirements apply
352 | either way.
353 |
354 | ### 8. Termination.
355 |
356 | You may not propagate or modify a covered work except as expressly provided under
357 | this License. Any attempt otherwise to propagate or modify it is void, and will
358 | automatically terminate your rights under this License (including any patent licenses
359 | granted under the third paragraph of section 11).
360 |
361 | However, if you cease all violation of this License, then your license from a
362 | particular copyright holder is reinstated (a) provisionally, unless and until the
363 | copyright holder explicitly and finally terminates your license, and (b) permanently,
364 | if the copyright holder fails to notify you of the violation by some reasonable means
365 | prior to 60 days after the cessation.
366 |
367 | Moreover, your license from a particular copyright holder is reinstated permanently
368 | if the copyright holder notifies you of the violation by some reasonable means, this
369 | is the first time you have received notice of violation of this License (for any
370 | work) from that copyright holder, and you cure the violation prior to 30 days after
371 | your receipt of the notice.
372 |
373 | Termination of your rights under this section does not terminate the licenses of
374 | parties who have received copies or rights from you under this License. If your
375 | rights have been terminated and not permanently reinstated, you do not qualify to
376 | receive new licenses for the same material under section 10.
377 |
378 | ### 9. Acceptance Not Required for Having Copies.
379 |
380 | You are not required to accept this License in order to receive or run a copy of the
381 | Program. Ancillary propagation of a covered work occurring solely as a consequence of
382 | using peer-to-peer transmission to receive a copy likewise does not require
383 | acceptance. However, nothing other than this License grants you permission to
384 | propagate or modify any covered work. These actions infringe copyright if you do not
385 | accept this License. Therefore, by modifying or propagating a covered work, you
386 | indicate your acceptance of this License to do so.
387 |
388 | ### 10. Automatic Licensing of Downstream Recipients.
389 |
390 | Each time you convey a covered work, the recipient automatically receives a license
391 | from the original licensors, to run, modify and propagate that work, subject to this
392 | License. You are not responsible for enforcing compliance by third parties with this
393 | License.
394 |
395 | An “entity transaction” is a transaction transferring control of an
396 | organization, or substantially all assets of one, or subdividing an organization, or
397 | merging organizations. If propagation of a covered work results from an entity
398 | transaction, each party to that transaction who receives a copy of the work also
399 | receives whatever licenses to the work the party's predecessor in interest had or
400 | could give under the previous paragraph, plus a right to possession of the
401 | Corresponding Source of the work from the predecessor in interest, if the predecessor
402 | has it or can get it with reasonable efforts.
403 |
404 | You may not impose any further restrictions on the exercise of the rights granted or
405 | affirmed under this License. For example, you may not impose a license fee, royalty,
406 | or other charge for exercise of rights granted under this License, and you may not
407 | initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
408 | that any patent claim is infringed by making, using, selling, offering for sale, or
409 | importing the Program or any portion of it.
410 |
411 | ### 11. Patents.
412 |
413 | A “contributor” is a copyright holder who authorizes use under this
414 | License of the Program or a work on which the Program is based. The work thus
415 | licensed is called the contributor's “contributor version”.
416 |
417 | A contributor's “essential patent claims” are all patent claims owned or
418 | controlled by the contributor, whether already acquired or hereafter acquired, that
419 | would be infringed by some manner, permitted by this License, of making, using, or
420 | selling its contributor version, but do not include claims that would be infringed
421 | only as a consequence of further modification of the contributor version. For
422 | purposes of this definition, “control” includes the right to grant patent
423 | sublicenses in a manner consistent with the requirements of this License.
424 |
425 | Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
426 | under the contributor's essential patent claims, to make, use, sell, offer for sale,
427 | import and otherwise run, modify and propagate the contents of its contributor
428 | version.
429 |
430 | In the following three paragraphs, a “patent license” is any express
431 | agreement or commitment, however denominated, not to enforce a patent (such as an
432 | express permission to practice a patent or covenant not to sue for patent
433 | infringement). To “grant” such a patent license to a party means to make
434 | such an agreement or commitment not to enforce a patent against the party.
435 |
436 | If you convey a covered work, knowingly relying on a patent license, and the
437 | Corresponding Source of the work is not available for anyone to copy, free of charge
438 | and under the terms of this License, through a publicly available network server or
439 | other readily accessible means, then you must either (1) cause the Corresponding
440 | Source to be so available, or (2) arrange to deprive yourself of the benefit of the
441 | patent license for this particular work, or (3) arrange, in a manner consistent with
442 | the requirements of this License, to extend the patent license to downstream
443 | recipients. “Knowingly relying” means you have actual knowledge that, but
444 | for the patent license, your conveying the covered work in a country, or your
445 | recipient's use of the covered work in a country, would infringe one or more
446 | identifiable patents in that country that you have reason to believe are valid.
447 |
448 | If, pursuant to or in connection with a single transaction or arrangement, you
449 | convey, or propagate by procuring conveyance of, a covered work, and grant a patent
450 | license to some of the parties receiving the covered work authorizing them to use,
451 | propagate, modify or convey a specific copy of the covered work, then the patent
452 | license you grant is automatically extended to all recipients of the covered work and
453 | works based on it.
454 |
455 | A patent license is “discriminatory” if it does not include within the
456 | scope of its coverage, prohibits the exercise of, or is conditioned on the
457 | non-exercise of one or more of the rights that are specifically granted under this
458 | License. You may not convey a covered work if you are a party to an arrangement with
459 | a third party that is in the business of distributing software, under which you make
460 | payment to the third party based on the extent of your activity of conveying the
461 | work, and under which the third party grants, to any of the parties who would receive
462 | the covered work from you, a discriminatory patent license (a) in connection with
463 | copies of the covered work conveyed by you (or copies made from those copies), or (b)
464 | primarily for and in connection with specific products or compilations that contain
465 | the covered work, unless you entered into that arrangement, or that patent license
466 | was granted, prior to 28 March 2007.
467 |
468 | Nothing in this License shall be construed as excluding or limiting any implied
469 | license or other defenses to infringement that may otherwise be available to you
470 | under applicable patent law.
471 |
472 | ### 12. No Surrender of Others' Freedom.
473 |
474 | If conditions are imposed on you (whether by court order, agreement or otherwise)
475 | that contradict the conditions of this License, they do not excuse you from the
476 | conditions of this License. If you cannot convey a covered work so as to satisfy
477 | simultaneously your obligations under this License and any other pertinent
478 | obligations, then as a consequence you may not convey it at all. For example, if you
479 | agree to terms that obligate you to collect a royalty for further conveying from
480 | those to whom you convey the Program, the only way you could satisfy both those terms
481 | and this License would be to refrain entirely from conveying the Program.
482 |
483 | ### 13. Use with the GNU Affero General Public License.
484 |
485 | Notwithstanding any other provision of this License, you have permission to link or
486 | combine any covered work with a work licensed under version 3 of the GNU Affero
487 | General Public License into a single combined work, and to convey the resulting work.
488 | The terms of this License will continue to apply to the part which is the covered
489 | work, but the special requirements of the GNU Affero General Public License, section
490 | 13, concerning interaction through a network will apply to the combination as such.
491 |
492 | ### 14. Revised Versions of this License.
493 |
494 | The Free Software Foundation may publish revised and/or new versions of the GNU
495 | General Public License from time to time. Such new versions will be similar in spirit
496 | to the present version, but may differ in detail to address new problems or concerns.
497 |
498 | Each version is given a distinguishing version number. If the Program specifies that
499 | a certain numbered version of the GNU General Public License “or any later
500 | version” applies to it, you have the option of following the terms and
501 | conditions either of that numbered version or of any later version published by the
502 | Free Software Foundation. If the Program does not specify a version number of the GNU
503 | General Public License, you may choose any version ever published by the Free
504 | Software Foundation.
505 |
506 | If the Program specifies that a proxy can decide which future versions of the GNU
507 | General Public License can be used, that proxy's public statement of acceptance of a
508 | version permanently authorizes you to choose that version for the Program.
509 |
510 | Later license versions may give you additional or different permissions. However, no
511 | additional obligations are imposed on any author or copyright holder as a result of
512 | your choosing to follow a later version.
513 |
514 | ### 15. Disclaimer of Warranty.
515 |
516 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
517 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
518 | PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
519 | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
520 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
521 | QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
522 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
523 |
524 | ### 16. Limitation of Liability.
525 |
526 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
527 | COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
528 | PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
529 | INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
530 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
531 | OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
532 | WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
533 | POSSIBILITY OF SUCH DAMAGES.
534 |
535 | ### 17. Interpretation of Sections 15 and 16.
536 |
537 | If the disclaimer of warranty and limitation of liability provided above cannot be
538 | given local legal effect according to their terms, reviewing courts shall apply local
539 | law that most closely approximates an absolute waiver of all civil liability in
540 | connection with the Program, unless a warranty or assumption of liability accompanies
541 | a copy of the Program in return for a fee.
542 |
543 | END OF TERMS AND CONDITIONS
544 |
545 | ## How to Apply These Terms to Your New Programs
546 |
547 | If you develop a new program, and you want it to be of the greatest possible use to
548 | the public, the best way to achieve this is to make it free software which everyone
549 | can redistribute and change under these terms.
550 |
551 | To do so, attach the following notices to the program. It is safest to attach them
552 | to the start of each source file to most effectively state the exclusion of warranty;
553 | and each file should have at least the “copyright” line and a pointer to
554 | where the full notice is found.
555 |
556 |
557 | Copyright (C)
558 |
559 | This program is free software: you can redistribute it and/or modify
560 | it under the terms of the GNU General Public License as published by
561 | the Free Software Foundation, either version 3 of the License, or
562 | (at your option) any later version.
563 |
564 | This program is distributed in the hope that it will be useful,
565 | but WITHOUT ANY WARRANTY; without even the implied warranty of
566 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
567 | GNU General Public License for more details.
568 |
569 | You should have received a copy of the GNU General Public License
570 | along with this program. If not, see .
571 |
572 | Also add information on how to contact you by electronic and paper mail.
573 |
574 | If the program does terminal interaction, make it output a short notice like this
575 | when it starts in an interactive mode:
576 |
577 | Copyright (C)
578 | This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
579 | This is free software, and you are welcome to redistribute it
580 | under certain conditions; type 'show c' for details.
581 |
582 | The hypothetical commands 'show w' and 'show c' should show the appropriate parts of
583 | the General Public License. Of course, your program's commands might be different;
584 | for a GUI interface, you would use an “about box”.
585 |
586 | You should also get your employer (if you work as a programmer) or school, if any, to
587 | sign a “copyright disclaimer” for the program, if necessary. For more
588 | information on this, and how to apply and follow the GNU GPL, see
589 | <>.
590 |
591 | The GNU General Public License does not permit incorporating your program into
592 | proprietary programs. If your program is a subroutine library, you may consider it
593 | more useful to permit linking proprietary applications with the library. If this is
594 | what you want to do, use the GNU Lesser General Public License instead of this
595 | License. But first, please read
596 | <>.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # js-pptx
2 | Pure Javascript reader/writer/editor for PowerPoint, for use in Node.js or the browser.
3 |
4 |
5 | # Design goals
6 | * Read/edit/author PowerPoint .pptx files
7 | * Pure Javascript with clean IP
8 | * Run in browser and/or Node.js
9 | * Friendly API for basic tasks, like text, shapes, charts, tables
10 | * Access to raw XML for when you need to be very specific
11 | * Rigorous testing
12 |
13 |
14 | # Current status
15 | Early in development. It can currently:
16 | * read an existing PPTX file
17 | * retain all existing content
18 | * add slides, shapes, and charts
19 | * save as a PPTX file
20 | * basic unit tests
21 |
22 | What it cannot yet do is:
23 | * Programmatically retrieve / query / edit existing slides
24 | * Generate themes, layouts, masters, animations, etc.
25 |
26 | # License
27 | GNU General Public License (GPL)
28 |
29 | # Install
30 |
31 | In node.js
32 | ```
33 | npm install protobi/js-pptx
34 | ```
35 |
36 | In the browser: **(Not yet implemented)**
37 | ```
38 |
39 | ```
40 |
41 | # Dependencies
42 | * [xml2js](https://github.com/nfarina/xmldoc)
43 | * [async](https://github.com/caolan/async)
44 | * [jszip](https://stuk.github.io/jszip)
45 |
46 | # Usage
47 |
48 | ```js
49 | var PPTX = require('../lib/pptx');
50 | var fs = require('fs');
51 |
52 | var INFILE = './test/files/parts3.pptx';
53 | var OUTFILE = './test/files/parts3-a.pptx';
54 |
55 | fs.readFile(INFILE, function (err, data) {
56 | if (err) throw err;
57 |
58 | var pptx = new PPTX.Presentation();
59 | pptx.load(data, function (err) {
60 |
61 | var slide1 = pptx.getSlide('slide1');
62 | var shapes = slide1.getShapes();
63 |
64 | var shapes = slide1.getShapes()
65 | shapes[3]
66 | .text("Now it's a trapezoid")
67 | .shapeProperties()
68 | .x(PPTX.emu.inch(1))
69 | .y(PPTX.emu.inch(1))
70 | .cx(PPTX.emu.inch(2))
71 | .cy(PPTX.emu.inch(0.75))
72 | .prstGeom('trapezoid');
73 | });
74 | });
75 | });
76 |
77 | ```
78 |
79 | # Inspiration / Motivation
80 | Inspired by [officegen](https://github.com/ZivBarber/officegen),
81 | which creates pptx with text/shapes/images/tables/charts wonderfully (but does not read existing PPT files).
82 |
83 | Also inspired by [js-xlsx](https://github.com/SheetJS/js-xlsx)
84 | which reads/writes XLSX/XLS/XLSB, works in the browser and Node.js, and has an incredibly
85 | thorough test suite (but does not read or write PowerPoint).
86 |
87 | Motivated by desire to read and modify existing presentations, to inherit their themes, layouts and possibly content,
88 | and work in the browser if possible.
89 |
90 | https://github.com/protobi/js-pptx/wiki/API
91 |
92 | # Design Philosophy
93 | The design concept is to represent the Office document at two levels of abstraction:
94 | * **Raw XML** The actual complete OpenXML representation, in all its detail
95 | * **Conceptual classes** Simple Javascript classes that provide a convenient API
96 |
97 | The conceptual classes provides a clear simple way to do common tasks, e.g. `Presentation().addSlide().addChart(data)`.
98 |
99 | The raw API provides a way to do anything that the OpenXML allows, even if it's not yet in the conceptual classes, e.g.
100 | e.g. `Presentation.getSlide(3).getShape(4).get('a:prstGeom').attr('prst', 'trapezoid')`
101 |
102 |
103 | This solves a major dilemma in existing projects, which have many issue reports like "Please add this crucial feature to the API".
104 | By being able to access the raw XML, all the features in OpenXML are available, while we make many of them more convenient.
105 |
106 | The technical approach here uses:
107 | * `JSZip` to unzip an existing `.pptx` file and zip it back,
108 | * `xml2js` to convert the XML to Javascript and back to XML.
109 |
110 | Converting to Javascript allows the content to be manipulated programmatically. For each major entity, a Javascript class is created,
111 | such as:
112 | * PPTX.Presentation
113 | * PPTX.Slide
114 | * PPTX.Shape
115 | * PPTX.spPr // ShapeProperties
116 | * etc.
117 |
118 | These classes allow properties to be set, and chained in a manner similar to d3 or jQuery.
119 | The Javascript classes provide syntactic sugar, as a convenient way to query and modify the presentation.
120 |
121 | But we can't possibly create a Javascript class that covers every entity and option defined in OpenXML.
122 | So each of these classes exposes the XML-to-Javascript object as a property `.content`, giving you theoretically
123 | direct access to anything in the OpenXML standard, enabling you to take over
124 | whenever the pre-defined features don't yet cover your particular use case.
125 |
126 | It's up to you of course, to make sure that those changes convert to valid XML. Debugging PPTX is a pain.
127 |
128 | Right now, this uses English names for high-level constructs (e.g. `Presentation` and `Slide`),
129 | but for lower level constructs uses names that directly mirror the OpenXML tagNames (e.g. `spPr` for ShapeProperties).
130 |
131 | The challenge is it'll be a lot easier to extend the library if we follow the OpenXML tag names,
132 | but the OpenXML tag names are so cryptic that they don't make great names for a Javascript library.
133 |
134 | So we default to using the English name is used when returning objects even if the object has a cryptic class name, e.g.:
135 | * `Slide.getShapes()` returns an array of `Shape` objects and
136 | * `Shape.shapeProperties()` returns an `spPr` object.
137 |
138 | Ideally would be consistent, and am working out which way to go. Advice is welcome!
139 |
140 | This library currently assumes it's starting from an existing presentation, and doesn't (yet) create one from scratch.
141 | This allows you to use existing themes, styles and layouts.
142 |
143 |
144 | # License
145 | GNU General Public License (GPL)
146 |
147 | # Install
148 |
149 | In node.js
150 | ```
151 | npm install protobi/js-pptx
152 | ```
153 |
154 | In the browser:
155 | ```
156 | // will use browserify but right now not yet implemented
157 | ```
158 |
159 | # Dependencies
160 | * [xml2js](https://github.com/nfarina/xmldoc)
161 | * [async](https://github.com/caolan/async)
162 | * [jszip](https://stuk.github.io/jszip)
163 |
164 | # Usage
165 |
166 | ```js
167 | "use strict";
168 |
169 | var fs = require("fs");
170 | var PPTX = require('..');
171 |
172 |
173 | var INFILE = './test/files/minimal.pptx'; // a blank PPTX file with my layouts, themes, masters.
174 | var OUTFILE = '/tmp/example.pptx';
175 |
176 | fs.readFile(INFILE, function (err, data) {
177 | if (err) throw err;
178 | var pptx = new PPTX.Presentation();
179 | pptx.load(data, function (err) {
180 | var slide1 = pptx.getSlide('slide1');
181 |
182 | var slide2 = pptx.addSlide("slideLayout3"); // section divider
183 | var slide3 = pptx.addSlide("slideLayout6"); // title only
184 |
185 |
186 | var triangle = slide1.addShape()
187 | .text("Triangle")
188 | .shapeProperties()
189 | .x(PPTX.emu.inch(2))
190 | .y(PPTX.emu.inch(2))
191 | .cx(PPTX.emu.inch(2))
192 | .cy(PPTX.emu.inch(2))
193 | .prstGeom('triangle');
194 |
195 | var triangle = slide1.addShape()
196 | .text("Ellipse")
197 | .shapeProperties()
198 | .x(PPTX.emu.inch(4))
199 | .y(PPTX.emu.inch(4))
200 | .cx(PPTX.emu.inch(2))
201 | .cy(PPTX.emu.inch(1))
202 | .prstGeom('ellipse');
203 |
204 | for (var i = 0; i < 20; i++) {
205 | slide2.addShape()
206 | .text("" + i)
207 | .shapeProperties()
208 | .x(PPTX.emu.inch((Math.random() * 10)))
209 | .y(PPTX.emu.inch((Math.random() * 6)))
210 | .cx(PPTX.emu.inch(1))
211 | .cy(PPTX.emu.inch(1))
212 | .prstGeom('ellipse');
213 | }
214 |
215 | slide1.getShapes()[3]
216 | .text("Now it's a trapezoid")
217 | .shapeProperties()
218 | .x(PPTX.emu.inch(1))
219 | .y(PPTX.emu.inch(1))
220 | .cx(PPTX.emu.inch(2))
221 | .cy(PPTX.emu.inch(0.75))
222 | .prstGeom('trapezoid');
223 |
224 | var chart = slide3.addChart(barChart, function (err, chart) {
225 |
226 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
227 | if (err) throw err;
228 | console.log("open " + OUTFILE)
229 | });
230 | });
231 | });
232 | });
233 |
234 | var barChart = {
235 | title: 'Sample bar chart',
236 | renderType: 'bar',
237 | data: [
238 | {
239 | name: 'Series 1',
240 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
241 | values: [4.3, 2.5, 3.5, 4.5]
242 | },
243 | {
244 | name: 'Series 2',
245 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
246 | values: [2.4, 4.4, 1.8, 2.8]
247 | },
248 | {
249 | name: 'Series 3',
250 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
251 | values: [2.0, 2.0, 3.0, 5.0]
252 | }
253 | ]
254 | }
255 | ```
256 |
257 | # Next steps
258 | * Browserify and test in browser
259 | * Publish to bower
260 | * Add tables
261 | * Add images
262 | * Set presentation properties
263 | * Set theme
264 | * Set layouts
265 | * Set masters
266 |
267 | # Contribute
268 |
269 | ###Test:
270 | `npm test`
271 |
272 | ###Build:
273 | `npm run build`
274 |
275 | ###Minify:
276 | `npm run minify`
277 |
278 | ###All:
279 | `npm run all`
280 |
281 |
--------------------------------------------------------------------------------
/api.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/api.md
--------------------------------------------------------------------------------
/baby/step10_xml_parse_modify_generate.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs");
4 | var PPTX = require('../lib/pptx');
5 | var Query = require('query');
6 |
7 | Array.prototype.get = function (filter) {
8 | return Query.query(this, filter);
9 | }
10 |
11 | //console.log([{a: 1}, {a:2}, {a:3}].query({a: { $gt: 1}}))
12 |
13 |
14 | var INFILE = './lab/parts3/parts3.pptx';
15 | var OUTFILE = './lab/parts2/parts2.pptx';
16 |
17 |
18 | fs.readFile(INFILE, function (err, data) {
19 | if (err) throw err;
20 | var pptx = new PPTX.Presentation();
21 | pptx.load(data, function (err) {
22 |
23 |
24 | var slide1 = pptx.getSlide('slide1');
25 | var shapes = slide1.getShapes();
26 | console.log(shapes[3].text())
27 |
28 | console.log(JSON.stringify(shapes[3].shapeProperties().toJSON(),null,4))
29 |
30 | shapes[3].shapeProperties().x(PPTX.emu.inch(1))
31 | shapes[3].shapeProperties().y(PPTX.emu.inch(1))
32 |
33 | shapes[3].shapeProperties().cx(PPTX.emu.inch(2))
34 | shapes[3].shapeProperties().cy(PPTX.emu.inch(0.75))
35 |
36 | shapes[3].shapeProperties().prstGeom('trapezoid');
37 |
38 | console.log(JSON.stringify(shapes[3].shapeProperties().toJSON(),null,4))
39 |
40 | //fs.writeFile('./lab/parts2/parts2.json', JSON.stringify(pptx, null,4), 'utf8');
41 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
42 | if (err) throw err;
43 | });
44 | });
45 | });
46 |
47 |
--------------------------------------------------------------------------------
/baby/step11_buildexcel.js:
--------------------------------------------------------------------------------
1 | var excelbuilder = require('msexcel-builder');
2 |
3 | // Create a new workbook file in current working-path
4 | var workbook = excelbuilder.createWorkbook('./', 'sample.xlsx')
5 |
6 | // Create a new worksheet with 10 columns and 12 rows
7 | var sheet1 = workbook.createSheet('sheet1', 10, 12);
8 |
9 | // Fill some data
10 | sheet1.set(1, 1, 'I am title');
11 | for (var i = 2; i < 5; i++)
12 | sheet1.set(i, 1, 'test'+i);
13 |
14 | // Save it
15 | workbook.save(function(err){
16 | if (err)
17 | workbook.cancel();
18 | else
19 | console.log('congratulations, your workbook created');
20 | });
--------------------------------------------------------------------------------
/baby/step12_buildexcel_data.js:
--------------------------------------------------------------------------------
1 | var excelbuilder = require('msexcel-builder');
2 |
3 | // Create a new workbook file in current working-path
4 |
5 |
6 | var old = function(data, callback) {
7 |
8 |
9 | // Create a new worksheet with 10 columns and 12 rows
10 | var workbook = excelbuilder.createWorkbook('./', 'sample.xlsx')
11 | var sheet1 = workbook.createSheet('sheet1', 10, 12);
12 |
13 | // Fill some data
14 | sheet1.set(1, 1, 'I am title');
15 | for (var i = 2; i < 5; i++)
16 | sheet1.set(i, 1, 'test'+i);
17 |
18 | // Save it
19 | workbook.save(callback);
20 | };
21 |
22 |
23 | var GLOBAL_CHART_COUNT = 0;
24 | createWorkbook = function(data, callback) {
25 | GLOBAL_CHART_COUNT += 1;
26 | var tmpExcelFile = 'sample'+GLOBAL_CHART_COUNT+'.xlsx';
27 |
28 |
29 | // First, generate a temporatory excel file for storing the chart's data
30 | var workbook = excelbuilder.createWorkbook('./', tmpExcelFile);
31 |
32 | // Create a new worksheet with 10 columns and 12 rows
33 | // number of columns: data['data'].length+1 -> equaly number of series
34 | // number of rows: data['data'][0].values.length+1
35 | var sheet1 = workbook.createSheet('Sheet1', data['data'].length+1, data['data'][0].values.length+1);
36 | var headerrow = 1;
37 | console.log("STARTED WORKBOOK...");
38 |
39 | // write header using serie name
40 | for( var j=0; j < data['data'].length; j++ ) {
41 | sheet1.set(j+2, headerrow, data['data'][j].name);
42 | }
43 |
44 | // write category column in the first column
45 | for( var j=0; j < data['data'][0].labels.length; j++ ) {
46 | sheet1.set(1, j+2, data['data'][0].labels[j]);
47 | }
48 |
49 | // for each serie, write out values in its row
50 | for (var i = 0; i < data['data'].length; i++) {
51 | for( var j=0; j < data['data'][i].values.length; j++ )
52 | {
53 | // col i+2
54 | // row j+1
55 | sheet1.set(i+2, j+2, data['data'][i].values[j]);
56 | }
57 | }
58 | console.log("FILLING WORKBOOK...");
59 | // Fill some data
60 | // Save it
61 |
62 |
63 | console.log("SAVING WORKBOOK...");
64 | workbook.save(function(err){
65 | if (err) {
66 | workbook.cancel();
67 | callback(err);
68 | }
69 | else {
70 | console.log("SAVED: " + workbook.length);
71 | callback(null, workbook);
72 | }
73 | });
74 | };
75 |
76 |
77 | var barChart = {
78 | title: 'Sample bar chart',
79 | renderType: 'bar',
80 | xmlOptions: {
81 | "c:title": {
82 | "c:tx": {
83 | "c:rich": {
84 | "a:p": {
85 | "a:r": {
86 | "a:t": "Override title via XML"
87 | }
88 | }
89 | }
90 | }
91 | }
92 | },
93 | data: [
94 | {
95 | name: 'europe',
96 | labels: ['Y2003', 'Y2004', 'Y2005'],
97 | values: [2.5, 2.6, 2.8],
98 | color: 'ff0000'
99 | },
100 | {
101 | name: 'namerica',
102 | labels: ['Y2003', 'Y2004', 'Y2005'],
103 | values: [2.5, 2.7, 2.9],
104 | color: '00ff00'
105 | },
106 | {
107 | name: 'asia',
108 | labels: ['Y2003', 'Y2004', 'Y2005'],
109 | values: [2.1, 2.2, 2.4],
110 | color: '0000ff'
111 | },
112 | {
113 | name: 'lamerica',
114 | labels: ['Y2003', 'Y2004', 'Y2005'],
115 | values: [0.3, 0.3, 0.3],
116 | color: 'ffff00'
117 | },
118 | {
119 | name: 'meast',
120 | labels: ['Y2003', 'Y2004', 'Y2005'],
121 | values: [0.2, 0.3, 0.3],
122 | color: 'ff00ff'
123 | },
124 | {
125 | name: 'africa',
126 | labels: ['Y2003', 'Y2004', 'Y2005'],
127 | values: [0.1, 0.1, 0.1],
128 | color: '00ffff'
129 | }
130 |
131 | ]
132 | };
133 |
134 | createWorkbook(barChart, function(err, workbook) {console.log("ALL DONE"); });
135 |
136 | //old(barChart, function(err, workbook) {console.log("ALL DONE"); });
--------------------------------------------------------------------------------
/baby/step13_buildexcel_data.js:
--------------------------------------------------------------------------------
1 | var excelbuilder = require('msexcel-builder');
2 |
3 | // Create a new workbook file in current working-path
4 |
5 |
6 | var old = function(data, callback) {
7 |
8 |
9 | // Create a new worksheet with 10 columns and 12 rows
10 | var workbook = excelbuilder.createWorkbook('./', 'sample.xlsx')
11 | var sheet1 = workbook.createSheet('sheet1', 10, 12);
12 |
13 | // Fill some data
14 | sheet1.set(1, 1, 'I am title');
15 | for (var i = 2; i < 5; i++)
16 | sheet1.set(i, 1, 'test'+i);
17 |
18 | // Save it
19 | workbook.save(callback);
20 | };
21 |
22 |
23 | var GLOBAL_CHART_COUNT = 0;
24 | createWorkbook = function(data, callback) {
25 | GLOBAL_CHART_COUNT += 1;
26 | var tmpExcelFile = 'sample'+GLOBAL_CHART_COUNT+'.xlsx';
27 |
28 |
29 | // First, generate a temporatory excel file for storing the chart's data
30 | var workbook = excelbuilder.createWorkbook('./', tmpExcelFile);
31 |
32 | // Create a new worksheet with 10 columns and 12 rows
33 | // number of columns: data['data'].length+1 -> equaly number of series
34 | // number of rows: data['data'][0].values.length+1
35 | var sheet1 = workbook.createSheet('Sheet1', data['data'].length+1, data['data'][0].values.length+1);
36 | var headerrow = 1;
37 | console.log("STARTED WORKBOOK...");
38 |
39 | // write header using serie name
40 | for( var j=0; j < data['data'].length; j++ ) {
41 | sheet1.set(j+2, headerrow, data['data'][j].name);
42 | }
43 |
44 | // write category column in the first column
45 | for( var j=0; j < data['data'][0].labels.length; j++ ) {
46 | sheet1.set(1, j+2, data['data'][0].labels[j]);
47 | }
48 |
49 | // for each serie, write out values in its row
50 | for (var i = 0; i < data['data'].length; i++) {
51 | for( var j=0; j < data['data'][i].values.length; j++ )
52 | {
53 | // col i+2
54 | // row j+1
55 | sheet1.set(i+2, j+2, data['data'][i].values[j]);
56 | }
57 | }
58 | console.log("FILLING WORKBOOK...");
59 | // Fill some data
60 | // Save it
61 |
62 |
63 | console.log("SAVING WORKBOOK...");
64 | workbook.save(function(err){
65 | if (err) {
66 | workbook.cancel();
67 | callback(err);
68 | }
69 | else {
70 | console.log("SAVED: " + workbook.length);
71 | callback(null, workbook);
72 | }
73 | });
74 | };
75 |
76 |
77 | var barChart = {
78 | title: 'Sample bar chart',
79 | renderType: 'bar',
80 | xmlOptions: {
81 | "c:title": {
82 | "c:tx": {
83 | "c:rich": {
84 | "a:p": {
85 | "a:r": {
86 | "a:t": "Override title via XML"
87 | }
88 | }
89 | }
90 | }
91 | }
92 | },
93 | data: [
94 | {
95 | name: 'europe',
96 | labels: ['Y2003', 'Y2004', 'Y2005'],
97 | values: [2.5, 2.6, 2.8],
98 | color: 'ff0000'
99 | },
100 | {
101 | name: 'namerica',
102 | labels: ['Y2003', 'Y2004', 'Y2005'],
103 | values: [2.5, 2.7, 2.9],
104 | color: '00ff00'
105 | },
106 | {
107 | name: 'asia',
108 | labels: ['Y2003', 'Y2004', 'Y2005'],
109 | values: [2.1, 2.2, 2.4],
110 | color: '0000ff'
111 | },
112 | {
113 | name: 'lamerica',
114 | labels: ['Y2003', 'Y2004', 'Y2005'],
115 | values: [0.3, 0.3, 0.3],
116 | color: 'ffff00'
117 | },
118 | {
119 | name: 'meast',
120 | labels: ['Y2003', 'Y2004', 'Y2005'],
121 | values: [0.2, 0.3, 0.3],
122 | color: 'ff00ff'
123 | },
124 | {
125 | name: 'africa',
126 | labels: ['Y2003', 'Y2004', 'Y2005'],
127 | values: [0.1, 0.1, 0.1],
128 | color: '00ffff'
129 | }
130 |
131 | ]
132 | };
133 |
134 | createWorkbook(barChart, function(err, workbook) {console.log("ALL DONE"); });
135 |
136 | //old(barChart, function(err, workbook) {console.log("ALL DONE"); });
--------------------------------------------------------------------------------
/baby/step1_open_save.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // open an existing doc, unzip it, zip it, and save it as new name. See if it opens.
4 |
5 |
6 | var JSZip = require('jszip');
7 | var fs = require("fs");
8 |
9 | var INFILE = './lab/out/out.pptx';
10 | var OUTFILE = './lab/baby.pptx';
11 |
12 |
13 | // read a zip file
14 | fs.readFile(INFILE, function(err, data) {
15 | if (err) throw err;
16 | var zip = new JSZip(data);
17 | var buffer = zip.generate({type:"nodebuffer"});
18 |
19 | fs.writeFile(OUTFILE, buffer, function(err) {
20 | if (err) throw err;
21 | });
22 | });
--------------------------------------------------------------------------------
/baby/step2_transplant_style.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // open an existing doc, unzip it, zip it, and save it as new name. See if it opens.
4 |
5 |
6 | var JSZip = require('jszip');
7 | var fs = require("fs");
8 |
9 | var INFILE = './lab/out/out.pptx';
10 | var OUTFILE = './lab/baby.pptx';
11 |
12 | var STYLEFILE = './lab/ex1/ex1.pptx';
13 |
14 | // read a zip file
15 | fs.readFile(INFILE, function(err, data) {
16 | if (err) throw err;
17 | var zip = new JSZip(data);
18 |
19 |
20 | fs.readFile(STYLEFILE, function(err, data2) {
21 | var zip_theme = new JSZip(data2)
22 | var theme_xml = zip_theme.file('ppt/theme/theme1.xml').asText();
23 | zip.file('ppt/theme/theme1.xml', theme_xml);
24 |
25 | var buffer = zip.generate({type:"nodebuffer"});
26 |
27 | fs.writeFile(OUTFILE, buffer, function(err) {
28 | if (err) throw err;
29 | });
30 | });
31 |
32 | });
--------------------------------------------------------------------------------
/baby/step3_read_minimal.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 |
7 | var INFILE = './lab/minimal/minimal.pptx';
8 |
9 | // read a zip file
10 | fs.readFile(INFILE, function(err, data) {
11 | if (err) throw err;
12 | var zip = new JSZip(data);
13 |
14 | // ok, yeah, no wow, we've got a pptx file open. Can I modify it somehow
15 |
16 | console.log(Object.keys(zip.files))
17 |
18 | console.log(zip.file('ppt/slides/slide1.xml').asText())
19 |
20 | });
21 |
22 |
23 |
24 | var x = ["[Content_Types].xml",
25 | "_rels/.rels",
26 | "ppt/slides/_rels/slide1.xml.rels",
27 | "ppt/_rels/presentation.xml.rels",
28 | "ppt/presentation.xml",
29 | "ppt/slides/slide1.xml",
30 | "ppt/slideLayouts/_rels/slideLayout6.xml.rels",
31 | "ppt/slideMasters/_rels/slideMaster1.xml.rels",
32 | "ppt/slideLayouts/_rels/slideLayout8.xml.rels",
33 | "ppt/slideLayouts/_rels/slideLayout9.xml.rels",
34 | "ppt/slideLayouts/_rels/slideLayout11.xml.rels",
35 | "ppt/slideLayouts/_rels/slideLayout10.xml.rels",
36 | "ppt/slideLayouts/_rels/slideLayout7.xml.rels",
37 | "ppt/slideLayouts/_rels/slideLayout2.xml.rels",
38 | "ppt/slideLayouts/_rels/slideLayout3.xml.rels",
39 | "ppt/slideLayouts/_rels/slideLayout4.xml.rels",
40 | "ppt/slideLayouts/_rels/slideLayout5.xml.rels",
41 | "ppt/slideLayouts/_rels/slideLayout1.xml.rels",
42 | "ppt/slideLayouts/slideLayout11.xml",
43 | "ppt/slideMasters/slideMaster1.xml",
44 | "ppt/slideLayouts/slideLayout1.xml",
45 | "ppt/slideLayouts/slideLayout2.xml",
46 | "ppt/slideLayouts/slideLayout3.xml",
47 | "ppt/slideLayouts/slideLayout10.xml",
48 | "ppt/slideLayouts/slideLayout4.xml",
49 | "ppt/slideLayouts/slideLayout6.xml",
50 | "ppt/slideLayouts/slideLayout5.xml",
51 | "ppt/slideLayouts/slideLayout9.xml",
52 | "ppt/slideLayouts/slideLayout7.xml",
53 | "ppt/slideLayouts/slideLayout8.xml",
54 | " docProps/thumbnail.jpeg",
55 | "ppt/theme/theme1.xml",
56 | "ppt/viewProps.xml",
57 | "ppt/tableStyles.xml",
58 | "ppt/presProps.xml",
59 | "docProps/app.xml",
60 | "docProps/core.xml",
61 | "ppt/printerSettings/printerSettings1.bin"
62 | ];
63 |
--------------------------------------------------------------------------------
/baby/step4_edit_slide.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 | var cheerio = require('cheerio');
7 | var pd = require('pretty-data').pd;
8 |
9 | var INFILE = './lab/parts/parts.pptx';
10 | var OUTFILE = './lab/parts/parts2.pptx';
11 |
12 |
13 | var $ = cheerio.load
14 |
15 | // read a zip file
16 | fs.readFile(INFILE, function(err, data) {
17 | if (err) throw err;
18 | var zip = new JSZip(data);
19 | var str0 = zip.file('ppt/slides/slide1.xml').asText();
20 | var $slide1 = cheerio.load(zip.file('ppt/slides/slide1.xml').asText(), {xmlMode: true});
21 |
22 | var str1 = $slide1.xml();
23 | console.log([str0.length, str1.length, str0==str1])
24 | var xml = $slide1.xml();
25 |
26 | // console.log(pretty);
27 | $slide1('p\\:sp').each(function(i, elem) {
28 |
29 | console.log('---------------------------------------')
30 | $slide1('a\\:t', elem).text("This is different")
31 | var a = $slide1('a\\:t', elem).text();
32 | console.log(a);
33 | // console.log(pd.xml($(elem, {xmlMode:true}).xml()));
34 | });
35 |
36 | var str2 = $slide1.xml();
37 | zip.file('ppt/slides/slide1.xml', str2);
38 |
39 | var buffer = zip.generate({type:"nodebuffer"});
40 |
41 | fs.writeFile(OUTFILE, buffer, function(err) {
42 | if (err) throw err;
43 | });
44 |
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/baby/step5_add_shape.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 | var cheerio = require('cheerio');
7 | var xmldoc = require('xmldoc');
8 | var pd = require('pretty-data').pd;
9 | var pretty = function(xml) { return pd.xml(xml); }
10 |
11 | var INFILE = './lab/parts/parts.pptx';
12 | var OUTFILE = './lab/parts/parts2.pptx';
13 |
14 | function $(xml) { return cheerio.load(xml, {xmlMode: true})}
15 |
16 | function Slide(xml) {
17 | this.$el = cheerio.load(xml, {xmlMode: true});
18 | return this;
19 | };
20 | Slide.prototype.xml = function() { return this.$el.xml(); }
21 |
22 | fs.readFile(INFILE, function(err, data) {
23 | if (err) throw err;
24 | var zip = new JSZip(data);
25 |
26 | var slide1xml = zip.file('ppt/slides/slide1.xml').asText();
27 | var $slide1 = cheerio.load(slide1xml, {xmlMode: true})
28 |
29 | var $shapes = $slide1('p\\:sp');
30 |
31 | // textBoxes.each(function(i, elem) {
32 | // console.log(i + "-----------------------------")
33 | // console.log(cheerio.load(elem).xml())
34 | // })
35 | //
36 | var $oval =$shapes[3];
37 | // console.log(($($oval)));
38 |
39 | var str = 'Another circle';
40 |
41 | $slide1('p\\:spTree').append(str);
42 |
43 |
44 | var str2 = $slide1.xml();
45 | zip.file('ppt/slides/slide1.xml', str2);
46 |
47 | var buffer = zip.generate({type:"nodebuffer"});
48 |
49 | fs.writeFile(OUTFILE, buffer, function(err) {
50 | if (err) throw err;
51 | });
52 |
53 |
54 |
55 |
56 | //
57 | // var $ovalText = $($oval)('a\\:t');
58 | //
59 | //
60 | // console.log($ovalText.text());
61 | // console.log($($ovalText[0]).xml() );
62 |
63 | // console.log(($($oval).xml()));
64 |
65 |
66 | });
67 |
--------------------------------------------------------------------------------
/baby/step6_add_slide.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 | var cheerio = require('cheerio');
7 | var xmldoc = require('xmldoc');
8 | var pd = require('pretty-data').pd;
9 | var pretty = function (xml) {
10 | return pd.xml(xml);
11 | }
12 |
13 | var INFILE = './lab/parts/parts.pptx';
14 | var OUTFILE = './lab/parts2/parts2.pptx';
15 | var REFERENCE = './lab/parts3/parts3.pptx';
16 |
17 |
18 | function $(xml) {
19 | return cheerio.load(xml, {xmlMode: true})
20 | }
21 |
22 | function Slide(xml) {
23 | this.$el = cheerio.load(xml, {xmlMode: true});
24 | return this;
25 | };
26 | Slide.prototype.xml = function () {
27 | return this.$el.xml();
28 | }
29 |
30 | var zip3 = new JSZip(fs.readFileSync(REFERENCE))
31 |
32 | fs.readFile(INFILE, function (err, data) {
33 | if (err) throw err;
34 | var zip = new JSZip(data);
35 |
36 |
37 |
38 | var slide1xml = zip.file('ppt/slides/slide1.xml').asText();
39 | var $slide1 = cheerio.load(slide1xml, {xmlMode: true})
40 |
41 |
42 | // 1. add the slide to ppt/slides/slideN.xml
43 | zip.file('ppt/slides/slide2.xml', slide1xml);
44 |
45 |
46 | // 2. add entry to ppt/_rels/presentation.xml.rels
47 |
48 | var $ppt_rels = $(zip.file('ppt/_rels/presentation.xml.rels').asText());
49 | var rId3 = '';
50 | $ppt_rels('Relationships').append(rId3)
51 | zip.file('ppt/_rels/presentation.xml.rels', $ppt_rels.xml());
52 |
53 |
54 | // 3. add reference to ppt/slides/_rels/
55 | var xml = '';
56 | xml += '';
57 | zip.file('ppt/slides/_rels/slide2.xml.rels', xml)
58 |
59 | // 4. add slide to ppt/presentation.xml
60 | var pres = zip.file('ppt/presentation.xml').asText();
61 | var $pres = $(pres);
62 | $pres('p\\:sldIdLst').append('')
63 | zip.file('ppt/presentation.xml', $pres.xml())
64 |
65 |
66 | // HACK! set docProps
67 |
68 | zip.file('docProps/app.xml', '\n148Microsoft Macintosh PowerPointOn-screen Show (4:3)82000falseTheme1Slide Titles2Office ThemeThis is the titleThis is the titleProven, Inc.falsefalsefalse14.0000')
69 | zip.file('ppt/viewProps.xml', '\n')
70 |
71 |
72 |
73 | var $ct = $(zip.file('[Content_Types].xml').asText());
74 | $ct('Types').append('');
75 | // console.log(pretty($ct.xml()));
76 | zip.file('[Content_Types].xml', $ct.xml())
77 | var $slidIdList =
78 | console.log($($pres('p\\:sldIdLst').children()[0]).xml())
79 |
80 |
81 |
82 | // SYSTEMATICALLY FIND PROBLEM
83 | var diffs = [
84 | // '[Content_Types].xml',
85 | // 'ppt/_rels/presentation.xml.rels',
86 | // 'docProps/core.xml',
87 | // 'ppt/slides/slide2.xml'
88 | ];
89 | // var diffs = Object.keys(zip3.files);
90 | // console.log(diffs);
91 | diffs.forEach(function(key) {
92 | zip.file(key, zip3.file(key).asArrayBuffer())
93 | })
94 | //
95 |
96 | var buffer = zip.generate({type: "nodebuffer"});
97 |
98 | fs.writeFile(OUTFILE, buffer, function (err) {
99 | if (err) throw err;
100 | });
101 | //
102 |
103 | });
104 |
--------------------------------------------------------------------------------
/baby/step7_diff.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 |
7 | //var FILE1 = './test/files/parts3-b.pptx';
8 | //var FILE2 = './test/files/parts3-a.pptx';
9 |
10 | var FILE1 = './lab/p0/protobi-2015-09-03 17.06.pptx';
11 | var FILE2 = './lab/p1/protobi-2015-09-03 17.06.pptx';
12 |
13 | var zip1 = new JSZip(fs.readFileSync(FILE1));
14 | var zip2 = new JSZip(fs.readFileSync(FILE2));
15 | Object.keys(zip1.files).forEach(function (key) {
16 | var str1 = zip1.file(key).asText().replace(/\n|\s/ig, '');
17 | var str2 = zip2.file(key) ? zip2.file(key).asText().replace(/\n|\s/ig, '') : "";
18 |
19 | // if (str1 != str2) {
20 | if (str1.length != str2.length) {
21 |
22 | // if (str1.length != str2.length) {
23 | console.log(key);
24 | console.log(str1.length, str2.length, str1.length == str2.length)
25 | }
26 |
27 | });
28 |
--------------------------------------------------------------------------------
/baby/step8_frankenstein_copy.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 | var cheerio = require('cheerio');
7 | var xmldoc = require('xmldoc');
8 | var pd = require('pretty-data').pd;
9 | var pretty = function(xml) { return pd.xml(xml); }
10 |
11 | var INFILE = './lab/parts3/parts3.pptx';
12 | var OUTFILE = './lab/parts2/parts2.pptx';
13 |
14 | function $(xml) { return cheerio.load(xml, {xmlMode: true})}
15 |
16 | fs.readFile(INFILE, function(err, data) {
17 | if (err) throw err;
18 | var zip1 = new JSZip(data);
19 |
20 | var zip2 = new JSZip();
21 |
22 |
23 | Object.keys(zip1.files).forEach(function(key) {
24 | console.log(key)
25 | zip2.file(key, zip1.file(key).asText() ) ;
26 | });
27 |
28 |
29 | var buffer = zip2.generate({type:"nodebuffer"});
30 |
31 | fs.writeFile(OUTFILE, buffer, function(err) {
32 | if (err) throw err;
33 | });
34 | //
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/baby/step9_xml_parse_generate.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs");
4 | var PPTX = require('../lib/pptx');
5 |
6 | var INFILE = './lab/parts3/parts3.pptx';
7 | var OUTFILE = './lab/parts2/parts2.pptx';
8 |
9 |
10 | fs.readFile(INFILE, function(err, data) {
11 | if (err) throw err;
12 | var pptx = new PPTX();
13 | pptx.load(data, function (err) {
14 | fs.writeFile('./lab/parts2/parts2.json', JSON.stringify(pptx, null,4), 'utf8');
15 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
16 | if (err) throw err;
17 | });
18 | });
19 | });
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/baby/t.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | var JSZip = require('jszip');
5 | var fs = require("fs");
6 |
7 | var TESTFILE = '/tmp/chart.pptx';
8 | var REFERENCE = './lab/chart/chart.pptx';
9 | var OUTFILE = '/tmp/chart2.pptx';
10 |
11 | function $(xml) {
12 | return cheerio.load(xml, {xmlMode: true})
13 | }
14 |
15 |
16 | var testZip = new JSZip(fs.readFileSync(TESTFILE));
17 | var refZip = new JSZip(fs.readFileSync(REFERENCE));
18 | var outZip = new JSZip();
19 |
20 | //console.log(Object.keys(refZip.files));
21 | //process.exit()
22 |
23 | // copy all the files from testzip to outzip
24 | // copy selected files from refzip to outzuip
25 | // save outzip
26 |
27 | Object.keys(testZip.files).forEach(function (key) {
28 | outZip.file(key, testZip.file(key).asArrayBuffer());
29 | });
30 |
31 | var keys = [
32 | // '[Content_Types].xml',
33 | // '_rels/.rels',
34 | // 'ppt/slides/_rels/slide2.xml.rels',
35 | // 'ppt/slides/_rels/slide1.xml.rels',
36 | // 'ppt/_rels/presentation.xml.rels',
37 | // 'ppt/presentation.xml',
38 | // 'ppt/slides/slide2.xml',
39 | // 'ppt/slides/slide1.xml',
40 | // 'ppt/slideLayouts/_rels/slideLayout8.xml.rels',
41 | // 'ppt/slideLayouts/_rels/slideLayout9.xml.rels',
42 | // 'ppt/slideLayouts/_rels/slideLayout5.xml.rels',
43 | // 'ppt/slideLayouts/_rels/slideLayout6.xml.rels',
44 | // 'ppt/slideLayouts/_rels/slideLayout4.xml.rels',
45 | // 'ppt/slideLayouts/_rels/slideLayout2.xml.rels',
46 | // 'ppt/slideLayouts/_rels/slideLayout1.xml.rels',
47 | // 'ppt/slideLayouts/_rels/slideLayout11.xml.rels',
48 | // 'ppt/slideLayouts/_rels/slideLayout10.xml.rels',
49 | // 'ppt/slideMasters/_rels/slideMaster1.xml.rels',
50 | // 'ppt/slideLayouts/_rels/slideLayout3.xml.rels',
51 | // 'ppt/slideLayouts/slideLayout10.xml',
52 | // 'ppt/slideLayouts/slideLayout9.xml',
53 | // 'ppt/slideLayouts/slideLayout2.xml',
54 | // 'ppt/slideLayouts/slideLayout1.xml',
55 | // 'ppt/slideMasters/slideMaster1.xml',
56 | // 'ppt/slideLayouts/_rels/slideLayout7.xml.rels',
57 | // 'ppt/slideLayouts/slideLayout3.xml',
58 | // 'ppt/slideLayouts/slideLayout4.xml',
59 | // 'ppt/slideLayouts/slideLayout5.xml',
60 | // 'ppt/slideLayouts/slideLayout6.xml',
61 | // 'ppt/slideLayouts/slideLayout7.xml',
62 | // 'ppt/slideLayouts/slideLayout8.xml',
63 | // 'ppt/slideLayouts/slideLayout11.xml',
64 | // 'ppt/embeddings/Microsoft_Excel_Sheet2.xlsx',
65 | // 'ppt/charts/_rels/chart2.xml.rels',
66 | // 'ppt/theme/theme1.xml',
67 | // 'ppt/charts/chart1.xml',
68 | // 'ppt/embeddings/Microsoft_Excel_Sheet1.xlsx',
69 | // 'ppt/charts/chart2.xml',
70 | // 'docProps/thumbnail.jpeg',
71 | // 'ppt/charts/_rels/chart1.xml.rels',
72 | // 'ppt/viewProps.xml',
73 | // 'ppt/tableStyles.xml',
74 | // 'ppt/presProps.xml',
75 | // 'docProps/app.xml',
76 | // 'docProps/core.xml',
77 | // 'ppt/printerSettings/printerSettings1.bin'
78 | ]
79 |
80 | keys.forEach(function (key) {
81 | outZip.file(key, refZip.file(key).asArrayBuffer());
82 | });
83 |
84 | var buffer = outZip.generate({type: "nodebuffer"});
85 |
86 | fs.writeFile(OUTFILE, buffer, function (err) {
87 | if (err) throw err;
88 | console.log("open "+OUTFILE)
89 | });
90 |
--------------------------------------------------------------------------------
/examples/example.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs");
4 | var PPTX = require('..');
5 |
6 |
7 | var INFILE = './test/files/minimal.pptx'; // a blank PPTX file with my layouts, themes, masters.
8 | var OUTFILE = '/tmp/example.pptx';
9 |
10 | fs.readFile(INFILE, function (err, data) {
11 | if (err) throw err;
12 | var pptx = new PPTX.Presentation();
13 | pptx.load(data, function (err) {
14 | var slide1 = pptx.getSlide('slide1');
15 |
16 | var slide2 = pptx.addSlide("slideLayout3"); // section divider
17 | var slide3 = pptx.addSlide("slideLayout6"); // title only
18 |
19 |
20 | var triangle = slide1.addShape()
21 | .text("Triangle")
22 | .shapeProperties()
23 | .x(PPTX.emu.inch(2))
24 | .y(PPTX.emu.inch(2))
25 | .cx(PPTX.emu.inch(2))
26 | .cy(PPTX.emu.inch(2))
27 | .prstGeom('triangle');
28 |
29 | var triangle = slide1.addShape()
30 | .text("Ellipse")
31 | .shapeProperties()
32 | .x(PPTX.emu.inch(4))
33 | .y(PPTX.emu.inch(4))
34 | .cx(PPTX.emu.inch(2))
35 | .cy(PPTX.emu.inch(1))
36 | .prstGeom('ellipse');
37 |
38 | for (var i = 0; i < 20; i++) {
39 | slide2.addShape()
40 | .text("" + i)
41 | .shapeProperties()
42 | .x(PPTX.emu.inch((Math.random() * 10)))
43 | .y(PPTX.emu.inch((Math.random() * 6)))
44 | .cx(PPTX.emu.inch(1))
45 | .cy(PPTX.emu.inch(1))
46 | .prstGeom('ellipse');
47 | }
48 |
49 | slide1.getShapes()[3]
50 | .text("Now it's a trapezoid")
51 | .shapeProperties()
52 | .x(PPTX.emu.inch(1))
53 | .y(PPTX.emu.inch(1))
54 | .cx(PPTX.emu.inch(2))
55 | .cy(PPTX.emu.inch(0.75))
56 | .prstGeom('trapezoid');
57 |
58 | var chart = slide3.addChart(barChart, function (err, chart) {
59 |
60 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
61 | if (err) throw err;
62 | console.log("open " + OUTFILE)
63 | });
64 | });
65 | });
66 | })
67 | ;
68 |
69 | var barChart = {
70 | title: 'Sample bar chart',
71 | renderType: 'bar',
72 | data: [
73 | {
74 | name: 'Series 1',
75 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
76 | values: [4.3, 2.5, 3.5, 4.5]
77 | },
78 | {
79 | name: 'Series 2',
80 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
81 | values: [2.4, 4.4, 1.8, 2.8]
82 | },
83 | {
84 | name: 'Series 3',
85 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
86 | values: [2.0, 2.0, 3.0, 5.0]
87 | }
88 | ]
89 | }
--------------------------------------------------------------------------------
/lib/chart.js:
--------------------------------------------------------------------------------
1 | var excelbuilder = require('./msexcel-builder');
2 | var fs = require('fs');
3 | var clone = require('./util/clone')
4 |
5 |
6 | var Chart = module.exports = function (args) {
7 | this.presentation = args.presentation; // should this be inferred from the slide?
8 | this.slide = args.slide;
9 | this.name = args.name;
10 | }
11 |
12 | var XmlNode = require('./xmlnode');
13 |
14 | // TODO Generate Excel Worksheet for dataseries
15 | // TODO Generate data series reference for chart xml
16 |
17 |
18 | Chart.prototype.load = function (chartInfo, done) {
19 | var self = this;
20 | var chartName = this.name; // e.g. 'chart1';
21 |
22 | var jsChartFrame = clone(require('./fragments/js/chartframe'));
23 | jsChartFrame["p:graphicFrame"]["p:nvGraphicFramePr"]["p:nvPr"]["p:extLst"]["p:ext"]["p14:modId"] =
24 | {
25 | "$": {
26 | "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
27 | "val": Math.floor(Math.random() * 4294967295)
28 | }
29 | }
30 | self.content = jsChartFrame["p:graphicFrame"];
31 |
32 |
33 | // 'ppt/charts/chartN.xml'
34 | var chartContent = this.getChartBase(chartInfo);
35 | this.presentation.registerChart(chartName, chartContent);
36 |
37 |
38 | this.createWorkbook(chartInfo, function (err, workbookJSZip) {
39 | self.presentation.registerChartWorkbook(chartName, workbookJSZip.generate({type: 'arraybuffer'}));
40 | done(null, self);
41 | });
42 | }
43 |
44 | Chart.prototype.getChartBase = function (chartInfo) {
45 | var jsChart = clone(require('./fragments/js/chart'));
46 | var jsChartSeries = this.getChartSeries(chartInfo);
47 |
48 | // TODO Generate the chart series from the data
49 | jsChart["c:chartSpace"]["c:chart"][0]["c:plotArea"][0]["c:barChart"][0]["c:ser"] = jsChartSeries['c:ser'];
50 | return jsChart;
51 | };
52 |
53 | Chart.prototype.getChartSeries = function (chartInfo) {
54 | var res = {
55 | "c:ser": chartInfo.data.map(this._ser, this)
56 | }
57 | return res;
58 | };
59 |
60 |
61 | /// @brief returns XML snippet for a chart dataseries
62 | Chart.prototype._ser = function (serie, i) {
63 | var rc2a = this._rowColToSheetAddress; // shortcut
64 |
65 |
66 | var ser = XmlNode()
67 | .addChild('c:idx', XmlNode().attr('val', i))
68 | .addChild('c:order', XmlNode().attr('val', i))
69 | .addChild('c:tx', this._strRef('Sheet1!' + rc2a(1, 2 + i, true, true), [serie.name]))
70 | .addChild('c:invertIfNegative', XmlNode().attr('val', 0))
71 | .addChild('c:cat', this._strRef('Sheet1!' + rc2a(2, 1, true, true) + ':' + rc2a(2 + serie.labels.length - 1, 1, true, true), serie.labels))
72 | .addChild('c:val', this._numRef('Sheet1!' + rc2a(2, 2 + i, true, true) + ':' + rc2a(2 + serie.labels.length - 1, 2 + i, true, true), serie.values, "General"))
73 |
74 | if (serie.color) {
75 | ser.addChild('c:spPr',
76 | XmlNode().addChild('a:solidFill',
77 | XmlNode().addChild('a:srgbClr',
78 | XmlNode().attr('val', serie.color)
79 | )
80 | )
81 | );
82 | }
83 | else if (serie.schemeColor) {
84 | ser.addChild('c:spPr',
85 | XmlNode().addChild('a:solidFill',
86 | XmlNode().addChild('a:schemeClr',
87 | XmlNode().attr('val', serie.schemeColor)
88 | )
89 | )
90 | );
91 | }
92 |
93 | return ser.el;
94 | };
95 |
96 |
97 | ///
98 | /// @brief Transform an array of string into an office's compliance structure
99 | ///
100 | /// @param[in] region String
101 | /// The reference cell of the string, for example: $A$1
102 | /// @param[in] stringArr
103 | /// An array of string, for example: ['foo', 'bar']
104 | ///
105 | Chart.prototype._strRef = function (region, stringArr) {
106 |
107 | var strRef = XmlNode().addChild('c:strRef', XmlNode()
108 | .addChild('c:f', region)
109 | .addChild('c:strCache', this._strCache(stringArr))
110 | );
111 | return strRef.el;
112 | }
113 |
114 |
115 | Chart.prototype._strCache = function (stringArr) {
116 | var strRef = XmlNode().addChild('c:ptCount', XmlNode().attr('val', stringArr.length))
117 | for (var i = 0; i < stringArr.length; i++) {
118 | strRef.addChild('c:pt', XmlNode().attr('idx', i).addChild('c:v', stringArr[i]))
119 | }
120 |
121 | return strRef;
122 | }
123 |
124 |
125 | ///
126 | /// @brief Transform an array of numbers into an office's compliance structure
127 | ///
128 | /// @param[in] region String
129 | /// The reference cell of the string, for example: $A$1
130 | /// @param[in] numArr
131 | /// An array of numArr, for example: [4, 7, 8]
132 | /// @param[in] formatCode
133 | /// A string describe the number's format. Example: General
134 | ///
135 | Chart.prototype._numRef = function (region, numArr, formatCode) {
136 |
137 | var numCache = XmlNode()
138 | .addChild('c:formatCode', formatCode)
139 | .addChild('c:ptCount', XmlNode().attr('val', numArr.length));
140 |
141 | for (var i = 0; i < numArr.length; i++) {
142 | numCache.addChild('c:pt', XmlNode().attr('idx', i).addChild('c:v', numArr[i].toString()));
143 | }
144 |
145 | var numRef = XmlNode().addChild('c:numRef', XmlNode()
146 | .addChild('c:f', region)
147 | .addChild('c:numCache', numCache)
148 | );
149 | return numRef.el;
150 |
151 | }
152 |
153 |
154 | Chart.prototype._rowColToSheetAddress = function (row, col, isRowAbsolute, isColAbsolute) {
155 | var address = "";
156 |
157 | if (isColAbsolute)
158 | address += '$';
159 |
160 | // these lines of code will transform the number 1-26 into A->Z
161 | // used in excel's cell's coordination
162 | while (col > 0) {
163 | var num = col % 26;
164 | col = (col - num ) / 26;
165 | address += String.fromCharCode(65 + num - 1);
166 | }
167 |
168 | if (isRowAbsolute)
169 | address += '$';
170 |
171 | address += row;
172 |
173 | return address;
174 | };
175 |
176 |
177 | // takes an array with series data
178 | // callback takes two parameters:
179 | // @err Error, null if successful
180 | // @wb JSZip object containing the workbook
181 | Chart.prototype.createWorkbook = function (data, callback) {
182 |
183 | // First, generate a temporatory excel file for storing the chart's data
184 | var workbook = excelbuilder.createWorkbook();
185 |
186 | // Create a new worksheet with 10 columns and 12 rows
187 | // number of columns: data['data'].length+1 -> equaly number of series
188 | // number of rows: data['data'][0].values.length+1
189 | var sheet1 = workbook.createSheet('Sheet1', data['data'].length + 1, data['data'][0].values.length + 1);
190 | var headerrow = 1;
191 |
192 | // write header using serie name
193 | for (var j = 0; j < data['data'].length; j++) {
194 | sheet1.set(j + 2, headerrow, data['data'][j].name);
195 | }
196 |
197 | // write category column in the first column
198 | for (var j = 0; j < data['data'][0].labels.length; j++) {
199 | sheet1.set(1, j + 2, data['data'][0].labels[j]);
200 | }
201 |
202 | // for each serie, write out values in its row
203 | for (var i = 0; i < data['data'].length; i++) {
204 | for (var j = 0; j < data['data'][i].values.length; j++) {
205 | // col i+2
206 | // row j+1
207 | sheet1.set(i + 2, j + 2, data['data'][i].values[j]);
208 | }
209 | }
210 | workbook.generate(callback); // returns (err, zip)
211 | };
--------------------------------------------------------------------------------
/lib/charts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | null: function(options) {
3 | return {
4 | "c:chartSpace": {
5 | "@xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
6 | "@xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
7 | "@xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
8 | "c:lang": { "@val": "en-US" },
9 | "c:date1904": { "@val": "1" },
10 | "c:chart": {}
11 | }
12 | }
13 | },
14 |
15 | "bar": function (options) {
16 | options = options || {};
17 | return {
18 | "c:chartSpace": {
19 | "@xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
20 | "@xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
21 | "@xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
22 | "c:lang": { "@val": "en-US" },
23 | "c:chart": {
24 | "c:plotArea": {
25 | "c:layout": {},
26 | "c:barChart": {
27 | "c:barDir": { "@val": "bar" },
28 | "c:grouping": { "@val": "clustered" },
29 | "#list": [
30 | {"c:axId": { "@val": "64451712" }},
31 | {"c:axId": { "@val": "64453248" }}
32 | ]
33 | },
34 | "c:catAx": {
35 | "c:axId": { "@val": "64451712" },
36 | "c:scaling": {
37 | "c:orientation": { "@val": options.catAxisReverseOrder ? "maxMin" : "minMax" }
38 | },
39 | "c:axPos": { "@val": "l" },
40 | "c:tickLblPos": { "@val": "nextTo" },
41 | "c:crossAx": { "@val": "64453248" },
42 | "c:crosses": { "@val": "autoZero" },
43 | "c:auto": { "@val": "1" },
44 | "c:lblAlgn": { "@val": "ctr" },
45 | "c:lblOffset": { "@val": "100" }
46 | },
47 | "c:valAx": {
48 | "c:axId": { "@val": "64453248" },
49 | "c:scaling": {
50 | "c:orientation": { "@val": "minMax" }
51 | },
52 | "c:axPos": { "@val": "b" },
53 | // "c:majorGridlines": {},
54 | "c:numFmt": {
55 | "@formatCode": "General",
56 | "@sourceLinked": "1"
57 | },
58 | "c:tickLblPos": { "@val": "nextTo" },
59 | "c:crossAx": { "@val": "64451712" },
60 | "c:crosses": { "@val": options.valAxisCrossAtMaxCategory ? "max" : "autoZero" },
61 | "c:crossBetween": { "@val": "between" }
62 | }
63 | },
64 | "c:legend": {
65 | "c:legendPos": { "@val": "r" },
66 | "c:layout": {}
67 | },
68 | "c:plotVisOnly": { "@val": "1" }
69 | },
70 | "c:txPr": {
71 | "a:bodyPr": {},
72 | "a:lstStyle": {},
73 | "a:p": {
74 | "a:pPr": {
75 | "a:defRPr": { "@sz": "1800" }
76 | },
77 | "a:endParaRPr": { "@lang": "en-US" }
78 | }
79 | },
80 | "c:externalData": { "@r:id": "rId1" }
81 | }
82 | }
83 | },
84 | "column": function (options) {
85 | options = options || {};
86 | return {
87 | "c:chartSpace": {
88 | "@xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
89 | "@xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
90 | "@xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
91 | "c:lang": { "@val": "en-US" },
92 | "c:date1904": { "@val": "1" },
93 | "c:chart": {
94 | "c:plotArea": {
95 | "c:layout": {},
96 | "c:barChart": {
97 | "c:barDir": { "@val": "col" },
98 | "c:grouping": { "@val": "clustered" },
99 | "c:overlap": { "@val": options.overlap || "0" },
100 | "c:gapWidth": { "@val": options.gapWidth || "150" },
101 |
102 | "#list": [
103 | {"c:axId": { "@val": "64451712" }},
104 | {"c:axId": { "@val": "64453248" }}
105 | ]
106 | },
107 | "c:catAx": {
108 | "c:axId": { "@val": "64451712" },
109 | "c:scaling": {
110 | "c:orientation": { "@val": options.catAxisReverseOrder ? "maxMin" : "minMax" }
111 | },
112 | "c:axPos": { "@val": "l" },
113 | "c:tickLblPos": { "@val": "nextTo" },
114 | "c:crossAx": { "@val": "64453248" },
115 | "c:crosses": { "@val": "autoZero" },
116 | "c:auto": { "@val": "1" },
117 | "c:lblAlgn": { "@val": "ctr" },
118 | "c:lblOffset": { "@val": "100" }
119 | },
120 | "c:valAx": {
121 | "c:axId": { "@val": "64453248" },
122 | "c:scaling": {
123 | "c:orientation": { "@val": "minMax" }
124 | },
125 | "c:axPos": { "@val": "b" },
126 | // "c:majorGridlines": {},
127 | "c:numFmt": {
128 | "@formatCode": "General",
129 | "@sourceLinked": "1"
130 | },
131 | "c:tickLblPos": { "@val": "nextTo" },
132 | "c:crossAx": { "@val": "64451712" },
133 | "c:crosses": { "@val": options.valAxisCrossAtMaxCategory ? "max" : "autoZero"},
134 | "c:crossBetween": { "@val": "between" }
135 | }
136 | },
137 | "c:legend": {
138 | "c:legendPos": { "@val": "r" },
139 | "c:layout": {}
140 | },
141 | "c:plotVisOnly": { "@val": "1" }
142 | },
143 | "c:txPr": {
144 | "a:bodyPr": {},
145 | "a:lstStyle": {},
146 | "a:p": {
147 | "a:pPr": {
148 | "a:defRPr": { "@sz": "1800" }
149 | },
150 | "a:endParaRPr": { "@lang": "en-US" }
151 | }
152 | },
153 | "c:externalData": { "@r:id": "rId1" }
154 | }
155 | }
156 | },
157 | "group-bar": function (options) {
158 | options = options || {};
159 | return {
160 | "c:chartSpace": {
161 | "@xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
162 | "@xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
163 | "@xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
164 | "c:lang": { "@val": "en-US" },
165 | "c:date1904": { "@val": "1" },
166 | "c:chart": {
167 | "c:plotArea": {
168 | "c:layout": {},
169 | "c:barChart": {
170 | "c:barDir": { "@val": "bar" },
171 | "c:grouping": { "@val": "stacked" },
172 | "c:overlap": { "@val": options.overlap || "100" },
173 | "c:gapWidth": { "@val": options.gapWidth || "150" },
174 | "#list": [
175 | {"c:axId": { "@val": "64451712" }},
176 | {"c:axId": { "@val": "64453248" }}
177 | ]
178 | },
179 | "c:catAx": {
180 | "c:axId": { "@val": "64451712" },
181 | "c:scaling": {
182 | "c:orientation": { "@val": options.catAxisReverseOrder ? "maxMin" : "minMax" }
183 | },
184 | "c:axPos": { "@val": "l" },
185 | "c:tickLblPos": { "@val": "nextTo" },
186 | "c:crossAx": { "@val": "64453248" },
187 | "c:crosses": { "@val": "autoZero" },
188 | "c:auto": { "@val": "1" },
189 | "c:lblAlgn": { "@val": "ctr" },
190 | "c:lblOffset": { "@val": "100" }
191 | },
192 | "c:valAx": {
193 | "c:axId": { "@val": "64453248" },
194 | "c:scaling": {
195 | "c:orientation": { "@val": "minMax" }
196 | },
197 | "c:axPos": { "@val": "b" },
198 | // "c:majorGridlines": {},
199 | "c:numFmt": {
200 | "@formatCode": "General",
201 | "@sourceLinked": "1"
202 | },
203 | "c:tickLblPos": { "@val": "nextTo" },
204 | "c:crossAx": { "@val": "64451712" },
205 | "c:crosses": { "@val": options.valAxisCrossAtMaxCategory ? "max" : "autoZero" },
206 | "c:crossBetween": { "@val": "between" }
207 | }
208 | },
209 | "c:legend": {
210 | "c:legendPos": { "@val": "r" },
211 | "c:layout": {}
212 | },
213 | "c:plotVisOnly": { "@val": "1" }
214 | },
215 | "c:txPr": {
216 | "a:bodyPr": {},
217 | "a:lstStyle": {},
218 | "a:p": {
219 | "a:pPr": {
220 | "a:defRPr": { "@sz": "1800" }
221 | },
222 | "a:endParaRPr": { "@lang": "en-US" }
223 | }
224 | },
225 | "c:externalData": { "@r:id": "rId1" }
226 | }
227 | }
228 | },
229 | "pie": function (options) {
230 | options = options || {};
231 | return {
232 | "c:chartSpace": {
233 | "@xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
234 | "@xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
235 | "@xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
236 | "c:lang": { "@val": "en-US" },
237 | "c:chart": {
238 | "c:title": {
239 | "c:layout": {}
240 | },
241 | "c:plotArea": {
242 | "c:layout": {},
243 | "c:pieChart": {
244 | "c:varyColors": { "@val": "1" },
245 | "c:firstSliceAng": { "@val": "0" },
246 | "#list": []
247 | }
248 | },
249 |
250 | "c:legend": {
251 | "c:legendPos": { "@val": "r" },
252 | "c:layout": {}
253 | },
254 | "c:plotVisOnly": { "@val": "1" }
255 | },
256 | "c:txPr": {
257 | "a:bodyPr": {},
258 | "a:lstStyle": {},
259 | "a:p": {
260 | "a:pPr": {
261 | "a:defRPr": { "@sz": "1800" }
262 | },
263 | "a:endParaRPr": { "@lang": "en-US" }
264 | }
265 | },
266 | "c:externalData": { "@r:id": "rId1" }
267 | }
268 | }
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/lib/defaults.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "p:sp": {
3 | "p:nvSpPr": [
4 | {
5 | "p:cNvPr": [
6 | {
7 | "$": {
8 | "id": "5",
9 | "name": "Oval 4"
10 | }
11 | }
12 | ],
13 | "p:cNvSpPr": [
14 | ""
15 | ],
16 | "p:nvPr": [
17 | ""
18 | ]
19 | }
20 | ],
21 | "p:spPr": [
22 | {
23 | "a:xfrm": [
24 | {
25 | "a:off": [
26 | {
27 | "$": {
28 | "x": "6578600",
29 | "y": "787400"
30 | }
31 | }
32 | ],
33 | "a:ext": [
34 | {
35 | "$": {
36 | "cx": "1181100",
37 | "cy": "1181100"
38 | }
39 | }
40 | ]
41 | }
42 | ],
43 | "a:prstGeom": [
44 | {
45 | "$": {
46 | "prst": "ellipse"
47 | },
48 | "a:avLst": [
49 | ""
50 | ]
51 | }
52 | ]
53 | }
54 | ],
55 | "p:style": [
56 | {
57 | "a:lnRef": [
58 | {
59 | "$": {
60 | "idx": "1"
61 | },
62 | "a:schemeClr": [
63 | {
64 | "$": {
65 | "val": "accent1"
66 | }
67 | }
68 | ]
69 | }
70 | ],
71 | "a:fillRef": [
72 | {
73 | "$": {
74 | "idx": "3"
75 | },
76 | "a:schemeClr": [
77 | {
78 | "$": {
79 | "val": "accent1"
80 | }
81 | }
82 | ]
83 | }
84 | ],
85 | "a:effectRef": [
86 | {
87 | "$": {
88 | "idx": "2"
89 | },
90 | "a:schemeClr": [
91 | {
92 | "$": {
93 | "val": "accent1"
94 | }
95 | }
96 | ]
97 | }
98 | ],
99 | "a:fontRef": [
100 | {
101 | "$": {
102 | "idx": "minor"
103 | },
104 | "a:schemeClr": [
105 | {
106 | "$": {
107 | "val": "lt1"
108 | }
109 | }
110 | ]
111 | }
112 | ]
113 | }
114 | ],
115 | "p:txBody": [
116 | {
117 | "a:bodyPr": [
118 | {
119 | "$": {
120 | "rtlCol": "0",
121 | "anchor": "ctr"
122 | }
123 | }
124 | ],
125 | "a:lstStyle": [
126 | ""
127 | ],
128 | "a:p": [
129 | {
130 | "a:pPr": [
131 | {
132 | "$": {
133 | "algn": "ctr"
134 | }
135 | }
136 | ],
137 | "a:r": [
138 | {
139 | "a:rPr": [
140 | {
141 | "$": {
142 | "lang": "en-US",
143 | "dirty": "0",
144 | "smtClean": "0"
145 | }
146 | }
147 | ],
148 | "a:t": [
149 | "A default circle"
150 | ]
151 | }
152 | ],
153 | "a:endParaRPr": [
154 | {
155 | "$": {
156 | "lang": "en-US",
157 | "dirty": "0"
158 | }
159 | }
160 | ]
161 | }
162 | ]
163 | }
164 | ]
165 | }
166 | };
--------------------------------------------------------------------------------
/lib/fragment.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var xml2js = require('xml2js');
3 |
4 | module.exports = {
5 | fromXml: function(name, callback) {
6 | fs.readFile(__dirname + '/fragments/' + name, 'utf8', function(err, xml) {
7 | if (err) callback(err);
8 | xml2js.parseString(xml,{explicitArray : false}, function (err, js) {
9 | callback(null, js);
10 | });
11 | })
12 | },
13 |
14 | fromBinary: function(name, callback) {
15 | fs.readFile(__dirname + '/fragments/' + name, function(err, data) {
16 | if (err) callback(err);
17 | callback(null, data );
18 | })
19 | }
20 | }
--------------------------------------------------------------------------------
/lib/fragments/Microsoft_Excel_Sheet1.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/lib/fragments/Microsoft_Excel_Sheet1.xlsx
--------------------------------------------------------------------------------
/lib/fragments/js/chart.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "c:chartSpace": {
3 | "$": {
4 | "xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
5 | "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
6 | "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
7 | },
8 | "c:date1904": [
9 | {
10 | "$": {
11 | "val": "0"
12 | }
13 | }
14 | ],
15 | "c:lang": [
16 | {
17 | "$": {
18 | "val": "en-US"
19 | }
20 | }
21 | ],
22 | "c:roundedCorners": [
23 | {
24 | "$": {
25 | "val": "0"
26 | }
27 | }
28 | ],
29 | "mc:AlternateContent": [
30 | {
31 | "$": {
32 | "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
33 | },
34 | "mc:Choice": [
35 | {
36 | "$": {
37 | "Requires": "c14",
38 | "xmlns:c14": "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"
39 | },
40 | "c14:style": [
41 | {
42 | "$": {
43 | "val": "118"
44 | }
45 | }
46 | ]
47 | }
48 | ],
49 | "mc:Fallback": [
50 | {
51 | "c:style": [
52 | {
53 | "$": {
54 | "val": "18"
55 | }
56 | }
57 | ]
58 | }
59 | ]
60 | }
61 | ],
62 | "c:chart": [
63 | {
64 | "c:autoTitleDeleted": [
65 | {
66 | "$": {
67 | "val": "0"
68 | }
69 | }
70 | ],
71 | "c:plotArea": [
72 | {
73 | "c:layout": [
74 | ""
75 | ],
76 | "c:barChart": [
77 | {
78 | "c:barDir": [
79 | {
80 | "$": {
81 | "val": "bar"
82 | }
83 | }
84 | ],
85 | "c:grouping": [
86 | {
87 | "$": {
88 | "val": "clustered"
89 | }
90 | }
91 | ],
92 | "c:varyColors": [
93 | {
94 | "$": {
95 | "val": "0"
96 | }
97 | }
98 | ],
99 | "c:ser": [], // insert generated c:ser here
100 | "c:dLbls": [
101 | {
102 | "c:showLegendKey": [
103 | {
104 | "$": {
105 | "val": "0"
106 | }
107 | }
108 | ],
109 | "c:showVal": [
110 | {
111 | "$": {
112 | "val": "0"
113 | }
114 | }
115 | ],
116 | "c:showCatName": [
117 | {
118 | "$": {
119 | "val": "0"
120 | }
121 | }
122 | ],
123 | "c:showSerName": [
124 | {
125 | "$": {
126 | "val": "0"
127 | }
128 | }
129 | ],
130 | "c:showPercent": [
131 | {
132 | "$": {
133 | "val": "0"
134 | }
135 | }
136 | ],
137 | "c:showBubbleSize": [
138 | {
139 | "$": {
140 | "val": "0"
141 | }
142 | }
143 | ]
144 | }
145 | ],
146 | "c:gapWidth": [
147 | {
148 | "$": {
149 | "val": "150"
150 | }
151 | }
152 | ],
153 | "c:axId": [
154 | {
155 | "$": {
156 | "val": "2067994824"
157 | }
158 | },
159 | {
160 | "$": {
161 | "val": "-2074751000"
162 | }
163 | }
164 | ]
165 | }
166 | ],
167 | "c:catAx": [
168 | {
169 | "c:axId": [
170 | {
171 | "$": {
172 | "val": "2067994824"
173 | }
174 | }
175 | ],
176 | "c:scaling": [
177 | {
178 | "c:orientation": [
179 | {
180 | "$": {
181 | "val": "minMax"
182 | }
183 | }
184 | ]
185 | }
186 | ],
187 | "c:delete": [
188 | {
189 | "$": {
190 | "val": "0"
191 | }
192 | }
193 | ],
194 | "c:axPos": [
195 | {
196 | "$": {
197 | "val": "l"
198 | }
199 | }
200 | ],
201 | "c:majorTickMark": [
202 | {
203 | "$": {
204 | "val": "out"
205 | }
206 | }
207 | ],
208 | "c:minorTickMark": [
209 | {
210 | "$": {
211 | "val": "none"
212 | }
213 | }
214 | ],
215 | "c:tickLblPos": [
216 | {
217 | "$": {
218 | "val": "nextTo"
219 | }
220 | }
221 | ],
222 | "c:crossAx": [
223 | {
224 | "$": {
225 | "val": "-2074751000"
226 | }
227 | }
228 | ],
229 | "c:crosses": [
230 | {
231 | "$": {
232 | "val": "autoZero"
233 | }
234 | }
235 | ],
236 | "c:auto": [
237 | {
238 | "$": {
239 | "val": "1"
240 | }
241 | }
242 | ],
243 | "c:lblAlgn": [
244 | {
245 | "$": {
246 | "val": "ctr"
247 | }
248 | }
249 | ],
250 | "c:lblOffset": [
251 | {
252 | "$": {
253 | "val": "100"
254 | }
255 | }
256 | ],
257 | "c:noMultiLvlLbl": [
258 | {
259 | "$": {
260 | "val": "0"
261 | }
262 | }
263 | ]
264 | }
265 | ],
266 | "c:valAx": [
267 | {
268 | "c:axId": [
269 | {
270 | "$": {
271 | "val": "-2074751000"
272 | }
273 | }
274 | ],
275 | "c:scaling": [
276 | {
277 | "c:orientation": [
278 | {
279 | "$": {
280 | "val": "minMax"
281 | }
282 | }
283 | ]
284 | }
285 | ],
286 | "c:delete": [
287 | {
288 | "$": {
289 | "val": "0"
290 | }
291 | }
292 | ],
293 | "c:axPos": [
294 | {
295 | "$": {
296 | "val": "b"
297 | }
298 | }
299 | ],
300 | "c:majorGridlines": [
301 | ""
302 | ],
303 | "c:numFmt": [
304 | {
305 | "$": {
306 | "formatCode": "General",
307 | "sourceLinked": "1"
308 | }
309 | }
310 | ],
311 | "c:majorTickMark": [
312 | {
313 | "$": {
314 | "val": "out"
315 | }
316 | }
317 | ],
318 | "c:minorTickMark": [
319 | {
320 | "$": {
321 | "val": "none"
322 | }
323 | }
324 | ],
325 | "c:tickLblPos": [
326 | {
327 | "$": {
328 | "val": "nextTo"
329 | }
330 | }
331 | ],
332 | "c:crossAx": [
333 | {
334 | "$": {
335 | "val": "2067994824"
336 | }
337 | }
338 | ],
339 | "c:crosses": [
340 | {
341 | "$": {
342 | "val": "autoZero"
343 | }
344 | }
345 | ],
346 | "c:crossBetween": [
347 | {
348 | "$": {
349 | "val": "between"
350 | }
351 | }
352 | ]
353 | }
354 | ]
355 | }
356 | ],
357 | "c:legend": [
358 | {
359 | "c:legendPos": [
360 | {
361 | "$": {
362 | "val": "r"
363 | }
364 | }
365 | ],
366 | "c:layout": [
367 | ""
368 | ],
369 | "c:overlay": [
370 | {
371 | "$": {
372 | "val": "0"
373 | }
374 | }
375 | ]
376 | }
377 | ],
378 | "c:plotVisOnly": [
379 | {
380 | "$": {
381 | "val": "1"
382 | }
383 | }
384 | ],
385 | "c:dispBlanksAs": [
386 | {
387 | "$": {
388 | "val": "gap"
389 | }
390 | }
391 | ],
392 | "c:showDLblsOverMax": [
393 | {
394 | "$": {
395 | "val": "0"
396 | }
397 | }
398 | ]
399 | }
400 | ],
401 | "c:txPr": [
402 | {
403 | "a:bodyPr": [
404 | ""
405 | ],
406 | "a:lstStyle": [
407 | ""
408 | ],
409 | "a:p": [
410 | {
411 | "a:pPr": [
412 | {
413 | "a:defRPr": [
414 | {
415 | "$": {
416 | "sz": "1800"
417 | }
418 | }
419 | ]
420 | }
421 | ],
422 | "a:endParaRPr": [
423 | {
424 | "$": {
425 | "lang": "en-US"
426 | }
427 | }
428 | ]
429 | }
430 | ]
431 | }
432 | ],
433 | "c:externalData": [
434 | {
435 | "$": {
436 | "r:id": "rId1"
437 | },
438 | "c:autoUpdate": [
439 | {
440 | "$": {
441 | "val": "0"
442 | }
443 | }
444 | ]
445 | }
446 | ]
447 | }
448 | }
--------------------------------------------------------------------------------
/lib/fragments/js/chartframe.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "p:graphicFrame": {
3 | "p:nvGraphicFramePr": {
4 | "p:cNvPr": {
5 | "$": {
6 | "id": "5",
7 | "name": "Chart 4"
8 | }
9 | },
10 | "p:cNvGraphicFramePr": "",
11 | "p:nvPr": {
12 | "p:extLst": {
13 | "p:ext": {
14 | "$": {
15 | "uri": "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}"
16 | },
17 | "p14:modId": {
18 | "$": {
19 | "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
20 | "val": "3543180680"
21 | }
22 | }
23 | }
24 | }
25 | }
26 | },
27 | "p:xfrm": {
28 | "a:off": {
29 | "$": {
30 | "x": "1524000",
31 | "y": "1397000"
32 | }
33 | },
34 | "a:ext": {
35 | "$": {
36 | "cx": "6096000",
37 | "cy": "4064000"
38 | }
39 | }
40 | },
41 | "a:graphic": {
42 | "a:graphicData": {
43 | "$": {
44 | "uri": "http://schemas.openxmlformats.org/drawingml/2006/chart"
45 | },
46 | "c:chart": {
47 | "$": {
48 | "xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
49 | "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
50 | "r:id": "rId2"
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/lib/fragments/xml/chart.xml:
--------------------------------------------------------------------------------
1 |
2 |
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 | Sheet1!$B$1
30 |
31 |
32 |
33 | Series 1
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Sheet1!$A$2:$A$5
42 |
43 |
44 |
45 | Category 1
46 |
47 |
48 | Category 2
49 |
50 |
51 | Category 3
52 |
53 |
54 | Category 4
55 |
56 |
57 |
58 |
59 |
60 |
61 | Sheet1!$B$2:$B$5
62 |
63 | General
64 |
65 |
66 | 4.3
67 |
68 |
69 | 2.5
70 |
71 |
72 | 3.5
73 |
74 |
75 | 4.5
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Sheet1!$C$1
87 |
88 |
89 |
90 | Series 2
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Sheet1!$A$2:$A$5
99 |
100 |
101 |
102 | Category 1
103 |
104 |
105 | Category 2
106 |
107 |
108 | Category 3
109 |
110 |
111 | Category 4
112 |
113 |
114 |
115 |
116 |
117 |
118 | Sheet1!$C$2:$C$5
119 |
120 | General
121 |
122 |
123 | 2.4
124 |
125 |
126 | 4.4
127 |
128 |
129 | 1.8
130 |
131 |
132 | 2.8
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | Sheet1!$D$1
144 |
145 |
146 |
147 | Series 3
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | Sheet1!$A$2:$A$5
156 |
157 |
158 |
159 | Category 1
160 |
161 |
162 | Category 2
163 |
164 |
165 | Category 3
166 |
167 |
168 | Category 4
169 |
170 |
171 |
172 |
173 |
174 |
175 | Sheet1!$D$2:$D$5
176 |
177 | General
178 |
179 |
180 | 2.0
181 |
182 |
183 | 2.0
184 |
185 |
186 | 3.0
187 |
188 |
189 | 5.0
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
--------------------------------------------------------------------------------
/lib/fragments/xml/chartframe.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/lib/msexcel-builder.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.8.0
2 |
3 | /*
4 | MS Excel 2007 Creater v0.0.1
5 | Author : chuanyi.zheng@gmail.com
6 | History: 2012/11/07 first created
7 | */
8 |
9 | (function() {
10 | var ContentTypes, DocPropsApp, JSZip, SharedStrings, Sheet, Style, Workbook, XlRels, XlWorkbook, a, baseXl, opt, tool, xml,
11 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
12 |
13 | JSZip = require('jszip');
14 |
15 | xml = require('xmlbuilder');
16 |
17 | a = 4;
18 |
19 | tool = {
20 | i2a: function(i) {
21 | return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123'.charAt(i - 1);
22 | }
23 | };
24 |
25 | opt = {
26 | tmpl_path: __dirname
27 | };
28 |
29 | ContentTypes = (function() {
30 | function ContentTypes(book) {
31 | this.book = book;
32 | }
33 |
34 | ContentTypes.prototype.toxml = function() {
35 | var i, types, _i, _ref;
36 | types = xml.create('Types', {
37 | version: '1.0',
38 | encoding: 'UTF-8',
39 | standalone: true
40 | });
41 | types.att('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types');
42 | types.ele('Override', {
43 | PartName: '/xl/theme/theme1.xml',
44 | ContentType: 'application/vnd.openxmlformats-officedocument.theme+xml'
45 | });
46 | types.ele('Override', {
47 | PartName: '/xl/styles.xml',
48 | ContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml'
49 | });
50 | types.ele('Default', {
51 | Extension: 'rels',
52 | ContentType: 'application/vnd.openxmlformats-package.relationships+xml'
53 | });
54 | types.ele('Default', {
55 | Extension: 'xml',
56 | ContentType: 'application/xml'
57 | });
58 | types.ele('Override', {
59 | PartName: '/xl/workbook.xml',
60 | ContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml'
61 | });
62 | types.ele('Override', {
63 | PartName: '/docProps/app.xml',
64 | ContentType: 'application/vnd.openxmlformats-officedocument.extended-properties+xml'
65 | });
66 | for (i = _i = 1, _ref = this.book.sheets.length; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
67 | types.ele('Override', {
68 | PartName: '/xl/worksheets/sheet' + i + '.xml',
69 | ContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml'
70 | });
71 | }
72 | types.ele('Override', {
73 | PartName: '/xl/sharedStrings.xml',
74 | ContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml'
75 | });
76 | types.ele('Override', {
77 | PartName: '/docProps/core.xml',
78 | ContentType: 'application/vnd.openxmlformats-package.core-properties+xml'
79 | });
80 | return types.end();
81 | };
82 |
83 | return ContentTypes;
84 |
85 | })();
86 |
87 | DocPropsApp = (function() {
88 | function DocPropsApp(book) {
89 | this.book = book;
90 | }
91 |
92 | DocPropsApp.prototype.toxml = function() {
93 | var i, props, tmp, _i, _ref;
94 | props = xml.create('Properties', {
95 | version: '1.0',
96 | encoding: 'UTF-8',
97 | standalone: true
98 | });
99 | props.att('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties');
100 | props.att('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
101 | props.ele('Application', 'Microsoft Excel');
102 | props.ele('DocSecurity', '0');
103 | props.ele('ScaleCrop', 'false');
104 | tmp = props.ele('HeadingPairs').ele('vt:vector', {
105 | size: 2,
106 | baseType: 'variant'
107 | });
108 | tmp.ele('vt:variant').ele('vt:lpstr', '工作表');
109 | tmp.ele('vt:variant').ele('vt:i4', '' + this.book.sheets.length);
110 | tmp = props.ele('TitlesOfParts').ele('vt:vector', {
111 | size: this.book.sheets.length,
112 | baseType: 'lpstr'
113 | });
114 | for (i = _i = 1, _ref = this.book.sheets.length; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
115 | tmp.ele('vt:lpstr', this.book.sheets[i - 1].name);
116 | }
117 | props.ele('Company');
118 | props.ele('LinksUpToDate', 'false');
119 | props.ele('SharedDoc', 'false');
120 | props.ele('HyperlinksChanged', 'false');
121 | props.ele('AppVersion', '12.0000');
122 | return props.end();
123 | };
124 |
125 | return DocPropsApp;
126 |
127 | })();
128 |
129 | XlWorkbook = (function() {
130 | function XlWorkbook(book) {
131 | this.book = book;
132 | }
133 |
134 | XlWorkbook.prototype.toxml = function() {
135 | var i, tmp, wb, _i, _ref;
136 | wb = xml.create('workbook', {
137 | version: '1.0',
138 | encoding: 'UTF-8',
139 | standalone: true
140 | });
141 | wb.att('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
142 | wb.att('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
143 | wb.ele('fileVersion ', {
144 | appName: 'xl',
145 | lastEdited: '4',
146 | lowestEdited: '4',
147 | rupBuild: '4505'
148 | });
149 | wb.ele('workbookPr', {
150 | filterPrivacy: '1',
151 | defaultThemeVersion: '124226'
152 | });
153 | wb.ele('bookViews').ele('workbookView ', {
154 | xWindow: '0',
155 | yWindow: '90',
156 | windowWidth: '19200',
157 | windowHeight: '11640'
158 | });
159 | tmp = wb.ele('sheets');
160 | for (i = _i = 1, _ref = this.book.sheets.length; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
161 | tmp.ele('sheet', {
162 | name: this.book.sheets[i - 1].name,
163 | sheetId: '' + i,
164 | 'r:id': 'rId' + i
165 | });
166 | }
167 | wb.ele('calcPr', {
168 | calcId: '124519'
169 | });
170 | return wb.end();
171 | };
172 |
173 | return XlWorkbook;
174 |
175 | })();
176 |
177 | XlRels = (function() {
178 | function XlRels(book) {
179 | this.book = book;
180 | }
181 |
182 | XlRels.prototype.toxml = function() {
183 | var i, rs, _i, _ref;
184 | rs = xml.create('Relationships', {
185 | version: '1.0',
186 | encoding: 'UTF-8',
187 | standalone: true
188 | });
189 | rs.att('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
190 | for (i = _i = 1, _ref = this.book.sheets.length; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
191 | rs.ele('Relationship', {
192 | Id: 'rId' + i,
193 | Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
194 | Target: 'worksheets/sheet' + i + '.xml'
195 | });
196 | }
197 | rs.ele('Relationship', {
198 | Id: 'rId' + (this.book.sheets.length + 1),
199 | Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
200 | Target: 'theme/theme1.xml'
201 | });
202 | rs.ele('Relationship', {
203 | Id: 'rId' + (this.book.sheets.length + 2),
204 | Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
205 | Target: 'styles.xml'
206 | });
207 | rs.ele('Relationship', {
208 | Id: 'rId' + (this.book.sheets.length + 3),
209 | Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
210 | Target: 'sharedStrings.xml'
211 | });
212 | return rs.end();
213 | };
214 |
215 | return XlRels;
216 |
217 | })();
218 |
219 | SharedStrings = (function() {
220 | function SharedStrings() {
221 | this.cache = {};
222 | this.arr = [];
223 | }
224 |
225 | SharedStrings.prototype.str2id = function(s) {
226 | var id;
227 | id = this.cache[s];
228 | if (id) {
229 | return id;
230 | } else {
231 | this.arr.push(s);
232 | this.cache[s] = this.arr.length;
233 | return this.arr.length;
234 | }
235 | };
236 |
237 | SharedStrings.prototype.toxml = function() {
238 | var i, si, sst, _i, _ref;
239 | sst = xml.create('sst', {
240 | version: '1.0',
241 | encoding: 'UTF-8',
242 | standalone: true
243 | });
244 | sst.att('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
245 | sst.att('count', '' + this.arr.length);
246 | sst.att('uniqueCount', '' + this.arr.length);
247 | for (i = _i = 0, _ref = this.arr.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
248 | si = sst.ele('si');
249 | si.ele('t', this.arr[i]);
250 | si.ele('phoneticPr', {
251 | fontId: 1,
252 | type: 'noConversion'
253 | });
254 | }
255 | return sst.end();
256 | };
257 |
258 | return SharedStrings;
259 |
260 | })();
261 |
262 | Sheet = (function() {
263 | function Sheet(book, name, cols, rows) {
264 | var i, j, _i, _j, _ref, _ref1;
265 | this.book = book;
266 | this.name = name;
267 | this.cols = cols;
268 | this.rows = rows;
269 | this.data = {};
270 | for (i = _i = 1, _ref = this.rows; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
271 | this.data[i] = {};
272 | for (j = _j = 1, _ref1 = this.cols; 1 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 1 <= _ref1 ? ++_j : --_j) {
273 | this.data[i][j] = {
274 | v: 0
275 | };
276 | }
277 | }
278 | this.merges = [];
279 | this.col_wd = [];
280 | this.row_ht = {};
281 | this.styles = {};
282 | }
283 |
284 | Sheet.prototype.set = function(col, row, str) {
285 | if ((str != null) && str !== '') {
286 | return this.data[row][col].v = this.book.ss.str2id('' + str);
287 | }
288 | };
289 |
290 | Sheet.prototype.merge = function(from_cell, to_cell) {
291 | return this.merges.push({
292 | from: from_cell,
293 | to: to_cell
294 | });
295 | };
296 |
297 | Sheet.prototype.width = function(col, wd) {
298 | return this.col_wd.push({
299 | c: col,
300 | cw: wd
301 | });
302 | };
303 |
304 | Sheet.prototype.height = function(row, ht) {
305 | return this.row_ht[row] = ht;
306 | };
307 |
308 | Sheet.prototype.font = function(col, row, font_s) {
309 | return this.styles['font_' + col + '_' + row] = this.book.st.font2id(font_s);
310 | };
311 |
312 | Sheet.prototype.fill = function(col, row, fill_s) {
313 | return this.styles['fill_' + col + '_' + row] = this.book.st.fill2id(fill_s);
314 | };
315 |
316 | Sheet.prototype.border = function(col, row, bder_s) {
317 | return this.styles['bder_' + col + '_' + row] = this.book.st.bder2id(bder_s);
318 | };
319 |
320 | Sheet.prototype.align = function(col, row, align_s) {
321 | return this.styles['algn_' + col + '_' + row] = align_s;
322 | };
323 |
324 | Sheet.prototype.valign = function(col, row, valign_s) {
325 | return this.styles['valgn_' + col + '_' + row] = valign_s;
326 | };
327 |
328 | Sheet.prototype.rotate = function(col, row, textRotation) {
329 | return this.styles['rotate_' + col + '_' + row] = textRotation;
330 | };
331 |
332 | Sheet.prototype.wrap = function(col, row, wrap_s) {
333 | return this.styles['wrap_' + col + '_' + row] = wrap_s;
334 | };
335 |
336 | Sheet.prototype.style_id = function(col, row) {
337 | var id, inx, style;
338 | inx = '_' + col + '_' + row;
339 | style = {
340 | font_id: this.styles['font' + inx],
341 | fill_id: this.styles['fill' + inx],
342 | bder_id: this.styles['bder' + inx],
343 | align: this.styles['algn' + inx],
344 | valign: this.styles['valgn' + inx],
345 | rotate: this.styles['rotate' + inx],
346 | wrap: this.styles['wrap' + inx]
347 | };
348 | id = this.book.st.style2id(style);
349 | return id;
350 | };
351 |
352 | Sheet.prototype.toxml = function() {
353 | var c, cols, cw, ht, i, ix, j, m, mc, r, sd, sid, ws, _i, _j, _k, _l, _len, _len1, _ref, _ref1, _ref2, _ref3;
354 | ws = xml.create('worksheet', {
355 | version: '1.0',
356 | encoding: 'UTF-8',
357 | standalone: true
358 | });
359 | ws.att('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
360 | ws.att('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
361 | ws.ele('dimension', {
362 | ref: 'A1'
363 | });
364 | ws.ele('sheetViews').ele('sheetView', {
365 | workbookViewId: '0'
366 | });
367 | ws.ele('sheetFormatPr', {
368 | defaultRowHeight: '13.5'
369 | });
370 | if (this.col_wd.length > 0) {
371 | cols = ws.ele('cols');
372 | _ref = this.col_wd;
373 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
374 | cw = _ref[_i];
375 | cols.ele('col', {
376 | min: '' + cw.c,
377 | max: '' + cw.c,
378 | width: cw.cw,
379 | customWidth: '1'
380 | });
381 | }
382 | }
383 | sd = ws.ele('sheetData');
384 | for (i = _j = 1, _ref1 = this.rows; 1 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 1 <= _ref1 ? ++_j : --_j) {
385 | r = sd.ele('row', {
386 | r: '' + i,
387 | spans: '1:' + this.cols
388 | });
389 | ht = this.row_ht[i];
390 | if (ht) {
391 | r.att('ht', ht);
392 | r.att('customHeight', '1');
393 | }
394 | for (j = _k = 1, _ref2 = this.cols; 1 <= _ref2 ? _k <= _ref2 : _k >= _ref2; j = 1 <= _ref2 ? ++_k : --_k) {
395 | ix = this.data[i][j];
396 | sid = this.style_id(j, i);
397 | if ((ix.v !== 0) || (sid !== 1)) {
398 | c = r.ele('c', {
399 | r: '' + tool.i2a(j) + i
400 | });
401 | if (sid !== 1) {
402 | c.att('s', '' + (sid - 1));
403 | }
404 | if (ix.v !== 0) {
405 | c.att('t', 's');
406 | c.ele('v', '' + (ix.v - 1));
407 | }
408 | }
409 | }
410 | }
411 | if (this.merges.length > 0) {
412 | mc = ws.ele('mergeCells', {
413 | count: this.merges.length
414 | });
415 | _ref3 = this.merges;
416 | for (_l = 0, _len1 = _ref3.length; _l < _len1; _l++) {
417 | m = _ref3[_l];
418 | mc.ele('mergeCell', {
419 | ref: '' + tool.i2a(m.from.col) + m.from.row + ':' + tool.i2a(m.to.col) + m.to.row
420 | });
421 | }
422 | }
423 | ws.ele('phoneticPr', {
424 | fontId: '1',
425 | type: 'noConversion'
426 | });
427 | ws.ele('pageMargins', {
428 | left: '0.7',
429 | right: '0.7',
430 | top: '0.75',
431 | bottom: '0.75',
432 | header: '0.3',
433 | footer: '0.3'
434 | });
435 | ws.ele('pageSetup', {
436 | paperSize: '9',
437 | orientation: 'portrait',
438 | horizontalDpi: '200',
439 | verticalDpi: '200'
440 | });
441 | return ws.end();
442 | };
443 |
444 | return Sheet;
445 |
446 | })();
447 |
448 | Style = (function() {
449 | function Style(book) {
450 | this.book = book;
451 | this.cache = {};
452 | this.mfonts = [];
453 | this.mfills = [];
454 | this.mbders = [];
455 | this.mstyle = [];
456 | this.with_default();
457 | }
458 |
459 | Style.prototype.with_default = function() {
460 | this.def_font_id = this.font2id(null);
461 | this.def_fill_id = this.fill2id(null);
462 | this.def_bder_id = this.bder2id(null);
463 | this.def_align = '-';
464 | this.def_valign = '-';
465 | this.def_rotate = '-';
466 | this.def_wrap = '-';
467 | return this.def_style_id = this.style2id({
468 | font_id: this.def_font_id,
469 | fill_id: this.def_fill_id,
470 | bder_id: this.def_bder_id,
471 | align: this.def_align,
472 | valign: this.def_valign,
473 | rotate: this.def_rotate
474 | });
475 | };
476 |
477 | Style.prototype.font2id = function(font) {
478 | var id, k;
479 | font || (font = {});
480 | font.bold || (font.bold = '-');
481 | font.iter || (font.iter = '-');
482 | font.sz || (font.sz = '11');
483 | font.color || (font.color = '-');
484 | font.name || (font.name = '宋体');
485 | font.scheme || (font.scheme = 'minor');
486 | font.family || (font.family = '2');
487 | k = 'font_' + font.bold + font.iter + font.sz + font.color + font.name + font.scheme + font.family;
488 | id = this.cache[k];
489 | if (id) {
490 | return id;
491 | } else {
492 | this.mfonts.push(font);
493 | this.cache[k] = this.mfonts.length;
494 | return this.mfonts.length;
495 | }
496 | };
497 |
498 | Style.prototype.fill2id = function(fill) {
499 | var id, k;
500 | fill || (fill = {});
501 | fill.type || (fill.type = 'none');
502 | fill.bgColor || (fill.bgColor = '-');
503 | fill.fgColor || (fill.fgColor = '-');
504 | k = 'fill_' + fill.type + fill.bgColor + fill.fgColor;
505 | id = this.cache[k];
506 | if (id) {
507 | return id;
508 | } else {
509 | this.mfills.push(fill);
510 | this.cache[k] = this.mfills.length;
511 | return this.mfills.length;
512 | }
513 | };
514 |
515 | Style.prototype.bder2id = function(bder) {
516 | var id, k;
517 | bder || (bder = {});
518 | bder.left || (bder.left = '-');
519 | bder.right || (bder.right = '-');
520 | bder.top || (bder.top = '-');
521 | bder.bottom || (bder.bottom = '-');
522 | k = 'bder_' + bder.left + '_' + bder.right + '_' + bder.top + '_' + bder.bottom;
523 | id = this.cache[k];
524 | if (id) {
525 | return id;
526 | } else {
527 | this.mbders.push(bder);
528 | this.cache[k] = this.mbders.length;
529 | return this.mbders.length;
530 | }
531 | };
532 |
533 | Style.prototype.style2id = function(style) {
534 | var id, k;
535 | style.align || (style.align = this.def_align);
536 | style.valign || (style.valign = this.def_valign);
537 | style.rotate || (style.rotate = this.def_rotate);
538 | style.wrap || (style.wrap = this.def_wrap);
539 | style.font_id || (style.font_id = this.def_font_id);
540 | style.fill_id || (style.fill_id = this.def_fill_id);
541 | style.bder_id || (style.bder_id = this.def_bder_id);
542 | k = 's_' + style.font_id + '_' + style.fill_id + '_' + style.bder_id + '_' + style.align + '_' + style.valign + '_' + style.wrap + '_' + style.rotate;
543 | id = this.cache[k];
544 | if (id) {
545 | return id;
546 | } else {
547 | this.mstyle.push(style);
548 | this.cache[k] = this.mstyle.length;
549 | return this.mstyle.length;
550 | }
551 | };
552 |
553 | Style.prototype.toxml = function() {
554 | var bders, cs, e, ea, es, fills, fonts, o, ss, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
555 | ss = xml.create('styleSheet', {
556 | version: '1.0',
557 | encoding: 'UTF-8',
558 | standalone: true
559 | });
560 | ss.att('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
561 | fonts = ss.ele('fonts', {
562 | count: this.mfonts.length
563 | });
564 | _ref = this.mfonts;
565 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
566 | o = _ref[_i];
567 | e = fonts.ele('font');
568 | if (o.bold !== '-') {
569 | e.ele('b');
570 | }
571 | if (o.iter !== '-') {
572 | e.ele('i');
573 | }
574 | e.ele('sz', {
575 | val: o.sz
576 | });
577 | if (o.color !== '-') {
578 | e.ele('color', {
579 | theme: o.color
580 | });
581 | }
582 | e.ele('name', {
583 | val: o.name
584 | });
585 | e.ele('family', {
586 | val: o.family
587 | });
588 | e.ele('charset', {
589 | val: '134'
590 | });
591 | if (o.scheme !== '-') {
592 | e.ele('scheme', {
593 | val: 'minor'
594 | });
595 | }
596 | }
597 | fills = ss.ele('fills', {
598 | count: this.mfills.length
599 | });
600 | _ref1 = this.mfills;
601 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
602 | o = _ref1[_j];
603 | e = fills.ele('fill');
604 | es = e.ele('patternFill', {
605 | patternType: o.type
606 | });
607 | if (o.fgColor !== '-') {
608 | es.ele('fgColor', {
609 | theme: '8',
610 | tint: '0.79998168889431442'
611 | });
612 | }
613 | if (o.bgColor !== '-') {
614 | es.ele('bgColor', {
615 | indexed: o.bgColor
616 | });
617 | }
618 | }
619 | bders = ss.ele('borders', {
620 | count: this.mbders.length
621 | });
622 | _ref2 = this.mbders;
623 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
624 | o = _ref2[_k];
625 | e = bders.ele('border');
626 | if (o.left !== '-') {
627 | e.ele('left', {
628 | style: o.left
629 | }).ele('color', {
630 | auto: '1'
631 | });
632 | } else {
633 | e.ele('left');
634 | }
635 | if (o.right !== '-') {
636 | e.ele('right', {
637 | style: o.right
638 | }).ele('color', {
639 | auto: '1'
640 | });
641 | } else {
642 | e.ele('right');
643 | }
644 | if (o.top !== '-') {
645 | e.ele('top', {
646 | style: o.top
647 | }).ele('color', {
648 | auto: '1'
649 | });
650 | } else {
651 | e.ele('top');
652 | }
653 | if (o.bottom !== '-') {
654 | e.ele('bottom', {
655 | style: o.bottom
656 | }).ele('color', {
657 | auto: '1'
658 | });
659 | } else {
660 | e.ele('bottom');
661 | }
662 | e.ele('diagonal');
663 | }
664 | ss.ele('cellStyleXfs', {
665 | count: '1'
666 | }).ele('xf', {
667 | numFmtId: '0',
668 | fontId: '0',
669 | fillId: '0',
670 | borderId: '0'
671 | }).ele('alignment', {
672 | vertical: 'center'
673 | });
674 | cs = ss.ele('cellXfs', {
675 | count: this.mstyle.length
676 | });
677 | _ref3 = this.mstyle;
678 | for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
679 | o = _ref3[_l];
680 | e = cs.ele('xf', {
681 | numFmtId: '0',
682 | fontId: o.font_id - 1,
683 | fillId: o.fill_id - 1,
684 | borderId: o.bder_id - 1,
685 | xfId: '0'
686 | });
687 | if (o.font_id !== 1) {
688 | e.att('applyFont', '1');
689 | }
690 | if (o.fill_id !== 1) {
691 | e.att('applyFill', '1');
692 | }
693 | if (o.bder_id !== 1) {
694 | e.att('applyBorder', '1');
695 | }
696 | if (o.align !== '-' || o.valign !== '-' || o.wrap !== '-') {
697 | e.att('applyAlignment', '1');
698 | ea = e.ele('alignment', {
699 | textRotation: (o.rotate === '-' ? '0' : o.rotate),
700 | horizontal: (o.align === '-' ? 'left' : o.align),
701 | vertical: (o.valign === '-' ? 'top' : o.valign)
702 | });
703 | if (o.wrap !== '-') {
704 | ea.att('wrapText', '1');
705 | }
706 | }
707 | }
708 | ss.ele('cellStyles', {
709 | count: '1'
710 | }).ele('cellStyle', {
711 | name: '常规',
712 | xfId: '0',
713 | builtinId: '0'
714 | });
715 | ss.ele('dxfs', {
716 | count: '0'
717 | });
718 | ss.ele('tableStyles', {
719 | count: '0',
720 | defaultTableStyle: 'TableStyleMedium9',
721 | defaultPivotStyle: 'PivotStyleLight16'
722 | });
723 | return ss.end();
724 | };
725 |
726 | return Style;
727 |
728 | })();
729 |
730 | Workbook = (function() {
731 | function Workbook() {
732 | this.generate = __bind(this.generate, this);
733 | this.id = '' + parseInt(Math.random() * 9999999);
734 | this.sheets = [];
735 | this.ss = new SharedStrings;
736 | this.ct = new ContentTypes(this);
737 | this.da = new DocPropsApp(this);
738 | this.wb = new XlWorkbook(this);
739 | this.re = new XlRels(this);
740 | this.st = new Style(this);
741 | }
742 |
743 | Workbook.prototype.createSheet = function(name, cols, rows) {
744 | var sheet;
745 | sheet = new Sheet(this, name, cols, rows);
746 | this.sheets.push(sheet);
747 | return sheet;
748 | };
749 |
750 | Workbook.prototype.generate = function(cb) {
751 | var i, key, zip, _i, _ref;
752 | zip = new JSZip;
753 | for (key in baseXl) {
754 | zip.file(key, baseXl[key]);
755 | }
756 | zip.file('[Content_Types].xml', this.ct.toxml());
757 | zip.file('docProps/app.xml', this.da.toxml());
758 | zip.file('xl/workbook.xml', this.wb.toxml());
759 | zip.file('xl/sharedStrings.xml', this.ss.toxml());
760 | zip.file('xl/_rels/workbook.xml.rels', this.re.toxml());
761 | for (i = _i = 0, _ref = this.sheets.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
762 | zip.file('xl/worksheets/sheet' + (i + 1) + '.xml', this.sheets[i].toxml());
763 | }
764 | zip.file('xl/styles.xml', this.st.toxml());
765 | return cb(null, zip);
766 | };
767 |
768 | return Workbook;
769 |
770 | })();
771 |
772 | module.exports = {
773 | createWorkbook: function() {
774 | return new Workbook();
775 | }
776 | };
777 |
778 | baseXl = {
779 | '_rels/.rels': '',
780 | 'docProps/core.xml': 'Administrator2006-09-13T11:21:51Z2006-09-13T11:21:55Z',
781 | 'xl/theme/theme1.xml': '',
782 | 'xl/styles.xml': ''
783 | };
784 |
785 | }).call(this);
786 |
787 | //# sourceMappingURL=msexcel-builder.js.map
788 |
--------------------------------------------------------------------------------
/lib/officechart.js:
--------------------------------------------------------------------------------
1 | /// @author vtloc
2 | /// @date 2014Jan09
3 | /// @author GradualStudent
4 | /// @date 2015jan06
5 | /// This module's purpose is to transform
6 | ///
7 | var _ = require('lodash'); // replacing underscore to get merge function
8 | var fs = require('fs');
9 | var _chartSpecs = require('./charts');
10 |
11 |
12 | function OfficeChart(chartInfo) {
13 |
14 | if (chartInfo instanceof OfficeChart) {
15 | return chartInfo;
16 | }
17 |
18 | return {
19 |
20 | chartSpec: null, // Javascript object that represents the XML tree for the PowerPoint chart
21 |
22 | toJSON: function () {
23 | return this.chartSpec;
24 | },
25 |
26 | getClass: function() { return "OfficeChart"; },
27 |
28 | ///
29 | /// @brief Create XML representation of chart object
30 | /// @param[chartInfo] object
31 | /// {
32 | /// title: 'eSurvey chart',
33 | /// data: [ // array of series
34 | /// {
35 | /// name: 'Income',
36 | /// labels: ['2005', '2006', '2007', '2008', '2009'],
37 | /// values: [23.5, 26.2, 30.1, 29.5, 24.6],
38 | /// color: 'ff0000'
39 | /// }
40 | /// ],
41 | /// overlap: "0",
42 | /// gapWidth: "150"
43 | ///
44 | /// }
45 |
46 | initialize: function (chartInfo) {
47 |
48 | if (chartInfo.getClass && chartInfo.getClass()== 'OfficeChart') {
49 | return chartInfo;
50 | }
51 |
52 | // overlap ["50"] is handled as an option within the chartbase
53 | // gapWidth ["150"] is handled as an option within the chartbase
54 | // valAxisCrossAtMaxCategory [true|false] is handled as an option within the chart base
55 | // catAxisReverseOrder [true|false] is handled as an option within the chart base
56 |
57 | this.chartSpec = OfficeChart.getChartBase(chartInfo); // get foundation XML for the chart type
58 |
59 | // Below are methods for handling options with more complex XML to mix in
60 | this.setData(chartInfo['data']);
61 | this.setTitle(chartInfo.title || chartInfo.name);
62 | this.setValAxisTitle(chartInfo.valAxisTitle);
63 | this.setCatAxisTitle(chartInfo.catAxisTitle);
64 | this.setValAxisNumFmt(chartInfo.valAxisNumFmt);
65 | this.setValAxisScale(chartInfo.valAxisMinValue, chartInfo.valAxisMaxValue);
66 | this.setTextSize(chartInfo.fontSize);
67 | this.mergeChartXml(chartInfo.xml);
68 | this.setValAxisMajorGridlines(chartInfo.valAxisMajorGridlines);
69 | this.setValAxisMinorGridlines(chartInfo.valAxisMinorGridlines);
70 |
71 | return this;
72 | },
73 |
74 | setTextSize: function(textSize) {
75 | if (textSize !== undefined) {
76 | var textRef = this._text(textSize);
77 | _.merge(this.chartSpec['c:chartSpace'], textRef);
78 | }
79 | },
80 |
81 | setTitle: function (title) {
82 | if (title !== undefined) {
83 | var titleRef = this._title(chartInfo.title || chartInfo.name);
84 | _.merge(this.chartSpec['c:chartSpace']['c:chart'], titleRef)
85 | }
86 | },
87 |
88 | setValAxisTitle: function (title) {
89 | if (title) {
90 | var titleRef = this._title(title);
91 | _.merge(this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:valAx'], titleRef);
92 | }
93 | },
94 |
95 | setCatAxisTitle: function (title) {
96 | if (title) {
97 | var titleRef = this._title(title);
98 | _.merge(this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:catAx'], titleRef);
99 | }
100 | },
101 |
102 | setValAxisNumFmt: function (format) {
103 | if (format !== undefined) {
104 | var numFmtRef = this._numFmt(format);
105 | _.merge(this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:valAx'], numFmtRef)
106 | }
107 | },
108 |
109 | setValAxisScale: function (min, max) {
110 | if (min!==undefined || max!== undefined) {
111 | var scalingRef = this._scaling(min, max);
112 | _.merge(this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:valAx'], scalingRef);
113 | }
114 | },
115 |
116 | mergeChartXml: function (xml) {
117 | if (xml !== undefined) {
118 | _.merge(this.chartSpec['c:chartSpace'], xml);
119 | }
120 | },
121 |
122 | setValAxisMajorGridlines: function(boolean) {
123 | if (boolean) {
124 | this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:valAx']['c:majorGridlines'] = {};
125 | }
126 | },
127 | setValAxisMinorGridlines: function(boolean) {
128 | if (boolean) {
129 | this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:valAx']['c:minorGridlines'] = {};
130 | }
131 | },
132 |
133 |
134 | setData: function (data) {
135 |
136 | if (data) {
137 | this.data = data;
138 |
139 | // Mix in data series
140 | var seriesDataRef;
141 | if (this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:barChart']) {
142 | seriesDataRef = this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:barChart']['#list'];
143 | }
144 | else if (this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:pieChart']) {
145 | seriesDataRef = this.chartSpec['c:chartSpace']['c:chart']['c:plotArea']['c:pieChart']['#list'];
146 | }
147 | else throw new Error("Can't add data to chart that is not initialized or not a recognized type")
148 |
149 | // the barChart/pieChart objects has other attributes too, so we push the series data on, not replace it
150 | if (chartInfo['data']) {
151 | var seriesData = this.getSeriesRef(chartInfo['data']);
152 | for (var i = 0; i < seriesData.length; i++) {
153 | seriesDataRef.push(seriesData[i])
154 | }
155 | }
156 | }
157 | return this;
158 | },
159 |
160 |
161 | ///
162 | /// @brief Transform an array of string into an office's compliance structure
163 | ///
164 | /// @param[in] region String
165 | /// The reference cell of the string, for example: $A$1
166 | /// @param[in] stringArr
167 | /// An array of string, for example: ['foo', 'bar']
168 | ///
169 | _strRef: function (region, stringArr) {
170 | var obj = {
171 | 'c:strRef': {
172 | 'c:f': region,
173 | 'c:strCache': function () {
174 | var result = {};
175 | result['c:ptCount'] = { '@val': stringArr.length };
176 | result['#list'] = [];
177 | for (var i = 0; i < stringArr.length; i++) {
178 | result['#list'].push({'c:pt': { '@idx': i, 'c:v': stringArr[i] }});
179 | }
180 | return result;
181 | }()
182 | }
183 | };
184 |
185 | return obj;
186 | },
187 |
188 | ///
189 | /// @brief Transform an array of numbers into an office's compliance structure
190 | ///
191 | /// @param[in] region String
192 | /// The reference cell of the string, for example: $A$1
193 | /// @param[in] numArr
194 | /// An array of numArr, for example: [4, 7, 8]
195 | /// @param[in] formatCode
196 | /// A string describe the number's format. Example: General
197 | ///
198 | _numRef: function (region, numArr, formatCode) {
199 | var obj = {
200 | 'c:numRef': {
201 | 'c:f': region,
202 | 'c:numCache': {
203 | 'c:formatCode': formatCode,
204 | 'c:ptCount': {'@val': '' + numArr.length},
205 | '#list': function () {
206 | result = [];
207 | for (var i = 0; i < numArr.length; i++) {
208 | result.push({'c:pt': { '@idx': i, 'c:v': numArr[i].toString() }});
209 | }
210 |
211 | return result;
212 | }
213 | }
214 | }
215 | };
216 |
217 | return obj;
218 | },
219 |
220 | _numFmt: function (formatCode) {
221 | return {
222 | "c:numFmt": {
223 | "@formatCode": formatCode ? formatCode : "General",
224 | "@sourceLinked": formatCode ? "0" : "1"
225 | }
226 | }
227 | },
228 |
229 | ///
230 | /// @brief Transform an array of string into an office's compliance structure
231 | ///
232 | /// @param[in] colorArr
233 | /// An array of colorArr, for example: ['ff0000', '00ff00', '0000ff']
234 | ///
235 | _colorRef: function (colorArr) {
236 | var arr = [];
237 | for (var i = 0; i < colorArr.length; i++) {
238 | arr.push({
239 | 'c:dPt': {
240 | 'c:idx': {'@val': i},
241 | 'c:bubble3D': {'@val': 0},
242 | 'c:spPr': {
243 | 'a:solidFill': {
244 | 'a:srgbClr': {'@val': colorArr[i].toString()}
245 | }
246 | }
247 | }
248 | });
249 | }
250 |
251 | return arr;
252 | },
253 |
254 | ///
255 | /// @brief Transform an array of string into an office's compliance structure
256 | ///
257 | /// @param[in] row int
258 | /// Row index.
259 | /// @param[in] col int
260 | /// Col index.
261 | /// @param[in] isRowAbsolute boolean
262 | /// Will add $ into cell's address if this parameter is true.
263 | /// @param[in] isColAbsolute boolean
264 | /// Will add $ into cell's address if this parameter is true.
265 | ///
266 | _rowColToSheetAddress: function (row, col, isRowAbsolute, isColAbsolute) {
267 | var address = "";
268 |
269 | if (isColAbsolute)
270 | address += '$';
271 |
272 | // these lines of code will transform the number 1-26 into A->Z
273 | // used in excel's cell's coordination
274 | while (col > 0) {
275 | var num = col % 26;
276 | col = (col - num ) / 26;
277 | address += String.fromCharCode(65 + num - 1);
278 | }
279 |
280 | if (isRowAbsolute)
281 | address += '$';
282 |
283 | address += row;
284 |
285 | return address;
286 | },
287 |
288 | /// @brief returns XML snippet for a chart dataseries
289 | _ser: function (serie, i) {
290 | var rc2a = this._rowColToSheetAddress; // shortcut
291 |
292 | var serieData = {
293 | 'c:ser': {
294 | 'c:idx': {'@val': i},
295 | 'c:order': {'@val': i},
296 | 'c:tx': this._strRef('Sheet1!' + rc2a(1, 2 + i, true, true), [serie.name]), // serie's value
297 | 'c:invertIfNegative': {'@val': '0'},
298 | 'c:cat': this._strRef('Sheet1!' + rc2a(2, 1, true, true) + ':' + rc2a(2 + serie.labels.length - 1, 1, true, true), serie.labels),
299 | 'c:val': this._numRef('Sheet1!' + rc2a(2, 2 + i, true, true) + ':' + rc2a(2 + serie.labels.length - 1, 2 + i, true, true), serie.values, "General")
300 | }
301 | };
302 |
303 | if (serie.color) {
304 | serieData['c:ser']['c:spPr'] = {
305 | 'a:solidFill': {
306 | 'a:srgbClr': {'@val': serie.color}
307 | }
308 | };
309 | }
310 | else if (serie.schemeColor) {
311 | serieData['c:ser']['c:spPr'] = {
312 | 'a:solidFill': {
313 | 'a:schemeClr': {'@val': serie.schemeColor}
314 | }
315 | };
316 | }
317 |
318 | if (serie.xml) {
319 | serieData['c:ser'] = _.merge(serieData['c:ser'], serie.xml)
320 | }
321 |
322 |
323 | // for pie charts
324 | if (serie.colors) {
325 | serieData['c:ser']['#list'] = this._colorRef(serie.colors);
326 | }
327 |
328 | return serieData;
329 | },
330 |
331 |
332 | /// @brief returns XML snippet for a chart dataseries
333 | getSeriesRef: function (data) {
334 | return data.map(this._ser, this);
335 | },
336 |
337 |
338 | /// @brief returns XML snippet for axis number format
339 | /// e.g. "$0" for US currency, "0%" for percentages
340 | xmlValAxisFormat: function (formatCode) {
341 | return {
342 | "c:chartSpace": {
343 | "c:chart": {
344 | "c:plotArea": {
345 | "c:valAx": {
346 | "c:majorGridlines": {},
347 | "c:numFmt": {
348 | "@formatCode": formatCode,
349 | "@sourceLinked": "0"
350 | }
351 | }
352 | }
353 | }
354 | }
355 | }
356 | },
357 |
358 | /// @brief returns XML snippet for an axis scale
359 | /// currently just min/max are supported
360 | _scaling: function (minValue, maxValue) {
361 | //
362 | var ref = {
363 | "c:scaling": {
364 | "c:orientation": {
365 | "@val": "minMax"
366 | }
367 | }
368 | };
369 | if (maxValue != undefined) {
370 | ref["c:scaling"]["c:max"] = {
371 | "@val": "" + (maxValue || "")
372 | };
373 | }
374 | if (minValue != undefined) {
375 | ref["c:scaling"]["c:max"] = {
376 | "@val": "" + (minValue || "")
377 | };
378 | }
379 | },
380 |
381 | _text: function(textSize) {
382 | return {
383 | "c:txPr": {
384 | "a:bodyPr": {},
385 | "a:listStyle": {},
386 | "a:p": {
387 | "a:pPr": {
388 | "a:defRPr": {
389 | "@sz": textSize
390 | }
391 | },
392 | "a:endParaRPr": {
393 | "@lang": "en-US"
394 | }
395 | }
396 | }
397 | };
398 | },
399 |
400 | /// @brief returns XML snippet for an axis title
401 | _title: function (title) {
402 | if (typeof title == 'object') return title; // assume it's an XML representations
403 | return {
404 | "c:title": {
405 | "c:tx": {
406 | "c:rich": {
407 | "a:bodyPr": {},
408 | "a:lstStyle": {},
409 | "a:p": {
410 | "a:pPr": {
411 | "a:defRPr": {}
412 | },
413 | "a:r": {
414 | "a:rPr": {
415 | "@lang": "en-US",
416 | "@dirty": "0",
417 | "@smtClean": "0"
418 | },
419 | "a:t": title
420 | },
421 | "a:endParaRPr": {
422 | "@lang": "en-US",
423 | "@dirty": "0"
424 | }
425 | }
426 | }
427 | },
428 | "c:layout": {},
429 | "c:overlay": {
430 | "@val": "0"
431 | }
432 | }
433 | }
434 | }
435 |
436 | }.initialize(chartInfo);
437 | }
438 |
439 | OfficeChart.getChartBase = function (chartInfo) {
440 |
441 | var chartBase;
442 |
443 | if (typeof chartInfo == 'string') {
444 | chartBase = _chartSpecs[chartInfo]();
445 | }
446 | else if (typeof chartInfo.renderType == 'string') {
447 | chartBase = _chartSpecs[chartInfo.renderType](chartInfo);
448 | }
449 | else if (chartInfo.xml) {
450 | chartBase = chartInfo.xml
451 | }
452 | else {
453 | throw new Error("invalid chart type")
454 | }
455 | // return deep copy so can create multiple charts from same base within one PowerPoint document
456 | return JSON.parse(JSON.stringify(chartBase));
457 | }
458 |
459 | module.exports = OfficeChart;
460 |
461 | /***********************************************************************************************************************
462 | // Column chart
463 | new OfficeChart({
464 | title: 'eSurvey chart',
465 | renderType: 'column',
466 | data: [ // array of series
467 | {
468 | name: 'Income',
469 | labels: ['2005', '2006', '2007', '2008', '2009'],
470 | values: [23.5, 26.2, 30.1, 29.5, 24.6],
471 | colors: ['ff0000', '00ff00', '0000ff', 'ffff00', '00ffff'] // optional
472 | },
473 | {
474 | name: 'Expense',
475 | labels: ['2005', '2006', '2007', '2008', '2009'],
476 | values: [18.1, 22.8, 23.9, 25.1, 25],
477 | colors: ['ff0000', '00ff00', '0000ff', 'ffff00', '00ffff'] // optional
478 | }
479 | ]
480 | });
481 |
482 |
483 | // Pie chart
484 | new OfficeChart({
485 | title: 'eSurvey chart',
486 | renderType: 'pie',
487 | data: [ // array of series
488 | {
489 | name: 'Income',
490 | labels: ['2005', '2006', '2007', '2008', '2009'],
491 | values: [23.5, 26.2, 30.1, 29.5, 24.6],
492 | colors: ['ff0000', '00ff00', '0000ff', 'ffff00', '00ffff'] // optional
493 | }
494 | ]
495 | });
496 |
497 |
498 | // Clustered bar chat
499 | new OfficeChart({
500 | title: 'eSurvey chart',
501 | renderType: 'group-bar',
502 | data: [ // array of series
503 | {
504 | name: 'Income',
505 | labels: ['2005', '2006', '2007', '2008', '2009'],
506 | values: [23.5, 26.2, 30.1, 29.5, 24.6],
507 | colors: ['ff0000', '00ff00', '0000ff', 'ffff00', '00ffff'] // optional
508 | }
509 | ]
510 | });
511 |
512 | ***********************************************************************************************************************/
--------------------------------------------------------------------------------
/lib/pptx.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | console.json = function(obj) { console.log(JSON.stringify(obj, null,4)); }
4 | module.exports = {
5 | Presentation: require('./presentation'),
6 | Slide: require('./slide'),
7 | Shape: require('./shape'),
8 | // spPr: require('./spPr'),
9 | emu: {
10 | inch: function(val) { return Math.floor(val * 914400); },
11 | point: function(val) { return Math.floor(val * 914400 / 72); },
12 | px: function(val) { return Math.floor(val * 914400 / 72); },
13 | cm: function(val) { return Math.floor(val * 360000); }
14 | }
15 | };
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/presentation.js:
--------------------------------------------------------------------------------
1 | var JSZip = require('jszip'); // this works on browser
2 | var async = require('async'); // this works on browser
3 | var xml2js = require('xml2js'); // this works on browser?
4 | var XmlNode = require('./xmlnode');
5 |
6 | var Slide = require('./slide');
7 |
8 | var Presentation = function (object) {
9 | this.content = {};
10 | };
11 |
12 | // fundamentally asynchronous because xml2js.parseString() is async
13 | Presentation.prototype.load = function (data, done) {
14 | var zip = new JSZip(data);
15 |
16 | var content = this.content;
17 | async.each(Object.keys(zip.files), function (key, callback) {
18 | var ext = key.substr(key.lastIndexOf('.'));
19 | if (ext == '.xml' || ext == '.rels') {
20 | var xml = zip.file(key).asText();
21 |
22 | xml2js.parseString(xml, function (err, js) {
23 | content[key] = js;
24 | callback(null);
25 | });
26 | }
27 | else {
28 | content[key] = zip.file(key).asText();
29 | callback(null);
30 | }
31 | }, done);
32 | };
33 |
34 | Presentation.prototype.toJSON = function () {
35 | return this.content;
36 | };
37 |
38 |
39 | Presentation.prototype.toBuffer = function () {
40 | var zip2 = new JSZip();
41 | var content = this.content;
42 | for (var key in content) {
43 | if (content.hasOwnProperty(key)) {
44 | var ext = key.substr(key.lastIndexOf('.'));
45 | if (ext == '.xml' || ext == '.rels') {
46 | var builder = new xml2js.Builder({renderOpts: {pretty: false}});
47 | var xml2 = (builder.buildObject(content[key]));
48 | zip2.file(key, xml2);
49 | }
50 | else {
51 | zip2.file(key, content[key]);
52 | }
53 | }
54 | }
55 | // zip2.file("docProps/app.xml", '172Microsoft Macintosh PowerPointOn-screen Show (4:3)123000falseTheme1Slide Titles3Office ThemeThis is the titleThis is the titleThis is the titleProven, Inc.falsefalsefalse14.0000');
56 | zip2.file("docProps/app.xml", '00Microsoft Macintosh PowerPointOn-screen Show (4:3)02000falseTheme1Slide Titles2Office ThemePowerPoint PresentationPowerPoint PresentationProven, Inc.falsefalsefalse14.0000')
57 | var buffer = zip2.generate({type: "nodebuffer"});
58 | return buffer;
59 | };
60 |
61 | Presentation.prototype.registerChart = function(chartName, content) {
62 | this.content['ppt/charts/' + chartName + '.xml'] = content;
63 |
64 | // '[Content_Types].xml' .. add references to the chart and the spreadsheet
65 | this.content["[Content_Types].xml"]["Types"]["Override"].push(XmlNode()
66 | .attr('PartName', "/ppt/charts/" + chartName + ".xml")
67 | .attr('ContentType', "application/vnd.openxmlformats-officedocument.drawingml.chart+xml")
68 | .el
69 | );
70 |
71 | var defaults = this.content["[Content_Types].xml"]["Types"]["Default"].filter(function (el) {
72 | return el['$']['Extension'] == 'xlsx'
73 | });
74 |
75 | if (defaults.length == 0) {
76 | this.content["[Content_Types].xml"]["Types"]["Default"].push(XmlNode()
77 | .attr('Extension', 'xlsx')
78 | .attr('ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
79 | .el
80 | );
81 | }
82 | }
83 |
84 | Presentation.prototype.registerChartWorkbook = function(chartName, workbookContent) {
85 |
86 | var numWorksheets = this.getWorksheetCount();
87 | var worksheetName = 'Microsoft_Excel_Sheet' + (numWorksheets + 1) + '.xlsx';
88 |
89 |
90 | this.content["ppt/embeddings/" + worksheetName] = workbookContent;
91 |
92 | // ppt/charts/_rels/chart1.xml.rels
93 | this.content["ppt/charts/_rels/" + chartName + ".xml.rels"] = XmlNode().setChild("Relationships", XmlNode()
94 | .attr({
95 | 'xmlns': "http://schemas.openxmlformats.org/package/2006/relationships"
96 | })
97 | .addChild('Relationship', XmlNode().attr({
98 | "Id": "rId1",
99 | "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package",
100 | "Target": "../embeddings/" + worksheetName
101 | }))
102 | ).el;
103 | }
104 |
105 | Presentation.prototype.getSlideCount = function () {
106 | return Object.keys(this.content).filter(function (key) {
107 | return key.substr(0, 16) === "ppt/slides/slide"
108 | }).length;
109 | }
110 |
111 | Presentation.prototype.getChartCount = function () {
112 | return Object.keys(this.content).filter(function (key) {
113 | return key.substr(0, 16) === "ppt/charts/chart"
114 | }).length;
115 | }
116 |
117 | Presentation.prototype.getWorksheetCount = function () {
118 | return Object.keys(this.content).filter(function (key) {
119 | return key.substr(0, 36) === "ppt/embeddings/Microsoft_Excel_Sheet"
120 | }).length;
121 | }
122 |
123 |
124 |
125 | Presentation.prototype.getSlide = function (name) {
126 | return new Slide({content: this.content['ppt/slides/' + name + '.xml'], presentation: this, name: name});
127 | }
128 |
129 | Presentation.prototype.addSlide = function (layoutName) {
130 | var slideName = "slide" + (this.getSlideCount() + 1);
131 |
132 | var layoutKey = "ppt/slideLayouts/" + layoutName + ".xml";
133 | var slideKey = "ppt/slides/" + slideName + ".xml";
134 | var relsKey = "ppt/slides/_rels/" + slideName + ".xml.rels";
135 |
136 |
137 | // create slide
138 | // var slideContent = this.content[layoutKey]["p:sldLayout"];
139 |
140 |
141 |
142 | //var sld = this.content["ppt/slides/slide1.xml"]; // this is cheating, copying an existing slide
143 | var sld = this.content[layoutKey]["p:sldLayout"];
144 | delete sld['$']["preserve"];
145 | delete sld['$']["type"];
146 |
147 | var slideContent = {
148 | "p:sld" : sld
149 | };
150 |
151 |
152 | slideContent = JSON.parse(JSON.stringify(slideContent));
153 |
154 | this.content[slideKey] = slideContent; //{ "p:sld": slideContent};
155 |
156 |
157 | var slide = new Slide({content: slideContent, presentation: this, name: slideName});
158 |
159 | // add to [Content Types].xml
160 | this.content["[Content_Types].xml"]["Types"]["Override"].push({
161 | "$": {
162 | "PartName": "/ppt/slides/" + slideName + ".xml",
163 | "ContentType": "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"
164 | }
165 | });
166 |
167 | //add it rels to slidelayout
168 | this.content[relsKey] = {
169 | "Relationships": {
170 | "$": {
171 | "xmlns": "http://schemas.openxmlformats.org/package/2006/relationships"
172 | },
173 | "Relationship": [
174 | {
175 | "$": {
176 | "Id": "rId1",
177 | "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout",
178 | "Target": "../slideLayouts/" + layoutName + ".xml"
179 | }
180 | }
181 | ]
182 | }
183 | };
184 |
185 | // add it to ppt/_rels/presentation.xml.rels
186 | var rId = "rId" + (this.content["ppt/_rels/presentation.xml.rels"]["Relationships"]["Relationship"].length + 1);
187 | this.content["ppt/_rels/presentation.xml.rels"]["Relationships"]["Relationship"].push({
188 | "$": {
189 | "Id": rId,
190 | "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
191 | "Target": "slides/" + slideName + ".xml"
192 | }
193 | });
194 |
195 | // add it to ppt/presentation.xml
196 | var maxId = 0;
197 | this.content["ppt/presentation.xml"]["p:presentation"]["p:sldIdLst"][0]["p:sldId"].forEach(function (ob) {
198 | if (+ob["$"]["id"] > maxId) maxId = +ob["$"]["id"]
199 | })
200 | this.content["ppt/presentation.xml"]["p:presentation"]["p:sldIdLst"][0]["p:sldId"].push({
201 | "$": {
202 | "id": "" + (+maxId + 1),
203 | "r:id": rId
204 | }
205 | });
206 |
207 | // increment slidecount
208 | var sldCount = this.getSlideCount();
209 | this.content["docProps/app.xml"]["Properties"]["Slides"][0] = sldCount ;
210 |
211 |
212 | return slide;
213 | }
214 |
215 | module.exports = Presentation;
--------------------------------------------------------------------------------
/lib/shape.js:
--------------------------------------------------------------------------------
1 | var XmlNode = require('./xmlnode');
2 |
3 | var shapeProperties = require('./shapeproperties');
4 | var defaults = require('./defaults');
5 | var clone = require('./util/clone')
6 |
7 | //======================================================================================================================
8 | // Shape (p:sp)
9 | //======================================================================================================================
10 |
11 | var Shape = function (content) {
12 | this.content = content || clone(defaults["p:sp"]);
13 | }
14 |
15 | Shape.prototype.text = function (text) {
16 | if (text) {
17 | this.content['p:txBody'][0]['a:p'][0]['a:r'][0]['a:t'][0] = text;
18 | return this;
19 | }
20 | else {
21 | return this.content['p:txBody'][0]['a:p'][0]['a:r'][0]['a:t'][0];
22 | }
23 | }
24 |
25 | Shape.prototype.shapeProperties = function () {
26 | return new shapeProperties(this.content["p:spPr"][0])
27 | }
28 |
29 | module.exports = Shape;
--------------------------------------------------------------------------------
/lib/shapeProperties.js:
--------------------------------------------------------------------------------
1 | var XmlNode = require('./xmlnode');
2 |
3 |
4 | //======================================================================================================================
5 | // spPr ShapeProperties
6 | //======================================================================================================================
7 |
8 | var shapeProperties = function (content) {
9 | this.content = content || this.getDefault();
10 | }
11 |
12 |
13 | shapeProperties.prototype.getDefault = function () {
14 |
15 | return XmlNode()
16 | .addChild("a:xfrm", XmlNode()
17 | .addChild("a:off", XmlNode().attr({
18 | "x": "6578600",
19 | "y": "787400"
20 | }))
21 | .addChild("a:ext", XmlNode().attr({
22 | "cx": "1181100",
23 | "cy": "1181100"
24 | })
25 | )
26 |
27 | )
28 | .addChld("a:prstGeom", XmlNode().attr({'prst': 'ellipse'}).addChild('a:avList', XmlNode()))
29 | ;
30 |
31 |
32 | return {
33 | "a:xfrm": [
34 | {
35 | "a:off": [
36 | {
37 | "$": {
38 | "x": "6578600",
39 | "y": "787400"
40 | }
41 | }
42 | ],
43 | "a:ext": [
44 | {
45 | "$": {
46 | "cx": "1181100",
47 | "cy": "1181100"
48 | }
49 | }
50 | ]
51 | }
52 | ],
53 | "a:prstGeom": [
54 | {
55 | "$": {
56 | "prst": "ellipse"
57 | },
58 | "a:avLst": [
59 | ""
60 | ]
61 | }
62 | ]
63 | };
64 | }
65 |
66 | shapeProperties.prototype.toJSON = function () {
67 | return {
68 | x: this.x(),
69 | y: this.y(),
70 | cx: this.cx(),
71 | cy: this.cy(),
72 | prstGeom: this.prstGeom()
73 | }
74 | }
75 |
76 | shapeProperties.prototype.x = function (val) {
77 | if (arguments.length == 0) return this.content["a:xfrm"][0]["a:off"][0]['$'].x;
78 | else this.content["a:xfrm"][0]["a:off"][0]['$'].x = val;
79 | return this;
80 | }
81 | shapeProperties.prototype.y = function (val) {
82 | if (arguments.length == 0) return this.content["a:xfrm"][0]["a:off"][0]['$'].y;
83 | else this.content["a:xfrm"][0]["a:off"][0]['$'].y = val;
84 | return this;
85 | }
86 | shapeProperties.prototype.cx = function (val) {
87 | if (arguments.length == 0) return this.content["a:xfrm"][0]["a:ext"][0]['$'].cx;
88 | else this.content["a:xfrm"][0]["a:ext"][0]['$'].cx = val;
89 | return this;
90 | }
91 | shapeProperties.prototype.cy = function (val) {
92 | if (arguments.length == 0) return this.content["a:xfrm"][0]["a:ext"][0]['$'].cy;
93 | else this.content["a:xfrm"][0]["a:ext"][0]['$'].cy = val;
94 | return this;
95 | }
96 |
97 | // see http://www.officeopenxml.com/drwSp-prstGeom.php
98 | shapeProperties.prototype.prstGeom = function (shape) {
99 | if (arguments.length == 0) return this.content["a:prstGeom"][0]["$"]["prst"];
100 | else this.content["a:prstGeom"][0]["$"]["prst"] = shape;
101 | return this;
102 | }
103 |
104 | module.exports = shapeProperties;
105 |
106 | //https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.presetgeometry(v=office.14).aspx
--------------------------------------------------------------------------------
/lib/slide.js:
--------------------------------------------------------------------------------
1 | var Shape = require('./shape');
2 | var Chart = require('./chart')
3 |
4 |
5 | //======================================================================================================================
6 | // Slide
7 | //======================================================================================================================
8 |
9 | var Slide = function (args) {
10 | this.content = args.content;
11 | this.presentation = args.presentation;
12 | this.name = args.name;
13 |
14 | // TODO: Validate arguments
15 | };
16 |
17 | Slide.prototype.getShapes = function () {
18 |
19 | // TODO break out getShapeTree
20 | return this.content["p:sld"]["p:cSld"][0]["p:spTree"][0]['p:sp'].map(function (sp) {
21 | return new Shape(sp);
22 | });
23 | };
24 |
25 | Slide.prototype.addChart = function (data, done) {
26 | var self = this;
27 | var chartName = "chart"+(this.presentation.getChartCount() + 1);
28 | var chart = new Chart({slide: this, presentation: this.presentation, name: chartName});
29 | var slideName = this.name;
30 |
31 | chart.load(data, function(err, data) { // TODO pass it real data
32 | self.content["p:sld"]["p:cSld"][0]["p:spTree"][0]["p:graphicFrame"] = chart.content; //jsChartFrame["p:graphicFrame"];
33 |
34 | // Add entry to slide1.xml.rels
35 | // There should a slide-level and/or presentation-level method to add/track rels
36 | var rels = self.presentation.content['ppt/slides/_rels/' + slideName + '.xml.rels'];
37 | var numRels = rels["Relationships"]["Relationship"].length;
38 | var rId = "rId"+(numRels + 1);
39 | var numRels = rels["Relationships"]["Relationship"].push({
40 | "$": {
41 | "Id": rId ,
42 | "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
43 | "Target": "../charts/" + chartName + ".xml"
44 | }
45 | });
46 | done(null, self);
47 | });
48 | };
49 |
50 | Slide.prototype.addShape = function () {
51 | var shape = new Shape();
52 | this.content["p:sld"]["p:cSld"][0]["p:spTree"][0]['p:sp'].push(shape.content);
53 | return shape;
54 | };
55 |
56 | module.exports = Slide;
--------------------------------------------------------------------------------
/lib/util/clone.js:
--------------------------------------------------------------------------------
1 | module.exports = function clone(obj) { return JSON.parse(JSON.stringify(obj))}
--------------------------------------------------------------------------------
/lib/xmlnode.js:
--------------------------------------------------------------------------------
1 | // Super light implementation of an XML DOM
2 | // Right now this keeps only the right hand side, not the left, so it doesn't keep its own tag
3 | // The tag is defined implicitly by its hash key in th
4 |
5 |
6 |
7 | var XmlNode = function (options) {
8 |
9 | if (this instanceof XmlNode) {
10 | this.el = {};
11 | if (typeof options == 'string') this.tagName = options;
12 | else if (options) this.tagName = options.tagName;
13 | }
14 | else return new XmlNode(options);
15 | }
16 |
17 | module.exports = XmlNode;
18 |
19 | XmlNode.prototype.toJSON = function () {
20 | return this.el;
21 | }
22 |
23 | // pass in a single key, value pair, e.g. XmlNode().attr('color', 'red') to set one attribute
24 | // or pass in an associative array, e.g. XmlNode().attr({color: 'red') to set multiple attributes
25 | // or pass in just a key to get the attribute value
26 | XmlNode.prototype.attr = function (key, val) {
27 | if (typeof val == 'number') val = ''+val;
28 | if (typeof key === 'undefined') {
29 | // return all attributes, if any are defined
30 | return this.el['$'];
31 | }
32 | else if (arguments.length == 1 && (typeof key == 'object')) {
33 | // assign attributes
34 | this.el['$'] = this.el['$'] || {};
35 | for (var attrName in key) {
36 | if (key.hasOwnProperty(attrName)) {
37 | this.el['$'][attrName] = key[attrName];
38 | }
39 | }
40 | return this;
41 | }
42 | else if (typeof val === 'undefined') {
43 | // get the attribute value
44 | return this.el['$'] ? this.el['$'][key] : undefined;
45 |
46 | }
47 | else {
48 | this.el['$'] = this.el['$'] || {};
49 | this.el['$'][key] = val;
50 | return this;
51 | }
52 | }
53 |
54 | XmlNode.prototype.addChild = function (tag, node) {
55 | // if (typeof node == 'string') this.el[tag] = node;
56 | // else {
57 | this.el[tag] = this.el[tag] || [];
58 | this.el[tag].push((node instanceof XmlNode) ? node.el : node);
59 | // }
60 | return this;
61 |
62 | }
63 |
64 |
65 | XmlNode.prototype.setChild = function (tag, node) {
66 | this.el[tag] = (node instanceof XmlNode) ? node.el : node;
67 | return this;
68 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-pptx",
3 | "version": "0.1.1",
4 | "description": "Pure Javascript reader/writer for PowerPoint .pptx files",
5 | "main": "lib/pptx.js",
6 | "scripts": {
7 | "test": "mocha"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/protobi/js-pptx.git"
12 | },
13 | "author": "Pieter Sheth-Voss",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/protobi/js-pptx/issues"
17 | },
18 | "homepage": "https://github.com/protobi/js-pptx",
19 | "dependencies": {
20 | "async": "^1.4.0",
21 | "jszip": "^2.5.0",
22 | "lodash": "^3.10.0",
23 | "msexcel-builder": "0.0.2",
24 | "query": "git://github.com/protobi/query.git",
25 | "xml2js": "^0.4.9",
26 | "xmlbuilder": "^2.6.4"
27 | },
28 | "devDependencies": {},
29 | "directories": {
30 | "test": "test"
31 | },
32 | "keywords": [
33 | "PowerPoint",
34 | "parser/generator"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/test/chart.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require('assert');
4 | var fs = require("fs");
5 | var PPTX = require('..');
6 | var xml2js = require('xml2js');
7 | var xmlbuilder = require('xmlbuilder')
8 |
9 | var INFILE = './lab/chart-null/chart-null.pptx';
10 | var OUTFILE = '/tmp/chart.pptx';
11 |
12 | describe('PPTX', function () {
13 |
14 | it('can read, modify, write and read', function (done) {
15 | fs.readFile(INFILE, function (err, data) {
16 | if (err) throw err;
17 | var pptx = new PPTX.Presentation();
18 |
19 |
20 | pptx.load(data, function (err) {
21 |
22 | var slide1 = pptx.getSlide('slide1');
23 | var slide2 = pptx.addSlide("slideLayout6");
24 | slide1.addChart(barChart, function (err, chart) {
25 | if (err) throw(err);
26 |
27 | slide2.addChart(barChart2, function (err, chart) {
28 | if (err) throw(err);
29 |
30 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
31 | if (err) throw err;
32 | console.log("open " + OUTFILE)
33 | done();
34 | });
35 | });
36 | });
37 | });
38 | });
39 | });
40 | });
41 |
42 | //
43 | //Series 1 Series 2 Series 3
44 | //Category 1 4.3 2.4 2
45 | //Category 2 2.5 4.4 2
46 | //Category 3 3.5 1.8 3
47 | //Category 4 4.5 2.8 5
48 |
49 | var barChart = {
50 | title: 'Sample bar chart',
51 | renderType: 'bar',
52 | data: [
53 | {
54 | name: 'Series 1',
55 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
56 | values: [4.3, 2.5, 3.5, 4.5]
57 | },
58 | {
59 | name: 'Series 2',
60 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
61 | values: [2.4, 4.4, 1.8, 2.8]
62 | },
63 | {
64 | name: 'Series 3',
65 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
66 | values: [2.0, 2.0, 3.0, 5.0]
67 | }
68 | ]
69 | }
70 |
71 | var barChart2 = {
72 | title: 'Sample bar chart',
73 | renderType: 'bar',
74 | xmlOptions: {
75 | "c:title": {
76 | "c:tx": {
77 | "c:rich": {
78 | "a:p": {
79 | "a:r": {
80 | "a:t": "Override title via XML"
81 | }
82 | }
83 | }
84 | }
85 | }
86 | },
87 | data: [
88 | {
89 | name: 'europe',
90 | labels: ['Y2003', 'Y2004', 'Y2005'],
91 | values: [2.5, 2.6, 2.8],
92 | color: 'ff0000'
93 | },
94 | {
95 | name: 'namerica',
96 | labels: ['Y2003', 'Y2004', 'Y2005'],
97 | values: [2.5, 2.7, 2.9],
98 | color: '00ff00'
99 | },
100 | {
101 | name: 'asia',
102 | labels: ['Y2003', 'Y2004', 'Y2005'],
103 | values: [2.1, 2.2, 2.4],
104 | color: '0000ff'
105 | },
106 | {
107 | name: 'lamerica',
108 | labels: ['Y2003', 'Y2004', 'Y2005'],
109 | values: [0.3, 0.3, 0.3],
110 | color: 'ffff00'
111 | },
112 | {
113 | name: 'meast',
114 | labels: ['Y2003', 'Y2004', 'Y2005'],
115 | values: [0.2, 0.3, 0.3],
116 | color: 'ff00ff'
117 | },
118 | {
119 | name: 'africa',
120 | labels: ['Y2003', 'Y2004', 'Y2005'],
121 | values: [0.1, 0.1, 0.1],
122 | color: '00ffff'
123 | }
124 |
125 | ]
126 | };
--------------------------------------------------------------------------------
/test/example.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require('assert');
4 | var fs = require("fs");
5 | var PPTX = require('..');
6 | var xml2js = require('xml2js');
7 | var xmlbuilder = require('xmlbuilder')
8 |
9 |
10 | var INFILE = './test/files/parts3.pptx';
11 | var OUTFILE = './test/files/parts3-a.pptx';
12 |
13 | describe('PPTX', function () {
14 |
15 | it('can read, modify, write and read', function (done) {
16 | fs.readFile(INFILE, function (err, data) {
17 | if (err) throw err;
18 | var pptx = new PPTX.Presentation();
19 | pptx.load(data, function (err) {
20 |
21 | var slide3 = pptx.addSlide("slideLayout3");
22 | var slide4 = pptx.addSlide("slideLayout2");
23 |
24 | var slide1 = pptx.getSlide('slide1');
25 | var shapes = slide1.getShapes();
26 |
27 | shapes[3]
28 | .text("Now it's a trapezoid")
29 | .shapeProperties()
30 | .x(PPTX.emu.inch(1))
31 | .y(PPTX.emu.inch(1))
32 | .cx(PPTX.emu.inch(2))
33 | .cy(PPTX.emu.inch(0.75))
34 | .prstGeom('trapezoid');
35 |
36 | var triangle = slide1.addShape()
37 | .text("Triangle")
38 | .shapeProperties()
39 | .x(PPTX.emu.inch(2))
40 | .y(PPTX.emu.inch(2))
41 | .cx(PPTX.emu.inch(2))
42 | .cy(PPTX.emu.inch(2))
43 | .prstGeom('triangle');
44 |
45 | for (var i= 0; i<20; i++) {
46 | slide3.addShape()
47 | .text(""+i)
48 | .shapeProperties()
49 | .x(PPTX.emu.inch((Math.random()*10)))
50 | .y(PPTX.emu.inch((Math.random()*6)))
51 | .cx(PPTX.emu.inch(1))
52 | .cy(PPTX.emu.inch(1))
53 | .prstGeom('ellipse');
54 | }
55 |
56 | var chart = slide1.addChart(barChart, function (err, chart) {
57 |
58 |
59 | fs.writeFile(OUTFILE, pptx.toBuffer(), function (err) {
60 | if (err) throw err;
61 |
62 | fs.readFile(OUTFILE, function (err, data) {
63 | var check = new PPTX.Presentation();
64 | check.load(data, function (err) {
65 |
66 | var props = check.getSlide('slide1').getShapes()[3].shapeProperties().toJSON();
67 | assert.deepEqual(props, { x: '914400',
68 | y: '914400',
69 | cx: '1828800',
70 | cy: '685800',
71 | prstGeom: 'trapezoid' });
72 | });
73 | console.log("open "+OUTFILE)
74 | done();
75 | });
76 | });
77 | });
78 |
79 | });
80 | });
81 | });
82 | });
83 |
84 | var barChart = {
85 | title: 'Sample bar chart',
86 | renderType: 'bar',
87 | data: [
88 | {
89 | name: 'Series 1',
90 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
91 | values: [4.3, 2.5, 3.5, 4.5]
92 | },
93 | {
94 | name: 'Series 2',
95 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
96 | values: [2.4, 4.4, 1.8, 2.8]
97 | },
98 | {
99 | name: 'Series 3',
100 | labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
101 | values: [2.0, 2.0, 3.0, 5.0]
102 | }
103 | ]
104 | }
--------------------------------------------------------------------------------
/test/files/minimal.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/test/files/minimal.pptx
--------------------------------------------------------------------------------
/test/files/parts3-a.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/test/files/parts3-a.pptx
--------------------------------------------------------------------------------
/test/files/parts3-b.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/test/files/parts3-b.pptx
--------------------------------------------------------------------------------
/test/files/parts3.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/won21kr/js-pptx/7aed5fb3cc88502a58993603936d71eea603bbfc/test/files/parts3.pptx
--------------------------------------------------------------------------------
/test/synthesize.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require('assert');
4 | var fs = require("fs");
5 | var JSZip = require('jszip')
6 |
7 | var INFILE = './test/files/minimal.pptx';
8 | var STORE = './test/files/minimal.json';
9 | var OUTFILE = './test/files/minimal-copy.pptx';
10 |
11 | var zip1 = new JSZip(fs.readFileSync(INFILE));
12 | var copy = {};
13 |
14 | Object.keys(zip1.files).forEach(function (key) {
15 | copy[key] = zip1.file(key).asText();
16 | });
17 |
18 | fs.writeFileSync(STORE, JSON.stringify(copy, null,4))
19 |
20 | var json = fs.readFileSync(STORE, 'utf8');
21 | var obj = JSON.parse(json);
22 |
23 | var zip2 = new JSZip();
24 | for (var key in obj) {
25 | zip2.file(key, obj[key]);
26 | }
27 |
28 | var buffer = zip2.generate({type:"nodebuffer", compression: 'DEFLATE'});
29 |
30 | fs.writeFile(OUTFILE, buffer, function(err) {
31 | if (err) throw err;
32 | });
33 |
34 | var zip3 = new JSZip();
35 | zip3.file('json', JSON.stringify(copy, null,4))
36 | var buffer3 = zip3.generate({type:"nodebuffer", compression: 'DEFLATE'});
37 |
38 | fs.writeFile('./test/files/minimal.json.jar', buffer3, function(err) {
39 | if (err) throw err;
40 | });
--------------------------------------------------------------------------------
/test/xmlnode.js:
--------------------------------------------------------------------------------
1 | var XmlNode = require('../lib/xmlnode')
2 | var assert = require('assert');
3 |
4 | describe("XmlNode", function () {
5 | it('constructs with new', function () {
6 | var node = new XmlNode();
7 | node.attr('color', '#39C');
8 | assert.equal(node.attr('color'), '#39C');
9 | assert.deepEqual(node.el, { $: { color: "#39C"}})
10 | });
11 |
12 | it('constructs as function', function () {
13 | var node = XmlNode().attr('color', '#39C')
14 | assert.equal(node.attr('color'), '#39C');
15 | assert.deepEqual(node.el, { $: { color: "#39C"}})
16 | });
17 |
18 | it('adds a child', function () {
19 | var node = XmlNode().attr("color", "#39C").addChild("p:sld", XmlNode().attr("color", "#9B6"));
20 | assert.deepEqual(node.el, { $: { color: "#39C"}, "p:sld": [
21 | { $: { color: "#9B6"}}
22 | ]})
23 |
24 | });
25 | it('sets a child', function () {
26 | var node = XmlNode().attr("color", "#39C").setChild("p:sld", XmlNode().attr("color", "#9B6"));
27 | assert.deepEqual(node.el, { $: { color: "#39C"}, "p:sld": { $: { color: "#9B6"}}})
28 |
29 | });
30 | // it('takes an element in its constructor', function () {
31 | // var node = XmlNode({ $: { color: "#39C"}, "p:sld": { $: { color: "#9B6"}}});
32 | // assert.deepEqual(node.el, { $: { color: "#39C"}, "p:sld": { $: { color: "#9B6"}}})
33 | // });
34 | // it('exposes toJSON() as a public method', function () {
35 | // var node = XmlNode({ $: { color: "#39C"}, "p:sld": { $: { color: "#9B6"}}});
36 | // assert.deepEqual(node.toJSON(), { $: { color: "#39C"}, "p:sld": { $: { color: "#9B6"}}})
37 | // });
38 | it('generates a spPr object', function () {
39 | var node = XmlNode()
40 | .addChild("a:xfrm", XmlNode()
41 | .addChild("a:off", XmlNode().attr({
42 | "x": "6578600",
43 | "y": "787400"
44 | }))
45 | .addChild("a:ext", XmlNode().attr({
46 | "cx": "1181100",
47 | "cy": "1181100"
48 | })
49 | )
50 |
51 | )
52 | .addChild("a:prstGeom", XmlNode().attr({'prst': 'ellipse'}).addChild('a:avLst', XmlNode()))
53 | ;
54 |
55 | var expected = {
56 | "a:xfrm": [
57 | {
58 | "a:off": [
59 | {
60 | "$": {
61 | "x": "6578600",
62 | "y": "787400"
63 | }
64 | }
65 | ],
66 | "a:ext": [
67 | {
68 | "$": {
69 | "cx": "1181100",
70 | "cy": "1181100"
71 | }
72 | }
73 | ]
74 | }
75 | ],
76 | "a:prstGeom": [
77 | {
78 | "$": {
79 | "prst": "ellipse"
80 | },
81 | "a:avLst": [
82 | {}
83 | ]
84 | }
85 | ]
86 | }
87 |
88 | assert.deepEqual(node.toJSON(), expected)
89 | })
90 | });
--------------------------------------------------------------------------------
/xml2js.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var xml2js = require('xml2js')
3 | var arguments = process.argv.slice(2);
4 | var INFILE = arguments[0];
5 | var OUTFILE = arguments[1];
6 |
7 | fs.readFile(INFILE, 'utf8', function(err, xml) {
8 | if (err) throw(err);
9 | xml2js.parseString(xml, function(err, js) {
10 | if (err) throw(err);
11 | if (OUTFILE) {
12 | var txt = "module.exports = " + JSON.stringify(js,null,4);
13 | fs.writeFile(OUTFILE, txt, 'utf8', function(err) {
14 | if (err) throw(err);
15 | console.log("File written to "+OUTFILE);
16 | } )
17 | }
18 | else console.log(JSON.stringify(js,null,4));
19 | })
20 |
21 | });
22 |
--------------------------------------------------------------------------------