├── .gitignore
├── LICENSE
├── README.md
├── doc
├── mynote.md
├── zh_cn.md
├── zh_cn.md.orig.2020-04-25_200527
└── zh_cn.md.toc.2020-04-25_200527
├── examples
├── all.tex
├── main.tex
├── setting.tex
└── simple.tex
├── excel2tex.py
├── img
├── alipay.jpg
├── all.png
├── excel_table.png
├── simple.png
└── wechat.png
└── src
├── Cell.py
├── MergedCell.py
├── Prop.py
├── Table.py
├── __init__.py
├── help.py
├── line.py
├── output.py
└── setting.py
/.gitignore:
--------------------------------------------------------------------------------
1 | /*.tex
2 | *.toc*
3 | *.orig*
4 | *.xlsx
5 | *.xls
6 | *.ipynb
7 | *.spec
8 | __pycache__
9 | build
10 | dist
11 | output
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Convert excel table to latex table
2 |
3 | A powerful tool that converts excel table to latex table in human-readable format.
4 |
5 | Documentation for simplified Chinese: [简体中文](../master/doc/zh_cn.md)
6 |
7 |
8 | * [Convert excel table to latex table](#convert-excel-table-to-latex-table)
9 | * [Supported excel's styles](#supported-excels-styles)
10 | * [Before conversion](#before-conversion)
11 | * [Requirements](#requirements)
12 | * [Usage](#usage)
13 | * [Simple usage](#simple-usage)
14 | * [Enable all formats](#enable-all-formats)
15 | * [Trouble shooting](#trouble-shooting)
16 | * [Mess code](#mess-code)
17 | * [Support](#support)
18 | * [Buy me a coffee](#buy-me-a-coffee)
19 |
20 |
21 |
22 |
23 |
24 | ## Supported excel's styles
25 |
26 | - horizontal line
27 | - none
28 | - solid
29 | - dash (coming soon)
30 | - double line (coming soon)
31 | - vertical line
32 | - none
33 | - solid
34 | - double line (coming soon)
35 | - colored line
36 | - colored cell
37 | - colored text
38 | - horizontal alignment
39 | - text shape
40 | - italic
41 | - bold
42 |
43 | ## Before conversion
44 |
45 | - Don't use **`theme colors`** that excel show in the panel, otherwise your color will not be recognized. `standard colors` or `more colors` will be ok.
46 | - Font style is specified for the whole text, not a single character, i.e., if you have two characters one of which is bold and another is italic, then they will be both bold and italic.
47 | - The height of every merged cell must not be less than the number of lines in your text.
48 |
49 | ## Requirements
50 |
51 | If you are not using a `-e` option, please add the following required packages to your preamble:
52 | ```tex
53 | \usepackage{multirow, makecell}
54 | ```
55 |
56 | Following python package is required:
57 | ```shell
58 | pip install openpyxl
59 | ```
60 |
61 | ## Usage
62 |
63 | ```text
64 | usage: excel2tex.py [-h] [-s SOURCE] [-o TARGET] [--setting SETTING] [--sig] [-m] [-e]
65 |
66 | optional arguments:
67 | -h, --help show this help message and exit
68 | -s SOURCE source file (default: table.xlsx)
69 | -o TARGET target file (default: table.tex)
70 | --setting SETTING setting file (default: setting.tex)
71 | --sig set file encoding to utf-8-sig, only use when there is mess code.
72 | -m, --math enabel inline math
73 | -e, --excel-format enabel all formats
74 | ```
75 |
76 | If you are using windows and have no python installed, an executable file is provided [here](https://github.com/ZhiyuanLck/excel2tex/releases/tag/v1.1)
77 |
78 | ### Simple usage
79 |
80 | We have the following excel table to be converted to latex table.
81 |
82 | 
83 |
84 | `python excel2tex.py` is the simplest method to do this, which means converting an excel file of name `table.xlsx` to a tex file of name `table.tex`. And because you are not using the `-e` option, the table is resolved in the simplest way:
85 | - All lines are drawn
86 | - No element will be colored
87 | - Text are all centered
88 | - All characters are converted to what they have been.
89 |
90 | So this is the converted table drawn in latex. Generated code is in [`simple.tex`](../master/examples/simple.tex)
91 |
92 | 
93 |
94 | ### Enable all formats
95 |
96 | If you want more styles to be resolved, try to use the `-e` option. The following command will convert the table with most of the styles you have set in excel. And a setting file of name `setting.tex` by default (change it by `--setting mysetting.tex`). Please input the setting file in your preamble. The `-m` option ensure that the character `$` is not escaped so that you can enter the math mode in excel just as in latex.
97 |
98 | ```shell
99 | python excel2tex.py -e -m
100 | ```
101 |
102 | Here is the result. The generated code is in [`all.tex`](../master/examples/all.tex)
103 |
104 | 
105 |
106 | ## Trouble shooting
107 |
108 | ### Mess code
109 |
110 | Try to set encoding to `utf-8-sig`, for example
111 |
112 | ```shell
113 | python excel2tex.py -s table.xlsx -o table.tex --sig
114 | ```
115 |
116 | ## Support
117 | If you have some emergency trouble with this tool, send me your code to my email: lichangkai225@qq.com
118 |
119 | ## Buy me a coffee
120 |
121 | Does this tool help you? You can buy me coffee!
122 |
123 |
124 | | wechat | alipay |
125 | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
126 | |
|
|
127 |
--------------------------------------------------------------------------------
/doc/mynote.md:
--------------------------------------------------------------------------------
1 | ## 要实现的功能
2 |
3 | ### 基础功能
4 |
5 | 文本
6 |
7 | - 颜色
8 | - 斜体、粗体
9 | - 对齐
10 |
11 | 单元格
12 |
13 | - 颜色
14 |
15 | 框线
16 |
17 | - 横线
18 | - 宽度
19 | - 颜色
20 | - 样式
21 | - 竖线
22 | - 宽度
23 | - 颜色
24 | - 样式
25 |
26 | ### 组合
27 | 合并单元格
28 |
29 | ### 绘制过程
30 | 1. 每一行hhline
31 | - 基础属性设置
32 | 2. 每一行单元格
33 |
34 | ### 单个单元格语法
35 |
36 | - 每个单元格写成`\muticolumn`形式
37 | - 所有右侧竖线样式由`\muticolumn`控制
38 | - 每行第一个单元格控制左侧竖线样式
39 | - 单元格颜色在`\multicolumn`中设置而不是在`\multirowcell`中
40 |
41 | ```tex
42 | \multicolumn{}{ }{ }
43 | ```
44 |
45 | ### 合并单元格语法
46 |
47 | - 合并单元格布局由合并单元格左下角单元格控制
48 | - `\multirowcell`中的对齐与`\multicolumn`中的对齐保持一致,即文本对齐方式
49 | - 除去左下角单元格,其他单元格文本为空,单元格颜色一致
50 |
51 | 右侧竖线跳过的情形:
52 | - block列数大于1且不是一行最后一个单元格
53 |
54 | ```tex
55 | % 内容
56 | \multirowcell{]{}
57 | ```
58 |
59 | ### 横线语法
60 |
61 | - 所有下侧横线样式在当前行换行符`\\`后由`\hhline`控制
62 | - 第一行单元格上侧的横线单独设置
63 |
64 | 下侧横线填充颜色
65 |
66 | 1. 无(无横线且无填充颜色)
67 | 2. 虚线(线宽最大 或 单元格无填充颜色)
68 | 3. 实线(线宽最大 或 单元格无填充颜色)
69 | 4. 实线填充(单元与下面的单元格属于一个合并单元,且有填充颜色)
70 | 5. 虚线+实线填充(线宽不是最大,且有填充颜色)
71 | 6. 实线+实线填充(线宽不是最大,且有填充颜色)
72 |
73 | 实际可以归纳为填充两个pattern,都用`\xleaders`实现,top pattern一定是实线,分别判断两个pattern是否需要填充(高度0pt)即可
74 |
75 | ```tex
76 | %% horizontal line
77 | % colored solid line pattern
78 | % #1 color #2 width #3 height
79 | \newcommand{\hsp}[3]{\hbox{\textcolor{#1}{\rule{#2}{#3}}}}
80 | % colored dash line pattern
81 | % #1 color #2 width #3 height #4 style
82 | \newcommand{\hdp}[4]{\hbox{\textcolor{#1}{\hdashrule{#2}{#3}{#4}}}}
83 | % fill line
84 | % #1 top fill #2 bottom fill
85 | \newcommand{\leaderfill}[1]{%
86 | \xleaders\hbox{%
87 | \vbox{\baselineskip=0pt\lineskip=0pt#1}%
88 | }\hfill%
89 | }
90 | % top: solid, bottom: solid
91 | % #1 top color #2 top height #3 bottom color#4 bottom height
92 | \newcommand{\ssfill}[4]{%
93 | \leaderfill{\hsp{#1}{0.01pt}{#2}\hsp{#3}{0.01pt}{#4}}%
94 | }
95 | % top: solid, bottom: dashed
96 | % #1 top color, #2 top height, #3 common width to expand
97 | % #4 bottom color #5 bottom height, #6 bottom dash line style
98 | \newcommand{\sdfill}[6]{%
99 | \leaderfill{\hsp{#1}{#3}{#2}\hdp{#4}{#3}{#5}{#6}}%
100 | }
101 | % single solid
102 | % #1 color #2 height
103 | \newcommand{\sfill}[2]{%
104 | \leaderfill{\hsp{#1}{0.01pt}{#2}}%
105 | }
106 | % single dash
107 | % #1 color #2 width #3 height #4 style
108 | \newcommand{\dfill}[4]{%
109 | \leaderfill{\hdp{#1}{#2}{#3}{#4}}%
110 | }
111 | ```
112 |
113 | 采取竖线覆盖横线的方式(第一行和最后一行除外),`hhline`中对`|`需要有`\beforevline`设置,最后需要有`\aftervline`设置
114 |
115 | ```tex
116 | \hhline{
117 |
118 |
119 | …
120 |
121 | }
122 | ```
123 |
124 | ``
125 |
126 | ```tex
127 | >{}
128 | !{}
129 | ```
130 |
131 | 相关宏命令
132 | ```tex
133 | %% vline settings
134 | % #1 rule width #2 color
135 | \newcommand{\beforevline}[2]{%
136 | \global\setlength\arrayrulewidth{#1}\arrayrulecolor{#2}%
137 | }
138 | \newcommand{\aftervline}{%
139 | \global\setlength\arrayrulewidth{0.4pt}\arrayrulecolor{black}%
140 | }
141 | ```
142 |
143 | # 步骤
144 |
145 | 1. 初始化单元格矩阵: 确定idx, text属性
146 | 2. 移除空行、空列: 得到有效的范围bounds
147 | 3. 重新扫描单元格矩阵,确定其他属性
148 | 4. 转换
149 | - 单元格
150 | - 边框
151 |
152 | # 属性列表
153 |
154 | - head 属性head
155 | - cell 真实的cell
156 | - coor
157 | - merged_idx
158 | - 对齐
159 | - 类型
160 | - 高
161 | - 宽
162 | - 颜色
163 | - 行首
164 | - 行尾
165 | - first_row
166 | - last_row
167 | - firt_col
168 | - last_col
169 | - 文本
170 | - 内容
171 | - 斜体
172 | - 粗体
173 | - 颜色
174 | - 边框
175 | - 四条边框
176 |
177 | # 表格转换
178 |
179 | - 矩阵每一列分成了若干等宽的长方形或正方行
180 | - 扫描每一行,如果是正方形输出1, 如果是长方形
181 | - 如果是长方形的开头,输出2
182 | - 否则输出0
183 |
184 | 单元格类型
185 |
186 | - plain
187 | - multirowcell_begin
188 | - height
189 | - multirowcell_other
190 | - multicolumn_begin
191 | - width
192 | - multicolumn_other: skip
193 | - block_firstline_begin
194 | - height
195 | - width
196 | - block_firstline_other: skip
197 | - block_placeholder_begin
198 | - block_placeholder_end
199 | - block_placeholder_other
200 |
201 | 扫描每一个单元格
202 |
203 | 1. plain cell
204 | 2. multirowcell
205 | 3. multicolumn
206 | 4. block
207 | a. 第一行
208 | - 第一个插入语句
209 | - 其他的跳过
210 | b. 其他行
211 | - 最后一个multicolumn加|
212 | - 其他不加
213 |
214 |
215 |
216 | ## cline处理
217 |
218 | ### 属性设置
219 |
220 | 1. 线段类型
221 | - is_none
222 | - is_dash
223 | 2. 宽度
224 | - thin
225 | - medium
226 | - thick
227 | 3. 颜色
228 | - 有颜色
229 | - 无颜色
230 | 4. dash line gap
231 |
232 | ### 设置cline
233 | - 检查每行是否一致
234 |
235 | ### 通过cline移除空行
236 |
237 | cline_x_min
238 | hcell_x_min
239 | self.x1
240 |
241 | cline_x_max
242 | hcell_x_max
243 | self.x2
244 |
245 | ccell_y_min
246 | hline_y_min
247 | self.y1
248 |
249 | ccell_y_max
250 | hline_y_max
251 | self.y2
252 |
253 | ### 分类
254 |
255 | 开始分类的条件:
256 |
257 | - 行第一个非空style
258 | - cur不是None且cur != pre
259 |
260 | 继续分类的条件:
261 |
262 | - cur不是None且cur = pre
263 |
264 | 结束分类的条件:
265 | - cur != pre
266 | - 倒数第一个非空style
267 |
268 | ### cline实现宏包
269 | - 实线使用`booktabs`包,统一用`\cmidrule`实现不同线宽的cline。由于会使用竖线,将会导致产生顶端间距的sep设置为0,且在顶层减去每条cline的线宽
270 | ```tex
271 | \setlength\abovetopsep{0pt}
272 | \setlength\belowbottomsep{0pt}
273 | \setlength\aboverulesep{0pt}
274 | \setlength\belowrulesep{0pt}
275 | ```
276 | - 颜色使用`colortbl`,定义宏命令
277 | ```tex
278 | \newcommand\colorwrap[2]{
279 | \arrayrulecolor{#1}#2
280 | \arrayrulecolor{black}
281 | }
282 |
283 | # todo
284 | - 检查异常的border
285 | - text颜色放到>{}中
286 | - vhline使用下面的竖线填充
287 | - 没有样式的横线,填充下方单元格的颜色
288 |
289 | ### note
290 | math宽度不要太高
291 | 已经知道的不兼容的包:`arydshln`, `stackengine`
292 |
--------------------------------------------------------------------------------
/doc/zh_cn.md:
--------------------------------------------------------------------------------
1 | # excel表格转换工具
2 |
3 | 将excel中的表格转换为latex中的表格,支持识别多种格式。
4 |
5 |
6 | * [excel表格转换工具](#excel表格转换工具)
7 | * [支持识别的excel样式](#支持识别的excel样式)
8 | * [准备表格时的注意事项](#准备表格时的注意事项)
9 | * [依赖的python包和tex包](#依赖的python包和tex包)
10 | * [使用](#使用)
11 | * [简单用法](#简单用法)
12 | * [识别更多格式](#识别更多格式)
13 | * [可能出现的问题](#可能出现的问题)
14 | * [乱码](#乱码)
15 | * [联系邮箱](#联系邮箱)
16 | * [打赏](#打赏)
17 |
18 |
19 |
20 |
21 |
22 | ## 支持识别的excel样式
23 |
24 | - 横线
25 | - 无样式
26 | - 实线
27 | - 虚线 (即将上线)
28 | - 双线 (即将上线)
29 | - 竖线
30 | - 无样式
31 | - 实线
32 | - 双线 (即将上线)
33 | - 线条颜色
34 | - 单元格颜色
35 | - 文本颜色
36 | - 水平对齐
37 | - 文本形式
38 | - 斜体
39 | - 粗体
40 |
41 | ## 准备表格时的注意事项
42 |
43 | - 设置颜色时不要选择**`主题颜色`**中的颜色,否则不能识别。选择`标准颜色`或者`更多颜色`通过色盘选取即可
44 | - 不支持单个字符文本样式设置,一个单元格中出现的文本样式最终都会被设置成这个单元格所有文本的样式
45 | - 你可以在excel中通过`alt + enter`进行手动换行,但是要注意最终文本的行数不能超过合并单元格的高度
46 |
47 | ## 依赖的python包和tex包
48 |
49 | 如果你没有使用`-e`选项,则把下面的tex包添加到导言区:
50 | ```tex
51 | \usepackage{multirow, makecell}
52 | ```
53 |
54 | 安装以下python包
55 | ```shell
56 | pip install openpyxl
57 | ```
58 |
59 | ## 使用
60 |
61 | ```text
62 | usage: excel2tex.py [-h] [-s SOURCE] [-o TARGET] [--setting SETTING] [--sig] [-m] [-e]
63 |
64 | 可选参数
65 | -h, --help 显示命令帮助并退出
66 | -s SOURCE 要转换的excel文件名(默认table.xlsx)
67 | -o TARGET 要生成的tex文件名(默认table.tex)
68 | --setting SETTING 要放到导言区的设置文件名(默认setting.tex)
69 | --sig 将编码设置为utf-8-sig,乱码的时候使用
70 | -m, --math 使得$...$被识别为数学模式
71 | -e, --excel-format 使用所有格式
72 | ```
73 |
74 | 如果你是windows用户且系统中没有python环境,这里提供了一个[二进制可执行文件](https://github.com/ZhiyuanLck/excel2tex/releases/tag/v1.1)使用
75 |
76 | ### 简单用法
77 |
78 | 下面是将要被转换的excel表格
79 |
80 | 
81 |
82 | 使用命令`python excel2tex.py`,你将得到一个只有必要格式的表格。这条命令的含义是将`table.xlsx`转换为`table.tex`。因为`-e`选项并没有被使用,生成的表格是最简的:
83 | - 绘制所有的线条
84 | - 文本和线条颜色默认黑色,无单元格填充
85 | - 所有文本居中对齐
86 | - 所有字符以本来的样子在latex生成的表格中显示
87 |
88 | 下面是转换生成的latex表格,生成的代码见[`simple.tex`](../master/simple.tex)
89 |
90 | 
91 |
92 | ### 识别更多格式
93 |
94 | 如果你希望尽可能的识别你在excel中设置的格式,使用`-e`选项。下面的命令会尽可能的识别你在excel中设置的格式然后转换成latex表格,同时也会生成一个设置文件,里面加载了需要的宏包,定义了一些命令和颜色,请在导言区导入这个配置文件(`\input{setting.tex}`)。`-m`选项保证了字符`$`不会被转义(`\$`),以便识别数学模式。
95 |
96 | ```shell
97 | python excel2tex.py -e -m
98 | ```
99 |
100 | 下面是转换生成的latex表格,生成的代码见[`all.tex`](../master/all.tex)
101 |
102 | 
103 |
104 | ## 可能出现的问题
105 |
106 | ### 乱码
107 |
108 | 使用`--sig`选项将编码设置为`utf-8-sig`
109 |
110 | ```shell
111 | python excel2tex.py -s table.xlsx -o table.tex --sig
112 | ```
113 |
114 | ## 联系邮箱
115 |
116 | 如果你有和这个脚本工具有关的紧急问题需要解决,把代码发到我的邮箱:lichangkai225@qq.com
117 |
118 | ## 打赏
119 |
120 | 如果这个工具帮助到了你,你喜欢它的话,欢迎打赏!
121 |
122 |
123 | | 微信 | 支付宝 |
124 | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
125 | | | |
126 |
--------------------------------------------------------------------------------
/doc/zh_cn.md.orig.2020-04-25_200527:
--------------------------------------------------------------------------------
1 | # excel表格转换工具
2 |
3 | 将excel中的表格转换为latex中的表格,支持识别多种格式。
4 |
5 |
6 | * [excel表格转换工具](#excel表格转换工具)
7 | * [支持识别的excel样式](#支持识别的excel样式)
8 | * [准备表格时的注意事项](#准备表格时的注意事项)
9 | * [依赖的python包和tex包](#依赖的python包和tex包)
10 | * [使用](#使用)
11 | * [简单用法](#简单用法)
12 | * [识别更多格式](#识别更多格式)
13 | * [可能出现的问题](#可能出现的问题)
14 | * [乱码](#乱码)
15 |
16 |
17 |
18 |
19 |
20 | ## 支持识别的excel样式
21 |
22 | - 横线
23 | - 无样式
24 | - 实线
25 | - 虚线 (即将上线)
26 | - 双线 (即将上线)
27 | - 竖线
28 | - 无样式
29 | - 实线
30 | - 双线 (即将上线)
31 | - 线条颜色
32 | - 单元格颜色
33 | - 文本颜色
34 | - 水平对齐
35 | - 文本形式
36 | - 斜体
37 | - 粗体
38 |
39 | ## 准备表格时的注意事项
40 |
41 | - 设置颜色时不要选择**`主题颜色`**中的颜色,否则不能识别。选择`标准颜色`或者`更多颜色`通过色盘选取即可
42 | - 不支持单个字符文本样式设置,一个单元格中出现的文本样式最终都会被设置成这个单元格所有文本的样式
43 | - 你可以在excel中通过`alt + enter`进行手动换行,但是要注意最终文本的行数不能超过合并单元格的高度
44 |
45 | ## 依赖的python包和tex包
46 |
47 | 如果你没有使用`-e`选项,则把下面的tex包添加到导言区:
48 | ```tex
49 | \usepackage{multirow, makecell}
50 | ```
51 |
52 | 安装以下python包
53 | ```shell
54 | pip install openpyxl
55 | ```
56 |
57 | ## 使用
58 |
59 | ```text
60 | usage: excel2tex.py [-h] [-s SOURCE] [-o TARGET] [--setting SETTING] [--sig] [-m] [-e]
61 |
62 | 可选参数
63 | -h, --help 显示命令帮助并退出
64 | -s SOURCE 要转换的excel文件名(默认table.xlsx)
65 | -o TARGET 要生成的tex文件名(默认table.tex)
66 | --setting SETTING 要放到导言区的设置文件名(默认setting.tex)
67 | --sig 将编码设置为utf-8-sig,乱码的时候使用
68 | -m, --math 使得$...$被识别为数学模式
69 | -e, --excel-format 使用所有格式
70 | ```
71 |
72 | 如果你是windows用户且系统中没有python环境,这里提供了一个[二进制可执行文件](https://github.com/ZhiyuanLck/excel2tex/releases/tag/0.1)(落后于当前版本)使用
73 |
74 | ### 简单用法
75 |
76 | 下面是将要被转换的excel表格
77 |
78 | 
79 |
80 | 使用命令`python excel2tex.py`,你将得到一个只有必要格式的表格。这条命令的含义是将`table.xlsx`转换为`table.tex`。因为`-e`选项并没有被使用,生成的表格是最简的:
81 | - 绘制所有的线条
82 | - 文本和线条颜色默认黑色,无单元格填充
83 | - 所有文本居中对齐
84 | - 所有字符以本来的样子在latex生成的表格中显示
85 |
86 | 下面是转换生成的latex表格,生成的代码见[`simple.tex`](../master/simple.tex)
87 |
88 | 
89 |
90 | ### 识别更多格式
91 |
92 | 如果你希望尽可能的识别你在excel中设置的格式,使用`-e`选项。下面的命令会尽可能的识别你在excel中设置的格式然后转换成latex表格,同时也会生成一个设置文件,里面加载了需要的宏包,定义了一些命令和颜色,请在导言区导入这个配置文件(`\input{setting.tex}`)。`-m`选项保证了字符`$`不会被转义(`\$`),以便识别数学模式。
93 |
94 | ```shell
95 | python excel2tex.py -e -m
96 | ```
97 |
98 | 下面是转换生成的latex表格,生成的代码见[`all.tex`](../master/all.tex)
99 |
100 | 
101 |
102 | ## 可能出现的问题
103 |
104 | ### 乱码
105 |
106 | 使用`--sig`选项将编码设置为`utf-8-sig`
107 |
108 | ```shell
109 | python excel2tex.py -s table.xlsx -o table.tex --sig
110 | ```
111 |
112 | ## 联系邮箱
113 |
114 | 如果你有和这个脚本工具有关的紧急问题需要解决,把代码发到我的邮箱:lichangkai225@qq.com
115 |
116 | ## 打赏
117 |
118 | 如果这个工具帮助到了你,你喜欢它的话,欢迎打赏!
119 |
120 |  
121 |
--------------------------------------------------------------------------------
/doc/zh_cn.md.toc.2020-04-25_200527:
--------------------------------------------------------------------------------
1 | * [excel表格转换工具](#excel表格转换工具)
2 | * [支持识别的excel样式](#支持识别的excel样式)
3 | * [准备表格时的注意事项](#准备表格时的注意事项)
4 | * [依赖的python包和tex包](#依赖的python包和tex包)
5 | * [使用](#使用)
6 | * [简单用法](#简单用法)
7 | * [识别更多格式](#识别更多格式)
8 | * [可能出现的问题](#可能出现的问题)
9 | * [乱码](#乱码)
10 | * [联系邮箱](#联系邮箱)
11 | * [打赏](#打赏)
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/all.tex:
--------------------------------------------------------------------------------
1 | \begin{tabular}{*{7}{c}}
2 | \hhline{
3 | >{\vsl{FF0000}{0.8pt}}
4 | !{\sfill{FF0000}{0.8pt}}
5 | !{\sfill{FF0000}{0.8pt}}
6 | !{\sfill{FF0000}{0.8pt}}
7 | !{\sfill{FF0000}{0.8pt}}
8 | !{\sfill{FF0000}{0.8pt}}
9 | !{\sfill{FF0000}{0.8pt}}
10 | !{\sfill{FF0000}{0.8pt}}
11 | >{\vsl{FF0000}{0.8pt}}
12 | }
13 | % row 1
14 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}c}{\cellcolor{EED270}}
15 | & \multicolumn{6}{r!{\vsl{FF0000}{0.8pt}}}{\cellcolor{057165}right aligned text}
16 | \\
17 | \hhline{
18 | >{\vsl{FF0000}{0.8pt}}
19 | !{\sfill{EED270}{0pt}}
20 | ~
21 | ~
22 | ~
23 | ~
24 | ~
25 | ~
26 | >{\vsl{FF0000}{0.8pt}}
27 | }
28 | % row 2
29 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}c}{\cellcolor{EED270}}
30 | & \multicolumn{1}{c}{\cellcolor{EED270}}
31 | & \multicolumn{4}{c}{\cellcolor{057165}center}
32 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}c!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}}
33 | \\
34 | \hhline{
35 | >{\vsl{FF0000}{0.8pt}}
36 | !{\sfill{EED270}{0.8pt}}
37 | !{\sfill{EED270}{0.8pt}}
38 | !{\ssfill{057165}{0.4pt}{EED270}{0.4pt}}
39 | !{\ssfill{057165}{0.4pt}{057165}{0.4pt}}
40 | !{\ssfill{057165}{0.4pt}{057165}{0.4pt}}
41 | !{\ssfill{057165}{0.4pt}{AF2BC7}{0.4pt}}
42 | !{\sfill{AF2BC7}{0.8pt}}
43 | >{\vsl{FF0000}{0.8pt}}
44 | }
45 | % row 3
46 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}c}{\cellcolor{EED270}}
47 | & \multicolumn{1}{c}{\cellcolor{EED270}}
48 | & \multicolumn{1}{c}{\cellcolor{EED270}}
49 | & \multicolumn{2}{>{\color{9DF4E0}}c}{\cellcolor{057165}}
50 | & \multicolumn{1}{>{\itshape\bfseries}c}{\cellcolor{AF2BC7}}
51 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}c!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}}
52 | \\
53 | \hhline{
54 | >{\vsl{FF0000}{0.8pt}}
55 | !{\sfill{EED270}{0.8pt}}
56 | !{\sfill{EED270}{0.8pt}}
57 | !{\sfill{EED270}{0.8pt}}
58 | >{\vsl{black}{0.8pt}}
59 | !{\sfill{black}{0.8pt}}
60 | >{\vsl{black}{0.8pt}}
61 | !{\ssfill{057165}{0.4pt}{AF2BC7}{0.4pt}}
62 | !{\sfill{AF2BC7}{0.8pt}}
63 | !{\sfill{AF2BC7}{0.8pt}}
64 | >{\vsl{FF0000}{0.8pt}}
65 | }
66 | % row 4
67 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}c}{\cellcolor{EED270}}
68 | & \multicolumn{1}{c}{\cellcolor{EED270}}
69 | & \multicolumn{1}{c!{\vsl{black}{0.8pt}}}{\cellcolor{EED270}
70 | \multirow{-2}*{}}
71 | & \multicolumn{1}{c!{\vsl{black}{0.8pt}}}{\cellcolor{FF0000}$x+y=1$}
72 | & \multicolumn{1}{c}{\cellcolor{AF2BC7}}
73 | & \multicolumn{1}{>{\itshape\bfseries}c}{\cellcolor{AF2BC7}}
74 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}c!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}}
75 | \\
76 | \hhline{
77 | >{\vsl{FF0000}{0.8pt}}
78 | !{\sfill{EED270}{0.8pt}}
79 | !{\sfill{EED270}{0.8pt}}
80 | !{\ssfill{EED270}{0.4pt}{30E8E6}{0.4pt}}
81 | >{\vsl{black}{0.8pt}}
82 | !{\sfill{black}{0.8pt}}
83 | >{\vsl{black}{0.8pt}}
84 | !{\sfill{AF2BC7}{0.8pt}}
85 | !{\sfill{AF2BC7}{0.8pt}}
86 | !{\sfill{AF2BC7}{0.8pt}}
87 | >{\vsl{FF0000}{0.8pt}}
88 | }
89 | % row 5
90 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}c}{\cellcolor{EED270}}
91 | & \multicolumn{1}{c}{\cellcolor{EED270}
92 | \multirow{-4}*{\minitab[c]{\minitab[c]{mid \\normal}}}}
93 | & \multicolumn{2}{c}{\cellcolor{30E8E6}}
94 | & \multicolumn{1}{c}{\cellcolor{AF2BC7}
95 | \multirow{-2}*{}}
96 | & \multicolumn{1}{>{\itshape\bfseries}c}{\cellcolor{AF2BC7}}
97 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}c!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}}
98 | \\
99 | \hhline{
100 | >{\vsl{FF0000}{0.8pt}}
101 | !{\sfill{EED270}{0pt}}
102 | ~
103 | ~
104 | ~
105 | ~
106 | !{\sfill{AF2BC7}{0pt}}
107 | !{\sfill{AF2BC7}{0pt}}
108 | >{\vsl{FF0000}{0.8pt}}
109 | }
110 | % row 6
111 | \multicolumn{1}{!{\vsl{FF0000}{0.8pt}}>{\color{FF0000}\bfseries}l}{\cellcolor{EED270}
112 | \multirow{-6}*{\minitab[l]{\minitab[l]{left \\bold \\red}}}}
113 | & \multicolumn{4}{c}{\cellcolor{30E8E6}center}
114 | & \multicolumn{1}{>{\itshape\bfseries}c}{\cellcolor{AF2BC7}
115 | \multirow{-4}*{\minitab[c]{\minitab[c]{bold \\italic}}}}
116 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}c!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}}
117 | \\
118 | \hhline{
119 | >{\vsl{FF0000}{0.8pt}}
120 | ~
121 | ~
122 | ~
123 | ~
124 | ~
125 | ~
126 | !{\sfill{AF2BC7}{0pt}}
127 | >{\vsl{FF0000}{0.8pt}}
128 | }
129 | % row 7
130 | \multicolumn{6}{!{\vsl{FF0000}{0.8pt}}l}{\cellcolor{30E8E6}left aligned text}
131 | & \multicolumn{1}{>{\color{8A0E0E}\itshape}r!{\vsl{FF0000}{0.8pt}}}{\cellcolor{AF2BC7}
132 | \multirow{-6}*{\minitab[r]{\minitab[r]{right \\italic \\brown}}}}
133 | \\
134 | \hhline{
135 | >{\vsl{FF0000}{0.8pt}}
136 | !{\sfill{FF0000}{0.8pt}}
137 | !{\sfill{FF0000}{0.8pt}}
138 | !{\sfill{FF0000}{0.8pt}}
139 | !{\sfill{FF0000}{0.8pt}}
140 | !{\sfill{FF0000}{0.8pt}}
141 | !{\sfill{FF0000}{0.8pt}}
142 | !{\sfill{FF0000}{0.8pt}}
143 | >{\vsl{FF0000}{0.8pt}}
144 | }
145 | \end{tabular}
--------------------------------------------------------------------------------
/examples/main.tex:
--------------------------------------------------------------------------------
1 | \documentclass{article}
2 | \usepackage{makecell}
3 | \input{setting.tex}
4 |
5 | \begin{document}
6 | \input{simple.tex}
7 |
8 | \input{all.tex}
9 | \end{document}
10 |
--------------------------------------------------------------------------------
/examples/setting.tex:
--------------------------------------------------------------------------------
1 | \usepackage[table]{xcolor}
2 | \usepackage{multirow}
3 | \usepackage{colortbl}
4 | \usepackage{dashrule}
5 | \usepackage{ehhline}
6 |
7 | %% nested tabular
8 | \newcommand{\minitab}[2][l]{\begin{tabular}{@{}#1@{}}#2\end{tabular}}
9 |
10 | %% vertical line
11 | % vertical colored line #1 color #2 width
12 | \newcommand{\vsl}[2]{\color{#1}\vrule width #2}
13 | % doubled vline
14 | % #1 first color #2 first width #3 sep #4 second color #5 second sep
15 | \newcommand{\dvsl}[5]{%
16 | \vsl{#1}{#2}\hspace{#3}\vsl{#4}{#5}%
17 | }
18 |
19 | %% horizontal line
20 | % colored solid line pattern
21 | % #1 color #2 width #3 height
22 | \newcommand{\hsp}[3]{\hbox{\textcolor{#1}{\rule{#2}{#3}}}}
23 | % colored dash line pattern
24 | % #1 color #2 width #3 height #4 style
25 | \newcommand{\hdp}[4]{\hbox{\textcolor{#1}{\hdashrule{#2}{#3}{#4}}}}
26 | % fill line
27 | % #1 top fill #2 bottom fill
28 | \newcommand{\leaderfill}[1]{%
29 | \xleaders\hbox{%
30 | \vbox{\baselineskip=0pt\lineskip=0pt#1}%
31 | }\hfill%
32 | }
33 | % top: solid, bottom: solid
34 | % #1 top color #2 top height #3 bottom color#4 bottom height
35 | \newcommand{\ssfill}[4]{%
36 | \leaderfill{\hsp{#1}{0.1pt}{#2}\hsp{#3}{0.1pt}{#4}}%
37 | }
38 | % top: solid, bottom: dashed
39 | % #1 top color, #2 top height, #3 common width to expand
40 | % #4 bottom color #5 bottom height, #6 bottom dash line style
41 | \newcommand{\sdfill}[6]{%
42 | \leaderfill{\hsp{#1}{#3}{#2}\hdp{#4}{#3}{#5}{#6}}%
43 | }
44 | % single solid
45 | % #1 color #2 height
46 | \newcommand{\sfill}[2]{%
47 | \leaderfill{\hsp{#1}{0.1pt}{#2}}%
48 | }
49 | % single dash
50 | % #1 color #2 width #3 height #4 style
51 | \newcommand{\dfill}[4]{%
52 | \leaderfill{\hdp{#1}{#2}{#3}{#4}}%
53 | }
54 |
55 | %% vline settings
56 | % #1 rule width #2 color
57 | \newcommand{\beforevline}[2]{%
58 | \global\setlength\arrayrulewidth{#1}\arrayrulecolor{#2}%
59 | }
60 | \newcommand{\aftervline}{%
61 | \global\setlength\arrayrulewidth{0.4pt}\arrayrulecolor{black}%
62 | }
63 | \definecolor{057165}{HTML}{057165}
64 | \definecolor{30E8E6}{HTML}{30E8E6}
65 | \definecolor{EED270}{HTML}{EED270}
66 | \definecolor{8A0E0E}{HTML}{8A0E0E}
67 | \definecolor{9DF4E0}{HTML}{9DF4E0}
68 | \definecolor{FF0000}{HTML}{FF0000}
69 | \definecolor{AF2BC7}{HTML}{AF2BC7}
70 |
--------------------------------------------------------------------------------
/examples/simple.tex:
--------------------------------------------------------------------------------
1 | % Please add the following required packages to your document preamble:
2 | % \usepackage{multirow, makecell}
3 |
4 | \begin{tabular}{*{7}{|c}|}
5 | \hline
6 |
7 | % row 1
8 | \multirowcell{6}{left \\bold \\red}
9 | & \multicolumn{6}{c|}{right aligned text} \\
10 | \cline{2-7}
11 |
12 | % row 2
13 | & \multirowcell{4}{mid \\normal}
14 | & \multicolumn{4}{c|}{center}
15 | & \multirowcell{6}{right \\italic \\brown} \\
16 | \cline{3-6}
17 |
18 | % row 3
19 | & & \multirowcell{2}{}
20 | & \multicolumn{2}{c|}{}
21 | & \multirowcell{4}{bold \\italic}
22 | & \\
23 | \cline{4-5}
24 |
25 | % row 4
26 | & & & $x+y=1$
27 | & \multirowcell{2}{}
28 | & & \\
29 | \cline{3-4}
30 |
31 | % row 5
32 | & & \multicolumn{2}{c|}{}
33 | & & & \\
34 | \cline{2-5}
35 |
36 | % row 6
37 | & \multicolumn{4}{c|}{center}
38 | & & \\
39 | \cline{1-6}
40 |
41 | % row 7
42 | \multicolumn{6}{|c|}{left aligned text}
43 | & \\
44 | \hline
45 | \end{tabular}
--------------------------------------------------------------------------------
/excel2tex.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from openpyxl import load_workbook
3 | from src.Table import Table
4 |
5 | if __name__ == '__main__':
6 | parser = argparse.ArgumentParser(
7 | description='Convert excel table to latex table.',
8 | formatter_class=argparse.RawTextHelpFormatter,
9 | epilog="""Note: The height of every merged cell must not be less than the number of lines in your text.
10 | """
11 | )
12 | parser.add_argument('-s', default='table.xlsx', dest='source', help='source file (default: %(default)s)')
13 | parser.add_argument('-o', default='table.tex', dest='target', help='target file (default: %(default)s)')
14 | parser.add_argument('--setting', default='setting.tex', dest='setting', help='setting file (default: %(default)s)')
15 | # parser.add_argument('-w', default='\\linewidth', dest='width', help='table width (default: %(default)s)')
16 | parser.add_argument('--sig', default='utf-8', dest='encoding',
17 | nargs='?',
18 | const='utf-8-sig',
19 | help='set file encoding to utf-8-sig, only use when there is mess code.')
20 | parser.add_argument('-m', '--math', type=bool, default=False, dest='math',
21 | help='enable inline math', const=True, nargs='?')
22 | parser.add_argument('-e', '--excel-format', type=bool, default=False,
23 | dest='excel_format', help='enable all formats', const=True, nargs='?')
24 | args = parser.parse_args()
25 | wb = load_workbook(args.source)
26 | ws = wb.active
27 | t = Table(ws, args)
28 | with open(args.target, 'w', encoding=args.encoding) as f:
29 | f.write(t.tex)
30 |
--------------------------------------------------------------------------------
/img/alipay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/img/alipay.jpg
--------------------------------------------------------------------------------
/img/all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/img/all.png
--------------------------------------------------------------------------------
/img/excel_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/img/excel_table.png
--------------------------------------------------------------------------------
/img/simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/img/simple.png
--------------------------------------------------------------------------------
/img/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/img/wechat.png
--------------------------------------------------------------------------------
/src/Cell.py:
--------------------------------------------------------------------------------
1 | from .Prop import TextProp, Border
2 |
3 | class Cell:
4 | def __init__(self, cell, table):
5 | self.table = table
6 | self.cell = cell
7 | self.args = table.args
8 | self.head = self
9 | self.coor = (1, 1)
10 | self.align = 'center'
11 | self.cell_type = 'plain'
12 | self.merged_idx = 0
13 | self.height = 1
14 | self.width = 1
15 | self.color = 'white'
16 | self.begin = False
17 | self.end = False
18 | # block控制语句
19 | self.control_cell = False
20 | self.one_row = False
21 | # block中与控制单元格在同一行的其他单元格忽略
22 | self.ignored = False
23 | self.block_begin = False
24 | self.block_end = False
25 | self.text_prop = TextProp(self.table)
26 | self.border = Border(self.table)
27 |
28 | def set_prop(self):
29 | align_dic = {
30 | "center": "c",
31 | "left": "l",
32 | "right": "r",
33 | }
34 | self.coor = (self.cell.row, self.cell.column)
35 | self.align = self.cell.alignment.horizontal
36 | if self.align is None:
37 | self.align = 'center'
38 | self.align = align_dic[self.align]
39 | if self.merged_idx:
40 | self.align = self.head.align
41 | row, col = self.coor
42 | merged_cell = self.table.merged_cells[self.merged_idx-1]
43 | self.control_cell = merged_cell.is_control(row, col)
44 | self.ignored = merged_cell.is_ignored(row, col)
45 | self.one_row = merged_cell.is_one_row(row, col)
46 | self.block_begin = col == merged_cell.y1
47 | self.block_end = col == merged_cell.y2
48 | self.text_prop.set_prop(self)
49 |
50 | def set_color(self):
51 | color = self.cell.fill.fgColor.rgb
52 | if color is not None and color != '00000000' and isinstance(color, str):
53 | self.color = color[2:]
54 | self.table.colors.add(self.color)
55 | elif self.merged_idx:
56 | self.color = self.head.color
57 | if self.color == '000000':
58 | self.color = 'white'
59 |
60 | def get_rvline(self):
61 | x, y = self.coor
62 | if self.merged_idx:
63 | y = self.table.merged_cells[self.merged_idx - 1].y2
64 | return self.table.vlines.borders[x - 1][y]
65 |
66 | def get_merged_idx(self, merged_cells, row, col):
67 | for m in merged_cells:
68 | if m.is_merged(row, col):
69 | return m.idx
70 | return 0
71 |
72 | def get_head(self, merged_cells, row, col):
73 | for m in merged_cells:
74 | if m.is_merged(row, col):
75 | return m.get_head()
76 | return row, col
77 |
78 | def skip(self):
79 | return self.cell_type != "multicolumn_other" and self.cell_type != "block_firstline_other"
80 |
81 |
82 | def not_empty(self):
83 | res = self.text_prop.text is not None or self.merged_idx
84 | if self.table.args.excel_format:
85 | res = res or self.color != 'white'
86 | return res
87 |
--------------------------------------------------------------------------------
/src/MergedCell.py:
--------------------------------------------------------------------------------
1 | class MergedCell:
2 | def __init__(self, idx, bounds):
3 | self.y1, self.x1, self.y2, self.x2 = bounds
4 | # idx'th mergedcell
5 | self.idx = idx
6 |
7 | def is_merged(self, row, col):
8 | return True if row >= self.x1 and row <= self.x2 and col >= self.y1 and col <= self.y2 else False
9 |
10 | def get_type(self, row, col):
11 | if self.x1 != self.x2 and self.y1 != self.y2:
12 | if row == self.x1:
13 | return "block_firstline_begin" if col == self.y1 else "block_firstline_other"
14 | if col == self.y1:
15 | return "block_placeholder_begin"
16 | return "block_placeholder_end" if col == self.y2 else "block_placeholder_other"
17 | if self.x1 == self.x2:
18 | return "multicolumn_begin" if col == self.y1 else "multicolumn_other"
19 | if self.y1 == self.y2:
20 | return "multirowcell_begin" if row == self.x1 else "multirowcell_other"
21 |
22 | def is_end(self, col_max):
23 | return self.y2 == col_max
24 |
25 | def get_head(self):
26 | return self.x1, self.y1
27 |
28 | def is_control(self, row, col):
29 | return row == self.x2 and col == self.y1
30 |
31 | def is_ignored(self, row, col):
32 | return row == self.x2 and col > self.y1
33 |
34 | def is_one_row(self, row, col):
35 | return self.x1 == self.x2
36 |
--------------------------------------------------------------------------------
/src/Prop.py:
--------------------------------------------------------------------------------
1 | class TextProp:
2 | def __init__(self, table):
3 | self.table = table
4 | self.text = ''
5 | self.i = False
6 | self.b = False
7 | self.color = '000000'
8 |
9 | def set_prop(self, cell):
10 | self.i = cell.cell.font.i
11 | self.b = cell.cell.font.b
12 | color = cell.cell.font.color.rgb
13 | if color is not None and isinstance(color, str):
14 | self.color = color[2:]
15 | if cell.merged_idx:
16 | self.i = cell.head.text_prop.i
17 | self.b = cell.head.text_prop.b
18 | self.color = cell.head.text_prop.color
19 | self.text = cell.head.text_prop.text
20 | if self.color != '000000':
21 | self.table.colors.add(self.color)
22 | self.text = self.format_text(cell) if cell.args.excel_format else self.get_cell_tex(cell)
23 |
24 | def format_text(self, cell):
25 | text = self.text
26 | # no text
27 | if text is None:
28 | text = ''
29 | # not string
30 | if not isinstance(text, str):
31 | text = str(text)
32 | # deal with escape character
33 | text = text.replace('#', r'\#')
34 | text = text.replace('%', r'\%')
35 | # inline math
36 | if not cell.args.math:
37 | text = text.replace('_', r'\_')
38 | text = text.replace('$', r'\$')
39 | # line break
40 | text = text.replace('\n', r' \\')
41 | # excel format
42 | if text:
43 | if cell.args.excel_format:
44 | if text.find('\\\\') != -1:
45 | text = f'\\minitab[{cell.align}]{{{text}}}'
46 | return text
47 |
48 | # not used by excel format
49 | def get_cell_tex(self, cell):
50 | text = self.format_text(cell)
51 | begin_line = '|' if cell.begin else ''
52 | str_dic = {
53 | 'plain': text,
54 | 'multirowcell_begin': '\\multirowcell{' + str(cell.height) + '}{' + text + '}',
55 | 'multirowcell_other': '',
56 | # 'multicolumn_begin': '\\multicolumn{' + str(cell.width) + '}{' + '|' if self.parameters['begin'] else '' + 'c|}{' + text + '}',
57 | 'multicolumn_begin': '\\multicolumn{' + str(cell.width) + '}{' + begin_line + 'c|}{' + text + '}',
58 | 'multicolumn_other': '',
59 | 'block_firstline_begin': '\\multicolumn{' + str(cell.width) + '}{' + begin_line + 'c|}{' + '\\multirowcell{' + str(cell.height) + '}{' + text + '}' + '}',
60 | 'block_firstline_other': '',
61 | 'block_placeholder_begin': '\\multicolumn{1}{|c}{}',
62 | 'block_placeholder_end': '\\multicolumn{1}{c|}{}',
63 | 'block_placeholder_other': '\\multicolumn{1}{c}{}'
64 | }
65 | text = str_dic[cell.cell_type]
66 | if not cell.begin:
67 | text = '& ' + text
68 | if cell.end:
69 | text += r' \\'
70 | if text:
71 | text = ' ' + text.strip() + '\n'
72 | return text
73 |
74 | class BorderLine:
75 | def __init__(self, table):
76 | self.table = table
77 | self.style = None
78 | self.color = None
79 |
80 | def set_prop(self, cell, pos):
81 | border = getattr(cell.cell.border, pos)
82 | if border.style is not None:
83 | self.style = border.style
84 | color = border.color.rgb
85 | if color is not None and isinstance(color, str):
86 | self.color = color[2:]
87 | if self.color != '000000':
88 | self.table.colors.add(self.color)
89 |
90 | class Border:
91 | def __init__(self, table):
92 | self.left = BorderLine(table)
93 | self.right = BorderLine(table)
94 | self.top = BorderLine(table)
95 | self.bottom = BorderLine(table)
96 |
97 | def set_prop(self, cell):
98 | for pos in ['left', 'right', 'top', 'bottom']:
99 | getattr(self, pos).set_prop(cell, pos)
100 |
--------------------------------------------------------------------------------
/src/Table.py:
--------------------------------------------------------------------------------
1 | from .Cell import Cell
2 | from .MergedCell import MergedCell
3 | from .help import scan_all
4 | from .line import LineMatrix
5 | from .output import Output
6 | from .setting import Setting
7 |
8 | class Table:
9 | def __init__(self, ws, args):
10 | self.ws = ws
11 | self.args = args
12 | self.x1 = 1
13 | self.x2 = ws.max_row + 3
14 | self.y1 = 1
15 | self.y2 = ws.max_column + 3
16 | self.colors = set()
17 | self.vspace = 0
18 | self.cells = [
19 | [Cell(self.ws.cell(i + 1, j + 1), self)
20 | for j in range(self.y2)]
21 | for i in range(self.x2)]
22 | self.merged_cells = [MergedCell(idx + 1, cell.bounds) for idx, cell in enumerate(ws.merged_cells.ranges)]
23 | self.init_cell_matrix()
24 | self.hlines = LineMatrix(self, 'hline')
25 | self.vlines = LineMatrix(self, 'vline')
26 | self.set_bounds()
27 | self.hlines.set_lines('hline')
28 | self.hlines.set_vspace()
29 | self.vlines.set_lines('vline')
30 | if self.args.excel_format:
31 | self.set_line_bounds()
32 | self.set_props()
33 | self.set_hlines()
34 | if self.args.excel_format:
35 | Setting(self)
36 | self.tex = Output(self).tex
37 | else:
38 | self.set_texs()
39 |
40 | def init_cell_matrix(self):
41 | for i in range(1, self.x2 + 1):
42 | for j in range(1, self.y2 + 1):
43 | cell = self.cells[i - 1][j - 1]
44 | r, c = cell.get_head(self.merged_cells, i, j)
45 | cell.head = self.cells[r - 1][c - 1]
46 | cell.merged_idx = cell.get_merged_idx(self.merged_cells, i, j)
47 | cell.text_prop.text = self.ws.cell(i, j).value
48 | cell.set_color()
49 | cell.border.set_prop(cell)
50 |
51 | def set_bounds(self):
52 | # remove top empty rows
53 | old = self.x1
54 | for i in range(old - 1, self.x2):
55 | cell_list = self.cells[i]
56 | if scan_all(cell_list, 'not_empty'):
57 | self.x1 += 1
58 | else:
59 | break
60 | # remove bottom empty rows
61 | old = self.x2
62 | for i in range(old, self.x1 - 1, -1):
63 | cell_list = self.cells[i - 1]
64 | if scan_all(cell_list, 'not_empty'):
65 | self.x2 -= 1
66 | else:
67 | break
68 | # remove left empty cols
69 | old = self.y1
70 | for j in range(old - 1, self.y2):
71 | cell_list = [row[j] for row in self.cells]
72 | if scan_all(cell_list, 'not_empty'):
73 | self.y1 += 1
74 | else:
75 | break
76 | # remove right empty cols
77 | old = self.y2
78 | for j in range(old - 1, self.y1 - 2, -1):
79 | cell_list = [row[j] for row in self.cells]
80 | if scan_all(cell_list, 'not_empty'):
81 | self.y2 -= 1
82 | else:
83 | break
84 |
85 | def set_line_bounds(self):
86 | hline_transpose = list(zip(*self.hlines.borders))
87 | vline_transpose = list(zip(*self.vlines.borders))
88 | # hline x
89 | hline_x_min = 1
90 | hline_x_max = len(self.hlines.borders)
91 | # cell y
92 | cell_y_min = 1
93 | cell_y_max = len(hline_transpose)
94 | # cell x
95 | cell_x_min = 1
96 | cell_x_max = len(self.vlines.borders)
97 | # vline y
98 | vline_y_min = 1
99 | vline_y_max = len(vline_transpose)
100 | # hline remove empty row line
101 | for hline in self.hlines.borders:
102 | if self.hlines.is_empty(hline):
103 | hline_x_min += 1
104 | else:
105 | break
106 | for hline in self.hlines.borders[::-1]:
107 | if self.hlines.is_empty(hline):
108 | hline_x_max -= 1
109 | else:
110 | break
111 | # hline remove empty col cell
112 | for hline in hline_transpose:
113 | if self.hlines.is_empty(hline):
114 | cell_y_min += 1
115 | else:
116 | break
117 | for hline in hline_transpose[::-1]:
118 | if self.hlines.is_empty(hline):
119 | cell_y_max -= 1
120 | else:
121 | break
122 | # vline remove empty col line
123 | for vline in vline_transpose:
124 | if self.vlines.is_empty(vline):
125 | vline_y_min += 1
126 | else:
127 | break
128 | for vline in vline_transpose[::-1]:
129 | if self.vlines.is_empty(vline):
130 | vline_y_max -= 1
131 | else:
132 | break
133 | # vline remove empty row cell
134 | for vline in self.vlines.borders:
135 | if self.vlines.is_empty(vline):
136 | cell_x_min += 1
137 | else:
138 | break
139 | for vline in self.vlines.borders[::-1]:
140 | if self.vlines.is_empty(vline):
141 | cell_x_max -= 1
142 | else:
143 | break
144 | # combine line range with origin cell range
145 | if hline_x_min <= hline_x_max:
146 | x1 = hline_x_min
147 | x2 = hline_x_max if hline_x_min == hline_x_max else hline_x_max - 1
148 | self.x1 = min(self.x1, x1)
149 | self.x2 = max(self.x2, x2)
150 | if vline_y_min <= vline_y_max:
151 | y1 = vline_y_min
152 | y2 = vline_y_max if vline_y_min == vline_y_max else vline_y_max - 1
153 | self.y1 = min(self.y1, y1)
154 | self.y2 = max(self.y2, y2)
155 | # combine new cell range with the range befor
156 | self.x1 = min(self.x1, cell_x_min)
157 | self.x2 = max(self.x2, cell_x_max)
158 | self.y1 = min(self.y1, cell_y_min)
159 | self.y2 = max(self.y2, cell_y_max)
160 |
161 | def set_props(self):
162 | for i in range(self.x1, self.x2 + 1):
163 | for j in range(self.y1, self.y2 + 1):
164 | cell = self.cells[i - 1][j - 1]
165 | self.set_cell_type(i, j)
166 | cell.set_prop()
167 |
168 | def set_cell_type(self, i, j):
169 | '''
170 | set `begin`, `end`, `height`, `width`, `cell_type`
171 | '''
172 | # for i in range(self.x1, self.x2 + 1):
173 | # for j in range(self.y1, self.y2 + 1):
174 | cell = self.cells[i - 1][j - 1]
175 | cell.cell_type = "plain"
176 | cell.begin = True if j == self.y1 else False
177 | # real end or block end
178 | cell.end = True if j == self.y2 else False
179 | if cell.merged_idx:
180 | merged_cell = self.merged_cells[cell.merged_idx - 1]
181 | cell.cell_type = merged_cell.get_type(i, j)
182 | cell.height = merged_cell.x2 - merged_cell.x1 + 1
183 | cell.width = merged_cell.y2 - merged_cell.y1 + 1
184 | cell.end = merged_cell.is_end(self.y2)
185 |
186 | def set_hlines(self):
187 | self.hline_ranges = []
188 | for i in range(self.x1, self.x2 + 1):
189 | row_hline = []
190 | hline_begin = self.y1 - 1
191 | hline_end = self.y1 - 1
192 | hline_start = True
193 | for j in range(self.y1, self.y2 + 1):
194 | if i == self.x1:
195 | continue
196 | cell = self.cells[i - 1][j - 1]
197 | up_cell = self.cells[i - 2][j - 1]
198 | if cell.cell_type == 'plain' or cell.merged_idx != up_cell.merged_idx:
199 | if hline_start:
200 | hline_begin = j
201 | hline_start = False
202 | hline_end = j
203 | if j == self.y2:
204 | row_hline.append((hline_begin, hline_end))
205 | else:
206 | if not hline_start:
207 | row_hline.append((hline_begin, hline_end))
208 | hline_start = True
209 | if i > self.x1:
210 | self.hline_ranges.append(row_hline)
211 |
212 | def set_texs(self):
213 | self.row_texs = []
214 | self.tex = '% Please add the following required packages to your document preamble:\n'
215 | self.tex += '% \\usepackage{multirow, makecell}\n'
216 | if self.args.excel_format:
217 | self.tex += '% \\usepackage{booktabs}'
218 | self.tex += '% \\usepackage{colortbl}'
219 | self.tex += '% \\usepackage{arydshln}'
220 | if self.args.excel_format:
221 | self.tex += '''
222 | {
223 | \\setlength\\abovetopsep{0pt}
224 | \\setlength\\belowbottomsep{0pt}
225 | \\setlength\\aboverulesep{0pt}
226 | \\setlength\\belowrulesep{0pt}
227 | \\newcommand\\colorwrap[2]{
228 | \\arrayrulecolor{#1}#2
229 | \\arrayrulecolor{black}
230 | }
231 | '''
232 | self.tex += '\n\\begin{tabular}{*{' + str(self.y2 - self.y1 + 1) + '}{|c}|}\n'
233 | if self.args.excel_format:
234 | self.convert_excel()
235 | self.tex += ''.join(self.row_texs)
236 | else:
237 | self.tex += '\\hline\n'
238 | self.convert()
239 | self.tex += ''.join(self.row_texs)
240 | self.tex += '\\hline\n'
241 | self.tex += '\\end{tabular}'
242 | if self.args.excel_format:
243 | self.tex += '\n}'
244 |
245 | def get_all_hline_tex(self, cell, n, row, col):
246 | res = ''
247 | if n < self.x2 - self.x1 + 1 and cell.end:
248 | row_hline = self.hline_ranges[n - 1]
249 | if len(row_hline) == 1 and row_hline[0][0] == self.y1 and row_hline[0][1] == self.y2:
250 | res += '\\hline'
251 | else:
252 | for hline_range in row_hline:
253 | res += '\\cline{' + str(hline_range[0]) + '-' + str(hline_range[1]) + '}\n'
254 | return res
255 |
256 | def convert(self):
257 | n = 1
258 | for i in range(self.x1, self.x2 + 1):
259 | row_tex = f'\n% row {n}\n'
260 | for j in range(self.y1, self.y2 + 1):
261 | cell = self.cells[i - 1][j - 1]
262 | if cell.skip():
263 | out_text = cell.text_prop.text
264 | # hline
265 | out_text += self.get_all_hline_tex(cell, n, i, j)
266 | row_tex += out_text
267 | n += 1
268 | self.row_texs.append(row_tex.replace(' &\n', ' &'))
269 |
270 | def convert_excel(self):
271 | n = 1
272 | for i in range(self.x1, self.x2 + 1):
273 | row_tex = f'\n% row {n}\n'
274 | if n == 1:
275 | row_tex += self.hlines.get_row_hline_tex(self.hlines.borders[i - 1])
276 | row_tex += '\\vspace{-' + str(self.vspace) + 'pt}'
277 | for j in range(self.y1, self.y2 + 1):
278 | cell = self.cells[i - 1][j - 1]
279 | if cell.skip():
280 | out_text = cell.text_prop.text
281 | row_tex += out_text
282 | # hline
283 | hline_tex = self.hlines.get_row_hline_tex(self.hlines.borders[i])
284 | row_tex += hline_tex
285 | n += 1
286 | self.row_texs.append(row_tex.replace(' &\n', ' &'))
287 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiyuanLck/excel2tex/b272f3181abcb8beb23e7fe9429b1b12a15a309a/src/__init__.py
--------------------------------------------------------------------------------
/src/help.py:
--------------------------------------------------------------------------------
1 | def scan_all(cell_list, check):
2 | '''
3 | return true if all checked false
4 | '''
5 | for cell in cell_list:
6 | if getattr(cell, check)():
7 | return False
8 | return True
9 |
10 | # wrap !{}
11 | def wrap_excl(text):
12 | return f' !{{{text}}}\n'
13 |
14 | # wrap >{}
15 | def wrap_ge(text):
16 | return f' >{{{text}}}\n'
17 |
--------------------------------------------------------------------------------
/src/line.py:
--------------------------------------------------------------------------------
1 | from copy import copy
2 |
3 | class ClineRange:
4 | def __init__(self, start, end, style):
5 | self.start = start
6 | self.end = end
7 | self.style = style
8 |
9 | class Line:
10 | style_dic = {
11 | # line_number
12 | # is_dash | l_n | width | dash_style
13 | 'thin': (False, 1, 0.4, [1, 1]),
14 | 'medium': (False, 1, 0.6, [1, 1]),
15 | 'thick': (False, 1, 0.8, [1, 1]),
16 | 'double': (False, 2, 0.4, [1, 1]),
17 | 'hair': (True, 1, 0.4, [1, 1]),
18 | 'dotted': (True, 1, 0.4, [1, 1]),
19 | 'dashed': (True, 1, 0.4, [1, 1]),
20 | 'dashDot': (True, 1, 0.4, [1, 1]),
21 | 'dashDotDot': (True, 1, 0.4, [1, 1]),
22 | 'mediumDashed': (True, 1, 0.4, [1, 1]),
23 | 'mediumDashDot': (True, 1, 0.4, [1, 1]),
24 | 'mediumDashDotDot': (True, 1, 0.4, [1, 1]),
25 | 'slantDashDot': (True, 1, 0.4, [1, 1]),
26 | }
27 | def __init__(self, table, idx):
28 | self.table = table
29 | self.idx = idx
30 | self.style = None
31 | self.is_none = True
32 | self.is_dash = False
33 | self.line_number = 1
34 | self.width = 0
35 | self.dash_style = []
36 | self.is_colored = False
37 | # hline or vline
38 | self.type = None
39 | self.color = 'white'
40 | # 第一行的顶端的横线
41 | self.first_hline = False
42 | # 表格末尾的横线
43 | self.last_hline = False
44 | # 表格左侧的竖线
45 | self.first_vline = False
46 | # 竖线是否不画
47 | self.ignored = False
48 |
49 | def get_lhline(self):
50 | table = self.table
51 | cells = table.cells
52 | lhline = None
53 | l_x, l_y = self.idx
54 | if l_y > table.y1 - 1:
55 | lhline = table.hlines.borders[l_x][l_y - 1]
56 | return lhline
57 |
58 | def valid_bound(self, x, y):
59 | table = self.table
60 | x_min = table.x1 - 1
61 | x_max = table.x2 - 1
62 | y_min = table.y1 - 1
63 | y_max = table.y2 - 1
64 | return x_min < x < x_max and y_min < y < y_max
65 |
66 | def get_cell(self, mode):
67 | table = self.table
68 | cells = table.cells
69 | l_x, l_y = self.idx
70 | cell_dic = {
71 | 't': (l_x - 1, l_y),
72 | 'b': (l_x, l_y),
73 | 'l': (l_x, l_y - 1),
74 | 'r': (l_x, l_y),
75 | }
76 | c_x, c_y = cell_dic[mode]
77 | if not self.valid_bound(c_x, c_y):
78 | return None
79 | return cells[c_x][c_y]
80 |
81 | def format_dash(self):
82 | return 'pt '.join([str(x) for x in self.dash_style]) + 'pt'
83 |
84 | def get_dash_width(self):
85 | return str(sum(self.dash_style)) + 'pt'
86 |
87 | def set_style(self, style):
88 | self.style = style
89 | (self.is_dash, self.line_number, self.width, self.dash_style) = self.style_dic[style]
90 |
91 | def get_vline(self, table, i, j):
92 | if j == 0:
93 | return table.cells[i][j].border.left
94 | left = table.cells[i][j - 1].border.right
95 | right = table.cells[i][j].border.left
96 | return left if left.style is not None else right
97 |
98 | def get_hline(self, table, i, j):
99 | if i == 0:
100 | return table.cells[i][j].border.top
101 | up = table.cells[i - 1][j].border.bottom
102 | down = table.cells[i][j].border.top
103 | return up if up.style is not None else down
104 |
105 | def set_line(self, table, i, j, type):
106 | self.type = type
107 | if i + 1 == table.x1:
108 | self.first_hline = True
109 | if i == table.x2:
110 | self.last_hline = True
111 | if j + 1 == table.y1:
112 | self.first_vline = True
113 | if type == 'hline' and i > table.x1 - 1 and i < table.x2:
114 | up = table.cells[i - 1][j].merged_idx
115 | down = table.cells[i][j].merged_idx
116 | if up == down and up > 0:
117 | self.ignored = True
118 | if type == 'vline' and j > table.y1 - 1 and j < table.y2:
119 | left = table.cells[i][j - 1].merged_idx
120 | right = table.cells[i][j].merged_idx
121 | if left == right and left > 0:
122 | self.ignored = True
123 | border = self.get_hline(table, i, j) if type == 'hline' else self.get_vline(table, i, j)
124 | if border.style is not None:
125 | self.is_none = False
126 | self.set_style(border.style)
127 | if border.color is not None:
128 | self.is_colored = True
129 | self.color = border.color
130 | table.colors.add(self.color)
131 | else:
132 | self.color = 'black'
133 |
134 | def is_eql(self, line):
135 | if self.style != line.style:
136 | return False
137 | if self.is_colored != line.is_colored:
138 | return False
139 | if self.color != line.color:
140 | return False
141 | return True
142 |
143 | class LineMatrix:
144 | def __init__(self, table, type):
145 | # befor set_bounds
146 | self.table = table
147 | self.x_max = table.x2 - 1
148 | self.y_max = table.y2 - 1
149 | # for hlines
150 | self.max_width = []
151 | # for vlines
152 | self.vline_max_width = []
153 | self.borders = [[Line(table, (i, j))
154 | for j in range(self.y_max)]
155 | for i in range(self.x_max)]
156 |
157 | # after set_props
158 | def set_lines(self, type):
159 | for i in range(self.x_max):
160 | max_w = 0
161 | for j in range(self.y_max):
162 | border = self.borders[i][j]
163 | border.set_line(self.table, i, j, type)
164 | max_w = max(border.width, max_w)
165 | self.max_width.append(max_w)
166 | for j in range(self.y_max):
167 | max_w = 0
168 | for i in range(self.x_max):
169 | border = self.borders[i][j]
170 | max_w = max(border.width, max_w)
171 | self.vline_max_width.append(max_w)
172 |
173 | def is_empty(self, line):
174 | for border in line:
175 | if border.style is not None:
176 | return False
177 | return True
178 |
179 | def is_full(self, line):
180 | style = line[0]
181 | for border in line:
182 | if not border.is_eql(style):
183 | return False
184 | return True
185 |
186 | def cut_range(self, hline):
187 | start_idx = len(hline) - 1
188 | end_idx = 0
189 | for i in range(len(hline)):
190 | if hline[i].style:
191 | start_idx = i
192 | break
193 | for i in range(len(hline) - 1, -1, -1):
194 | if hline[i].style:
195 | end_idx = i
196 | break
197 | return start_idx, end_idx
198 |
199 | def get_hline_range(self, hline):
200 | start_idx, end_idx = self.cut_range(hline)
201 | # one line
202 | if start_idx == end_idx:
203 | return [ClineRange(start_idx, end_idx, hline[0])]
204 | # none line
205 | if end_idx == 0:
206 | return []
207 | i = start_idx
208 | start = start_idx
209 | end = end_idx
210 | res = []
211 | pre = Line(self.table, (-1, -1))
212 | for border in hline[start_idx:end_idx + 1]:
213 | if not border.is_eql(pre):
214 | # previous group end
215 | if i > start_idx and pre.style:
216 | end = i - 1
217 | res.append(ClineRange(start + 1, end + 1, pre))
218 | # new group begin
219 | if border.style:
220 | start = i
221 | # current group end
222 | if i == end_idx:
223 | res.append(ClineRange(start + 1, i + 1, border))
224 | pre = copy(border)
225 | i += 1
226 | return res
227 |
228 | def get_hline_tex(self, hline_range):
229 | tex = ''
230 | style = hline_range.style
231 | if style.is_dash:
232 | tex += '\\cdashline{' + str(hline_range.start) + '-' + str(hline_range.end) + '}'
233 | elif not style.is_none:
234 | tex += '\\cmidrule[' + str(style.width) + 'pt' + ']{' + str(hline_range.start) + '-' + str(hline_range.end) + '}'
235 | if style.is_colored:
236 | tex = '\\colorwrap{' + style.color + '}' + '{' + tex + '}'
237 | # not support
238 | # if style.line_number == 2:
239 | # tex = tex + '\n' + tex
240 | return tex
241 |
242 | # not used
243 | def set_vspace(self):
244 | for hline in self.borders:
245 | hline_ranges = self.get_hline_range(hline)
246 | max_width = 0
247 | for hline_range in hline_ranges:
248 | if not hline_range.style.is_dash:
249 | max_width = max(0.4, max_width, hline_range.style.width)
250 | self.table.vspace += max_width
251 |
252 | # not used
253 | def get_row_hline_tex(self, hline):
254 | tex = ''
255 | hline_ranges = self.get_hline_range(hline)
256 | if not hline_ranges:
257 | return tex
258 | # max_width = 0
259 | for hline_range in hline_ranges:
260 | # if not hline_range.style.is_dash:
261 | # max_width = max(0.4, max_width, hline_range.style.width)
262 | tex += self.get_hline_tex(hline_range) + '\n'
263 | # self.table.vspace += max_width
264 | return tex
265 |
--------------------------------------------------------------------------------
/src/output.py:
--------------------------------------------------------------------------------
1 | from .help import wrap_excl, wrap_ge
2 |
3 | class OutputBase:
4 | def __init__(self, table):
5 | self.table = table
6 | self.cells = table.cells
7 | self.hlines = table.hlines.borders
8 | self.vlines = table.vlines.borders
9 | self.max_width = table.hlines.max_width
10 | self.vline_max_width = table.vlines.vline_max_width
11 |
12 | def get_vline(self, vline):
13 | if vline.is_none or vline.ignored:
14 | return ''
15 | return f'\\vsl{{{vline.color}}}{{{vline.width}pt}}'
16 |
17 | class OutputHhline(OutputBase):
18 | def __init__(self, table):
19 | super().__init__(table)
20 |
21 | def get_tbvline(self, i, j):
22 | '''
23 | get top right and bottom right vlines of hlines[i][j]
24 | '''
25 | tvline = None
26 | bvline = None
27 | if i > self.table.x1 - 1:
28 | tvline = self.vlines[i - 1][j + 1]
29 | if i < self.table.x2:
30 | bvline = self.vlines[i][j + 1]
31 | return tvline, bvline
32 |
33 | def get_hhline(self, i):
34 | res = '\\hhline{\n'
35 | for j in range(self.table.y1 - 1, self.table.y2):
36 | rhline = self.hlines[i][j]
37 | lhline = rhline.get_lhline()
38 | # first vhline
39 | if j == self.table.y1 - 1:
40 | tvline, bvline = self.get_tbvline(i, j - 1)
41 | res += self.get_vhline(lhline, rhline, tvline, bvline)
42 | res += self.get_hline(i, j)
43 | tvline, bvline = self.get_tbvline(i, j)
44 | res += self.get_vhline(lhline, rhline, tvline, bvline)
45 | res += '}\n'
46 | return res
47 |
48 | def has_style(self, vline):
49 | if vline is None:
50 | return False
51 | return not vline.is_none
52 |
53 | # used when 't' and 'b' has only one 'true'
54 | # def get_single_vline(self, vline, vline_cal_width):
55 | # lcell = vline.get_cell('l')
56 | # rcell = vline.get_cell('r')
57 | # lcolor = 'white'
58 | # rcolor = 'white'
59 | # if lcell is not None:
60 | # lcolor = lcell.color
61 | # if rcell is not None:
62 | # rcolor = rcell.color
63 | # l = lcolor == 'white'
64 | # r = rcolor == 'white'
65 | # if l and r:
66 | # return ''
67 | # return f'\\vsl{{{rcolor}}}{{{vline.width}pt}}'
68 |
69 | def get_vhline(self, lhline, rhline, tvline, bvline):
70 | l, r, t, b = (self.has_style(vline) for vline in (lhline, rhline, tvline, bvline))
71 | if not t and not b:
72 | return ''
73 | elif t and not b:
74 | res = self.get_vline(tvline)
75 | elif not t and b:
76 | res = self.get_vline(bvline)
77 | else:
78 | lcolor = 'white'
79 | rcolor = 'white'
80 | if l:
81 | lcolor = lhline.color
82 | if r:
83 | rcolor = rhline.color
84 | bcolor = bvline.color
85 | if bcolor == lcolor or bcolor == rcolor:
86 | res = self.get_vline(bvline)
87 | else:
88 | res = self.get_vline(tvline)
89 | return wrap_ge(res) if res else res
90 |
91 | def get_hline(self, i, j):
92 | hline = self.hlines[i][j]
93 | max_w = self.max_width[i]
94 | if hline.first_hline:
95 | return self.get_base_hline(max_w, hline)
96 | cell = self.cells[i - 1][j]
97 | cell_color = cell.color
98 | if hline.ignored:
99 | return self.get_ignored_hline(max_w, hline, cell_color)
100 | return self.get_other_hline(max_w, hline, cell_color)
101 |
102 | # 没有样式的横线,对半填充
103 | def get_none_hline(self, max_w, hline):
104 | if not max_w:
105 | return ' ~\n'
106 | width = max_w / 2
107 | tcell = hline.get_cell('t')
108 | bcell = hline.get_cell('b')
109 | tcolor = None
110 | bcolor = None
111 | if tcell is not None and tcell.color != 'white':
112 | tcolor = tcell.color
113 | if bcell is not None and bcell.color != 'white':
114 | bcolor = bcell.color
115 | if tcolor is None and bcolor is None:
116 | return ' ~\n'
117 | if tcolor is None and bcolor is not None:
118 | res = self.sfill(bcolor, width)
119 | if bcolor is None:
120 | bcolor = 'white'
121 | res = self.ssfill(tcolor, width, bcolor, width)
122 | return wrap_excl(res)
123 |
124 | def get_base_hline(self, max_w, hline):
125 | if hline.is_none:
126 | # return ' ~\n'
127 | return self.get_none_hline(max_w, hline)
128 | color = hline.color if hline.is_colored else 'black'
129 | if hline.is_dash:
130 | res = self.dfill(color, hline.get_dash_width(), hline.width, hline.format_dash())
131 | return wrap_excl(res)
132 | res = self.sfill(hline.color, hline.width)
133 | return wrap_excl(res)
134 |
135 | def get_other_hline(self, max_w, hline, cell_color):
136 | if cell_color == 'white':
137 | return self.get_base_hline(max_w, hline)
138 | if hline.is_none:
139 | # return ' ~\n'
140 | return self.get_none_hline(max_w, hline)
141 | div = max_w - hline.width
142 | common_width = hline.get_dash_width()
143 | res = None
144 | if div and hline.is_dash:
145 | res = self.sdfill(cell_color, div, common_width, hline.color, hline.width, hline.format_dash())
146 | if not div and hline.is_dash:
147 | res = self.dfill(hline.color, common_width, hline.width, hline.format_dash())
148 | if div and not hline.is_dash:
149 | res = self.ssfill(cell_color, div, hline.color, hline.width)
150 | if not div and not hline.is_dash:
151 | res = self.sfill(hline.color, hline.width)
152 | return wrap_excl(res)
153 |
154 | def get_ignored_hline(self, max_width, hline, cell_color):
155 | if cell_color == 'white':
156 | return ' ~\n'
157 | res = self.sfill(cell_color, max_width)
158 | return wrap_excl(res)
159 |
160 | def dfill(self, color, width, height, style):
161 | return f'\\dfill{{{color}}}{{{width}}}{{{height}pt}}{{{style}}}'
162 |
163 | def sfill(self, color, height):
164 | return f'\\sfill{{{color}}}{{{height}pt}}'
165 |
166 | def ssfill(self, tcolor, theight, bcolor, bheight):
167 | return f'\\ssfill{{{tcolor}}}{{{theight}pt}}{{{bcolor}}}{{{bheight}pt}}'
168 |
169 | def sdfill(self, tcolor, theight, common_width, bcolor, bheight, style):
170 | return f'\\sdfill{{{tcolor}}}{{{theight}pt}}{{{common_width}}}{{{bcolor}}}{{{bheight}pt}}{{{style}}}'
171 |
172 | class OutputCell(OutputBase):
173 | def __init__(self, table):
174 | super().__init__(table)
175 |
176 | def get_row(self, i):
177 | row_tex = ''
178 | vlines = self.vlines[i]
179 | for j in range(self.table.y1 - 1, self.table.y2):
180 | cell = self.cells[i][j]
181 | lvline = vlines[j]
182 | rvline = cell.get_rvline()
183 | if not cell.ignored:
184 | row_tex += self.wrap_cell(cell, self.get_cell(cell, lvline, rvline))
185 | row_tex += '\\\\\n'
186 | return row_tex
187 |
188 | def wrap_cell(self, cell, tex):
189 | before = ' '
190 | if not cell.begin:
191 | before += '&'
192 | if tex:
193 | before += ' '
194 | return before + tex
195 |
196 | def get_cell(self, cell, lvline, rvline):
197 | left = '' if not cell.begin else self.get_cell_vline(lvline)
198 | right = self.get_cell_vline(rvline)
199 | align = self.get_align(cell)
200 | return f'\\multicolumn{{{cell.width}}}{{{left}{align}{right}}}{{{self.get_col(cell)}}}\n'
201 |
202 | def get_align(self, cell):
203 | if cell.merged_idx and not cell.control_cell:
204 | align = 'c'
205 | else:
206 | align = cell.align
207 | before = ''
208 | prop = cell.text_prop
209 | if prop.color != '000000':
210 | before += f'\\color{{{prop.color}}}'
211 | if prop.i:
212 | before += '\\itshape'
213 | if prop.b:
214 | before += '\\bfseries'
215 | if before:
216 | before = f'>{{{before}}}'
217 | return before + align
218 |
219 | def get_cell_vline(self, vline):
220 | res = self.get_vline(vline)
221 | return f'!{{{res}}}' if res else res
222 | # if vline.is_none:
223 | # lcell = vline.get_cell('l')
224 | # rcell = vline.get_cell('r')
225 | # lcolor = 'white'
226 | # rcolor = 'white'
227 | # if lcell is not None:
228 | # lcolor = lcell.color
229 | # if rcell is not None:
230 | # rcolor = rcell.color
231 | # l = lcolor == 'white'
232 | # r = rcolor == 'white'
233 | # if l and r:
234 | # return ''
235 | # x, y = vline.idx
236 | # width = self.vline_max_width[y] / 2
237 | # res = f'\\vsl{{{lcolor}}}{{{width}pt}}'
238 | # res += f'\\vsl{{{rcolor}}}{{{width}pt}}'
239 | # else:
240 | # res = self.get_vline(vline)
241 | # return f'!{{{res}}}' if res else res
242 |
243 | def get_col(self, cell):
244 | res = ''
245 | text = cell.text_prop.text
246 | if cell.color != 'white':
247 | res += f'\\cellcolor{{{cell.color}}}'
248 | if cell.control_cell:
249 | if cell.one_row:
250 | res += text
251 | else:
252 | res += f'\n \\multirow{{-{cell.height}}}*{{{text}}}'
253 | elif not cell.merged_idx:
254 | res += text
255 | return res
256 |
257 | class Output:
258 | def __init__(self, table):
259 | self.table = table
260 | self.args = table.args
261 | hhline = OutputHhline(table)
262 | outputcell = OutputCell(table)
263 | tex = ''
264 | tex += hhline.get_hhline(0)
265 | for i in range(table.x1 - 1, table.x2):
266 | tex += f'% row {i - table.x1 + 2}\n'
267 | tex += outputcell.get_row(i)
268 | tex += hhline.get_hhline(i + 1)
269 | self.tex = self.wrap_tex(tex)
270 |
271 | def wrap_tex(self, tex):
272 | width = self.table.y2 - self.table.y1 + 1
273 | before = f'\\begin{{tabular}}{{*{{{width}}}{{c}}}}\n'
274 | end = f'\\end{{tabular}}'
275 | return before + tex + end
276 |
--------------------------------------------------------------------------------
/src/setting.py:
--------------------------------------------------------------------------------
1 | class Setting:
2 | def __init__(self, table):
3 | self.args = table.args
4 | self.output_setting(table.colors)
5 |
6 | def output_setting(self, colors):
7 | out = self.get_setting() + self.get_color(colors)
8 | with open(self.args.setting, 'w', encoding=self.args.encoding) as f:
9 | f.write(out)
10 |
11 | def get_color(self, colors):
12 | res = ''
13 | for color in colors:
14 | res += self.set_color(color)
15 | return res
16 |
17 | def set_color(self, color):
18 | return f'\\definecolor{{{color}}}{{HTML}}{{{color}}}\n'
19 |
20 | def get_setting(self):
21 | return r'''\usepackage[table]{xcolor}
22 | \usepackage{multirow}
23 | \usepackage{colortbl}
24 | \usepackage{dashrule}
25 | \usepackage{ehhline}
26 |
27 | %% nested tabular
28 | \newcommand{\minitab}[2][l]{\begin{tabular}{@{}#1@{}}#2\end{tabular}}
29 |
30 | %% vertical line
31 | % vertical colored line #1 color #2 width
32 | \newcommand{\vsl}[2]{\color{#1}\vrule width #2}
33 | % doubled vline
34 | % #1 first color #2 first width #3 sep #4 second color #5 second sep
35 | \newcommand{\dvsl}[5]{%
36 | \vsl{#1}{#2}\hspace{#3}\vsl{#4}{#5}%
37 | }
38 |
39 | %% horizontal line
40 | % colored solid line pattern
41 | % #1 color #2 width #3 height
42 | \newcommand{\hsp}[3]{\hbox{\textcolor{#1}{\rule{#2}{#3}}}}
43 | % colored dash line pattern
44 | % #1 color #2 width #3 height #4 style
45 | \newcommand{\hdp}[4]{\hbox{\textcolor{#1}{\hdashrule{#2}{#3}{#4}}}}
46 | % fill line
47 | % #1 top fill #2 bottom fill
48 | \newcommand{\leaderfill}[1]{%
49 | \xleaders\hbox{%
50 | \vbox{\baselineskip=0pt\lineskip=0pt#1}%
51 | }\hfill%
52 | }
53 | % top: solid, bottom: solid
54 | % #1 top color #2 top height #3 bottom color#4 bottom height
55 | \newcommand{\ssfill}[4]{%
56 | \leaderfill{\hsp{#1}{0.1pt}{#2}\hsp{#3}{0.1pt}{#4}}%
57 | }
58 | % top: solid, bottom: dashed
59 | % #1 top color, #2 top height, #3 common width to expand
60 | % #4 bottom color #5 bottom height, #6 bottom dash line style
61 | \newcommand{\sdfill}[6]{%
62 | \leaderfill{\hsp{#1}{#3}{#2}\hdp{#4}{#3}{#5}{#6}}%
63 | }
64 | % single solid
65 | % #1 color #2 height
66 | \newcommand{\sfill}[2]{%
67 | \leaderfill{\hsp{#1}{0.1pt}{#2}}%
68 | }
69 | % single dash
70 | % #1 color #2 width #3 height #4 style
71 | \newcommand{\dfill}[4]{%
72 | \leaderfill{\hdp{#1}{#2}{#3}{#4}}%
73 | }
74 | '''
75 |
--------------------------------------------------------------------------------
|