Merge pull request 'feature/translation-turk-eng' (#2) from feature/translation-turk-eng into master

Reviewed-on: #2
This commit is contained in:
Şhah İSMAİL 2020-05-31 19:13:43 +00:00
commit e742b5680e
152 changed files with 198468 additions and 0 deletions

2
go.mod
View file

@ -1,3 +1,5 @@
module gullion/words_convert
go 1.14
require github.com/360EntSecGroup-Skylar/excelize/v2 v2.2.0

31
go.sum Normal file
View file

@ -0,0 +1,31 @@
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
github.com/360EntSecGroup-Skylar/excelize/v2 v2.2.0 h1:5DuRTdH6M8yPjvFfBkACVmuk7SoTzmaB8yM6KVqEhP8=
github.com/360EntSecGroup-Skylar/excelize/v2 v2.2.0/go.mod h1:Uwb0d1GgxJieUWZG5WylTrgQ2SrldfjagAxheU8W6MQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91 h1:gp02YctZuIPTk0t7qI+wvg3VQwTPyNmSGG6ZqOsjSL8=
github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

92
main.go Normal file
View file

@ -0,0 +1,92 @@
package main
import (
"fmt"
"strconv"
"github.com/360EntSecGroup-Skylar/excelize/v2"
)
func main() {
langHashMap := make(map[string]string)
populateLanguageHash(langHashMap)
convertTurkeyToEngLabel("credit-card-transactions/Domestic-and-international-Usage.xlsx",
"Sheet1", langHashMap)
// exchangeHandler := initExhangeData()
// println(exchangeHandler["Some_key"].usd)
}
func convertTurkeyToEngLabel(fileName string, sheetName string, langHash map[string]string) {
//create new file for label conversion
fileLabelConversion := excelize.NewFile()
index := fileLabelConversion.NewSheet(sheetName)
//read from base xls file
file, err := excelize.OpenFile(fileName)
if err != nil {
fmt.Println(err)
return
}
// Get all the rows in base file
rows, err := file.GetRows(sheetName)
for rowIndex, row := range rows {
convertColumnItemsTurkeyToEng(row, langHash)
cellName := "A" + strconv.Itoa(rowIndex+1)
fileLabelConversion.SetSheetRow(sheetName, cellName, &row)
}
// Set active sheet of the workbook.
fileLabelConversion.SetActiveSheet(index)
// Save xlsx file by the given path.
if err := fileLabelConversion.SaveAs("credit-card-transactions/Domestic-and-international-Usage-eng.xlsx"); err != nil {
fmt.Println(err)
}
}
func convertColumnItemsTurkeyToEng(row []string, langHash map[string]string) {
for i, item := range row {
if value, ok := langHash[item]; ok {
row[i] = value
}
}
}
func populateLanguageHash(langHash map[string]string) {
langHash["Dönem"] = "period"
langHash["OCAK"] = "january"
langHash["ŞUBAT"] = "february"
langHash["MART"] = "March"
langHash["1. DÖNEM"] = "1st Quarter"
langHash["NISAN"] = "April"
langHash["MAYIS"] = "May"
langHash["HAZIRAN"] = "June"
langHash["2. DÖNEM"] = "2nd Quarter"
langHash["TEMMUZ"] = "July"
langHash["AĞUSTOS"] = "August"
langHash["EYLÜL"] = "Sepember"
langHash["3. DÖNEM"] = "3rd Quarter"
langHash["EKIM"] = "October"
langHash["KASIM"] = "November"
langHash["ARALIK"] = "December"
langHash["4. DÖNEM"] = "4th Quarter"
langHash["2020 YILI"] = "2020 year"
langHash["2019 YILI"] = "2019 year"
langHash["2018 YILI"] = "2018 year"
langHash["2017 YILI"] = "2017 year"
langHash["2016 YILI"] = "2016 year"
langHash["2015 YILI"] = "2015 year"
langHash["2014 YILI"] = "2014 year"
langHash["2013 YILI"] = "2013 year"
langHash["2012 YILI"] = "2012 year"
langHash["2011 YILI"] = "2011 year"
langHash["İşlem Adedi"] = "Number of transaction over period"
langHash["İşlem Tutarı (Milyon TL)"] = "Volumn of transactions (Million TL)"
langHash["Alışveriş"] = "Purchase"
langHash["Nakit Çekme"] = "Cash"
langHash["Toplam"] = "Total"
// langHash[""] = ""
}

View file

@ -0,0 +1,5 @@
~$*.xlsx
test/Test*.xlsx
*.out
*.test
.idea

View file

@ -0,0 +1,26 @@
language: go
install:
- go get -d -t -v ./... && go build -v ./...
go:
- 1.11.x
- 1.12.x
- 1.13.x
- 1.14.x
os:
- linux
- osx
env:
jobs:
- GOARCH=amd64
- GOARCH=386
script:
- go vet ./...
- go test ./... -v -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xuri.me](https://xuri.me). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct][version]
[homepage]: https://www.contributor-covenant.org
[version]: https://www.contributor-covenant.org/version/2/0/code_of_conduct

View file

@ -0,0 +1,465 @@
# Contributing to excelize
Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
guidelines useful to experienced open source contributors. Finally, make sure
you read our [community guidelines](#community-guidelines) before you
start participating.
## Topics
* [Reporting Security Issues](#reporting-security-issues)
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-other-issues)
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
* [Community Guidelines](#community-guidelines)
## Reporting security issues
The excelize maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
Please **DO NOT** file a public issue, instead send your report privately to
[xuri.me](https://xuri.me).
Security reports are greatly appreciated and we will publicly thank you for it.
We currently do not offer a paid security bounty program, but are not
ruling it out in the future.
## Reporting other issues
A great way to contribute to the project is to send a detailed report when you
encounter an issue. We always appreciate a well-written, thorough bug report,
and will thank you for it!
Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
doesn't already include that problem or suggestion before submitting an issue.
If you find a match, you can use the "subscribe" button to get notified on
updates. Do *not* leave random "+1" or "I have this too" comments, as they
only clutter the discussion, and don't help resolving it. However, if you
have ways to reproduce the issue or have additional information that may help
resolving the issue, please leave a comment.
When reporting issues, always include the output of `go env`.
Also include the steps required to reproduce the problem if possible and
applicable. This information will help us review and fix your issue faster.
When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
Don't forget to remove sensitive data from your logfiles before posting (you can
replace those parts with "REDACTED").
## Quick contribution tips and guidelines
This section gives the experienced contributor some tips and guidelines.
### Pull requests are always welcome
Not sure if that typo is worth a pull request? Found a bug and know how to fix
it? Do it! We will appreciate it. Any significant improvement should be
documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
anybody starts working on it.
We are always thrilled to receive pull requests. We do our best to process them
quickly. If your pull request is not accepted on the first try,
don't get discouraged!
### Design and cleanup proposals
You can propose new designs for existing excelize features. You can also design
entirely new features. We really appreciate contributors who want to refactor or
otherwise cleanup our project.
We try hard to keep excelize lean and focused. Excelize can't do everything for
everybody. This means that we might decide against incorporating a new feature.
However, there might be a way to implement that feature *on top of* excelize.
### Conventions
Fork the repository and make changes on your fork in a feature branch:
* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
the issue.
* If it's a feature branch, create an enhancement issue to announce
your intentions, and name it XXXX-something where XXXX is the number of the
issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Run the full test on your branch before
submitting a pull request.
Update the documentation when creating or modifying features. Test your
documentation changes for clarity, concision, and correctness, as well as a
clean documentation build.
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
committing your changes. Most editors have plug-ins that do this automatically.
Pull request descriptions should be as clear as possible and include a reference
to all the issues that they address.
### Successful Changes
Before contributing large or high impact changes, make the effort to coordinate
with the maintainers of the project before submitting a pull request. This
prevents you from doing extra work that may or may not be merged.
Large PRs that are just submitted without any prior communication are unlikely
to be successful.
While pull requests are the methodology for submitting changes to code, changes
are much more likely to be accepted if they are accompanied by additional
engineering work. While we don't define this explicitly, most of these goals
are accomplished through communication of the design goals and subsequent
solutions. Often times, it helps to first state the problem before presenting
solutions.
Typically, the best methods of accomplishing this are to submit an issue,
stating the problem. This issue can include a problem statement and a
checklist with requirements. If solutions are proposed, alternatives should be
listed and eliminated. Even if the criteria for elimination of a solution is
frivolous, say so.
Larger changes typically work best with design documents. These are focused on
providing context to the design at the time the feature was conceived and can
inform future documentation contributions.
### Commit Messages
Commit messages must start with a capitalized and short summary
written in the imperative, followed by an optional, more detailed explanatory
text which is separated from the summary by an empty line.
Commit messages should follow best practices, including explaining the context
of the problem and how it was solved, including in caveats or follow up changes
required. They should tell the story of the change and provide readers
understanding of what led to it.
In practice, the best approach to maintaining a nice commit message is to
leverage a `git add -p` and `git commit --amend` to formulate a solid
changeset. This allows one to piece together a change, as information becomes
available.
If you squash a series of commits, don't just submit that. Re-write the commit
message, as if the series of commits was a single stroke of brilliance.
That said, there is no requirement to have a single commit for a PR, as long as
each commit tells the story. For example, if there is a feature that requires a
package, it might make sense to have the package in a separate commit then have
a subsequent commit that uses it.
Remember, you're telling part of the story with the commit message. Don't make
your chapter weird.
### Review
Code review comments may be added to your pull request. Discuss, then make the
suggested modifications and push additional commits to your feature branch. Post
a comment after pushing. New commits show up in the pull request automatically,
but the reviewers are notified only when you comment.
Pull requests must be cleanly rebased on top of master without multiple branches
mixed into the PR.
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
feature branch to update your pull request rather than `merge master`.
Before you make a pull request, squash your commits into logical units of work
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
set of patches that should be reviewed together: for example, upgrading the
version of a vendored dependency and taking advantage of its now available new
feature constitute two separate units of work. Implementing a new function and
calling it in another file constitute a single logical unit of work. The very
high majority of submissions should have a single commit, so if in doubt: squash
down to one.
After every commit, make sure the test passes. Include documentation
changes in the same pull request so that a revert would remove all traces of
the feature or fix.
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
close an issue. Including references automatically closes the issue on a merge.
Please see the [Coding Style](#coding-style) for further guidelines.
### Merge approval
The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
indicate acceptance.
### Sign your work
The sign-off is a simple line at the end of the explanation for the patch. Your
signature certifies that you wrote the patch or otherwise have the right to pass
it on as an open-source patch. The rules are pretty simple: if you can certify
the below (from [developercertificate.org](http://developercertificate.org/)):
```text
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
Then you just add a line to every git commit message:
```text
Signed-off-by: Ri Xu https://xuri.me
```
Use your real name (sorry, no pseudonyms or anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
### How can I become a maintainer
First, all maintainers have 3 things
* They share responsibility in the project's success.
* They have made a long-term, recurring time investment to improve the project.
* They spend that time doing whatever needs to be done, not necessarily what
is the most interesting or fun.
Maintainers are often under-appreciated, because their work is harder to appreciate.
It's easy to appreciate a really cool and technically advanced feature. It's harder
to appreciate the absence of bugs, the slow but steady improvement in stability,
or the reliability of a release process. But those things distinguish a good
project from a great one.
Don't forget: being a maintainer is a time investment. Make sure you
will have time to make yourself available. You don't have to be a
maintainer to make a difference on the project!
If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
## Community guidelines
We want to keep the community awesome, growing and collaborative. We need
your help to keep it that way. To help with this we've come up with some general
guidelines for the community as a whole:
* Be nice: Be courteous, respectful and polite to fellow community members:
no regional, racial, gender, or other abuse will be tolerated. We like
nice people way better than mean ones!
* Encourage diversity and participation: Make everyone in our community feel
welcome, regardless of their background and the extent of their
contributions, and do everything possible to encourage participation in
our community.
* Keep it legal: Basically, don't get us in trouble. Share only content that
you own, do not share private or sensitive information, and don't break
the law.
* Stay on topic: Make sure that you are posting to the correct channel and
avoid off-topic discussions. Remember when you update an issue or respond
to an email you are potentially sending to a large number of people. Please
consider this before you update. Also remember that nobody likes spam.
* Don't send email to the maintainers: There's no need to send email to the
maintainers to ask them to investigate an issue or to take a look at a
pull request. Instead of sending an email, GitHub mentions should be
used to ping maintainers to review a pull request, a proposal or an
issue.
### Guideline violations — 3 strikes method
The point of this section is not to find opportunities to punish people, but we
do need a fair way to deal with people who are making our community suck.
1. First occurrence: We'll give you a friendly, but public reminder that the
behavior is inappropriate according to our guidelines.
2. Second occurrence: We will send you a private message with a warning that
any additional violations will result in removal from the community.
3. Third occurrence: Depending on the violation, we may need to delete or ban
your account.
**Notes:**
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
have spam all over the place.
* Violations are forgiven after 6 months of good behavior, and we won't hold a
grudge.
* People who commit minor infractions will get some education, rather than
hammering them in the 3 strikes process.
* The rules apply equally to everyone in the community, no matter how much
you've contributed.
* Extreme violations of a threatening, abusive, destructive or illegal nature
will be addressed immediately and are not subject to 3 strikes or forgiveness.
* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
appeals, we know that mistakes happen, and we'll work with you to come up with a
fair solution if there has been a misunderstanding.
## Coding Style
Unless explicitly stated, we follow all coding guidelines from the Go
community. While some of these standards may seem arbitrary, they somehow seem
to result in a solid, consistent codebase.
It is possible that the code base does not currently comply with these
guidelines. We are not looking for a massive PR that fixes this, since that
goes against the spirit of the guidelines. All new contributions should make a
best effort to clean up and make the code base better than they left it.
Obviously, apply your best judgement. Remember, the goal here is to make the
code base easier for humans to navigate and understand. Always keep that in
mind when nudging others to comply.
The rules:
1. All code should be formatted with `gofmt -s`.
2. All code should pass the default levels of
[`golint`](https://github.com/golang/lint).
3. All code should follow the guidelines covered in [Effective
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
4. Comment the code. Tell us the why, the history and the context.
5. Document _all_ declarations and methods, even private ones. Declare
expectations, caveats and anything else that may be important. If a type
gets exported, having the comments already there will ensure it's ready.
6. Variable name length should be proportional to its context and no longer.
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
In practice, short methods will have short variable names and globals will
have longer names.
7. No underscores in package names. If you need a compound name, step back,
and re-examine why you need a compound name. If you still think you need a
compound name, lose the underscore.
8. No utils or helpers packages. If a function is not general enough to
warrant its own package, it has not been written generally enough to be a
part of a util package. Just leave it unexported and well-documented.
9. All tests should run with `go test` and outside tooling should not be
required. No, we don't need another unit testing framework. Assertion
packages are acceptable if they provide _real_ incremental value.
10. Even though we call these "rules" above, they are actually just
guidelines. Since you've read all the rules, you now know that.
If you are having trouble getting into the mood of idiomatic Go, we recommend
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
kool-aid is a lot easier than going thirsty.
## Code Review Comments and Effective Go Guidelines
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
### Package Comment
Every package should have a package comment, a block comment preceding the package clause.
For multi-file packages, the package comment only needs to be present in one file, and any one will do.
The package comment should introduce the package and provide information relevant to the package as a
whole. It will appear first on the godoc page and should set up the detailed documentation that follows.
### Single Method Interface Name
By convention, one-method interfaces are named by the method name plus an -er suffix
or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
There are a number of such names and it's productive to honor them and the function names they capture.
Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
don't give your method one of those names unless it has the same signature and meaning. Conversely,
if your type implements a method with the same meaning as a method on a well-known type, give it the
same name and signature; call your string-converter method String not ToString.
### Avoid Annotations in Comments
Comments do not need extra formatting such as banners of stars. The generated output
may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
does do is to display indented text in a fixed-width font, suitable for program snippets.
The package comment for the fmt package uses this to good effect.
### Comment First Word as Subject
Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
The first sentence should be a one-sentence summary that starts with the name being declared.
### Good Package Name
It's helpful if everyone using the package can use the same name
to refer to its contents, which implies that the package name should
be good: short, concise, evocative. By convention, packages are
given lower case, single-word names; there should be no need for
underscores or mixedCaps. Err on the side of brevity, since everyone
using your package will be typing that name. And don't worry about
collisions a priori. The package name is only the default name for
imports; it need not be unique across all source code, and in the
rare case of a collision the importing package can choose a different
name to use locally. In any case, confusion is rare because the file
name in the import determines just which package is being used.
### Avoid Renaming Imports
Avoid renaming imports except to avoid a name collision; good package names
should not require renaming. In the event of collision, prefer to rename the
most local or project-specific import.
### Context as First Argument
Values of the context.Context type carry security credentials, tracing information,
deadlines, and cancellation signals across API and process boundaries. Go programs
pass Contexts explicitly along the entire function call chain from incoming RPCs
and HTTP requests to outgoing requests.
Most functions that use a Context should accept it as their first parameter.
### Do Not Discard Errors
Do not discard errors using _ variables. If a function returns an error,
check it to make sure the function succeeded. Handle the error, return it, or,
in truly exceptional situations, panic.
### Go Error Format
Error strings should not be capitalized (unless beginning with proper nouns
or acronyms) or end with punctuation, since they are usually printed following
other context. That is, use fmt.Errorf("something bad") not fmt.Errorf("Something bad"),
so that log.Printf("Reading %s: %v", filename, err) formats without a spurious
capital letter mid-message. This does not apply to logging, which is implicitly
line-oriented and not combined inside other messages.
### Use Crypto Rand
Do not use package math/rand to generate keys, even
throwaway ones. Unseeded, the generator is completely predictable.
Seeded with time.Nanoseconds(), there are just a few bits of entropy.
Instead, use crypto/rand's Reader, and if you need text, print to
hexadecimal or base64.

View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2016-2020 The excelize Authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,45 @@
# PR Details
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
## How Has This Been Tested
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Docs change / refactoring / dependency upgrade
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

View file

@ -0,0 +1,183 @@
<p align="center"><img width="650" src="./excelize.svg" alt="Excelize logo"></p>
<p align="center">
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
<a href="https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc"><img src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white" alt="go.dev"></a>
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
</p>
# Excelize
## Introduction
Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLSX / XLSM / XLTM files. Supports reading and writing spreadsheet documents generated by Microsoft Excel&trade; 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.10 or later. The full API docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc) and [docs reference](https://xuri.me/excelize/).
## Basic Usage
### Installation
```bash
go get github.com/360EntSecGroup-Skylar/excelize
```
- If your package management with [Go Modules](https://blog.golang.org/using-go-modules), please install with following command.
```bash
go get github.com/360EntSecGroup-Skylar/excelize/v2
```
### Create XLSX file
Here is a minimal example usage that will create XLSX file.
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f := excelize.NewFile()
// Create a new sheet.
index := f.NewSheet("Sheet2")
// Set value of a cell.
f.SetCellValue("Sheet2", "A2", "Hello world.")
f.SetCellValue("Sheet1", "B2", 100)
// Set active sheet of the workbook.
f.SetActiveSheet(index)
// Save xlsx file by the given path.
if err := f.SaveAs("Book1.xlsx"); err != nil {
fmt.Println(err)
}
}
```
### Reading XLSX file
The following constitutes the bare to read a XLSX document.
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f, err := excelize.OpenFile("Book1.xlsx")
if err != nil {
fmt.Println(err)
return
}
// Get value from cell by given worksheet name and axis.
cell, err := f.GetCellValue("Sheet1", "B2")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(cell)
// Get all the rows in the Sheet1.
rows, err := f.GetRows("Sheet1")
for _, row := range rows {
for _, colCell := range row {
fmt.Print(colCell, "\t")
}
fmt.Println()
}
}
```
### Add chart to XLSX file
With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
f := excelize.NewFile()
for k, v := range categories {
f.SetCellValue("Sheet1", k, v)
}
for k, v := range values {
f.SetCellValue("Sheet1", k, v)
}
if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`); err != nil {
fmt.Println(err)
return
}
// Save xlsx file by the given path.
if err := f.SaveAs("Book1.xlsx"); err != nil {
fmt.Println(err)
}
}
```
### Add picture to XLSX file
```go
package main
import (
"fmt"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f, err := excelize.OpenFile("Book1.xlsx")
if err != nil {
fmt.Println(err)
return
}
// Insert a picture.
if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
fmt.Println(err)
}
// Insert a picture to worksheet with scaling.
if err := f.AddPicture("Sheet1", "D2", "image.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
fmt.Println(err)
}
// Insert a picture offset in the cell with printing support.
if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`); err != nil {
fmt.Println(err)
}
// Save the xlsx file with the origin path.
if err = f.Save(); err != nil {
fmt.Println(err)
}
}
```
## Contributing
Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change. XML is compliant with [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm).
## Licenses
This program is under the terms of the BSD 3-Clause License. See [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause).
The Excel logo is a trademark of [Microsoft Corporation](https://aka.ms/trademarks-usage). This artwork is an adaptation.
gopher.{ai,svg,png} was created by [Takuya Ueda](https://twitter.com/tenntenn). Licensed under the [Creative Commons 3.0 Attributions license](http://creativecommons.org/licenses/by/3.0/).

View file

@ -0,0 +1,183 @@
<p align="center"><img width="650" src="./excelize.svg" alt="Excelize logo"></p>
<p align="center">
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
<a href="https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc"><img src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white" alt="go.dev"></a>
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
</p>
# Excelize
## 简介
Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel&trade; 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写 API用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.10 或更高版本,完整的 API 使用文档请访问 [go.dev](https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc) 或查看 [参考文档](https://xuri.me/excelize/)。
## 快速上手
### 安装
```bash
go get github.com/360EntSecGroup-Skylar/excelize
```
- 如果您使用 [Go Modules](https://blog.golang.org/using-go-modules) 管理软件包,请使用下面的命令来安装最新版本。
```bash
go get github.com/360EntSecGroup-Skylar/excelize/v2
```
### 创建 Excel 文档
下面是一个创建 Excel 文档的简单例子:
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f := excelize.NewFile()
// 创建一个工作表
index := f.NewSheet("Sheet2")
// 设置单元格的值
f.SetCellValue("Sheet2", "A2", "Hello world.")
f.SetCellValue("Sheet1", "B2", 100)
// 设置工作簿的默认工作表
f.SetActiveSheet(index)
// 根据指定路径保存文件
if err := f.SaveAs("Book1.xlsx"); err != nil {
fmt.Println(err)
}
}
```
### 读取 Excel 文档
下面是读取 Excel 文档的例子:
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f, err := excelize.OpenFile("Book1.xlsx")
if err != nil {
fmt.Println(err)
return
}
// 获取工作表中指定单元格的值
cell, err := f.GetCellValue("Sheet1", "B2")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(cell)
// 获取 Sheet1 上所有单元格
rows, err := f.GetRows("Sheet1")
for _, row := range rows {
for _, colCell := range row {
fmt.Print(colCell, "\t")
}
fmt.Println()
}
}
```
### 在 Excel 文档中创建图表
使用 Excelize 生成图表十分简单,仅需几行代码。您可以根据工作表中的已有数据构建图表,或向工作表中添加数据并创建图表。
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
```go
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
f := excelize.NewFile()
for k, v := range categories {
f.SetCellValue("Sheet1", k, v)
}
for k, v := range values {
f.SetCellValue("Sheet1", k, v)
}
if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`); err != nil {
fmt.Println(err)
return
}
// 根据指定路径保存文件
if err := f.SaveAs("Book1.xlsx"); err != nil {
fmt.Println(err)
}
}
```
### 向 Excel 文档中插入图片
```go
package main
import (
"fmt"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f, err := excelize.OpenFile("Book1.xlsx")
if err != nil {
fmt.Println(err)
return
}
// 插入图片
if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
fmt.Println(err)
}
// 在工作表中插入图片,并设置图片的缩放比例
if err := f.AddPicture("Sheet1", "D2", "image.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
fmt.Println(err)
}
// 在工作表中插入图片,并设置图片的打印属性
if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`); err != nil {
fmt.Println(err)
}
// 保存文件
if err = f.Save(); err != nil {
fmt.Println(err)
}
}
```
## 社区合作
欢迎您为此项目贡献代码,提出建议或问题、修复 Bug 以及参与讨论对新功能的想法。 XML 符合标准: [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm)。
## 开源许可
本项目遵循 BSD 3-Clause 开源许可协议,访问 [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 查看许可协议文件。
Excel 徽标是 [Microsoft Corporation](https://aka.ms/trademarks-usage) 的商标,项目的图片是一种改编。
gopher.{ai,svg,png} 由 [Takuya Ueda](https://twitter.com/tenntenn) 创作,遵循 [Creative Commons 3.0 Attributions license](http://creativecommons.org/licenses/by/3.0/) 创作共用授权条款。

View file

@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
We will dive into any security-related issue as long as your Excelize version is still supported by us. When reporting an issue, include as much information as possible, but no need to fill fancy forms or answer tedious questions. Just tell us what you found, how to reproduce it, and any concerns you have about it. We will respond as soon as possible and follow up with any missing information.
## Reporting a Vulnerability
Please e-mail us directly at `xuri.me@gmail.com` or use the security issue template on GitHub. In general, public disclosure is made after the issue has been fully identified and a patch is ready to be released. A security issue gets the highest priority assigned and a reply regarding the vulnerability is given within a typical 24 hours. Thank you!

View file

@ -0,0 +1,334 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"errors"
"strings"
)
type adjustDirection bool
const (
columns adjustDirection = false
rows adjustDirection = true
)
// adjustHelper provides a function to adjust rows and columns dimensions,
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
// columns.
//
// sheet: Worksheet name that we're editing
// column: Index number of the column we're inserting/deleting before
// row: Index number of the row we're inserting/deleting before
// offset: Number of rows/column to insert/delete negative values indicate deletion
//
// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
//
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if dir == rows {
f.adjustRowDimensions(xlsx, num, offset)
} else {
f.adjustColDimensions(xlsx, num, offset)
}
f.adjustHyperlinks(xlsx, sheet, dir, num, offset)
if err = f.adjustMergeCells(xlsx, dir, num, offset); err != nil {
return err
}
if err = f.adjustAutoFilter(xlsx, dir, num, offset); err != nil {
return err
}
if err = f.adjustCalcChain(dir, num, offset); err != nil {
return err
}
checkSheet(xlsx)
_ = checkRow(xlsx)
if xlsx.MergeCells != nil && len(xlsx.MergeCells.Cells) == 0 {
xlsx.MergeCells = nil
}
return nil
}
// adjustColDimensions provides a function to update column dimensions when
// inserting or deleting rows or columns.
func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
for rowIdx := range xlsx.SheetData.Row {
for colIdx, v := range xlsx.SheetData.Row[rowIdx].C {
cellCol, cellRow, _ := CellNameToCoordinates(v.R)
if col <= cellCol {
if newCol := cellCol + offset; newCol > 0 {
xlsx.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
}
}
}
}
}
// adjustRowDimensions provides a function to update row dimensions when
// inserting or deleting rows or columns.
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
for i := range xlsx.SheetData.Row {
r := &xlsx.SheetData.Row[i]
if newRow := r.R + offset; r.R >= row && newRow > 0 {
f.ajustSingleRowDimensions(r, newRow)
}
}
}
// ajustSingleRowDimensions provides a function to ajust single row dimensions.
func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) {
r.R = num
for i, col := range r.C {
colName, _, _ := SplitCellName(col.R)
r.C[i].R, _ = JoinCellName(colName, num)
}
}
// adjustHyperlinks provides a function to update hyperlinks when inserting or
// deleting rows or columns.
func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
// short path
if xlsx.Hyperlinks == nil || len(xlsx.Hyperlinks.Hyperlink) == 0 {
return
}
// order is important
if offset < 0 {
for rowIdx, linkData := range xlsx.Hyperlinks.Hyperlink {
colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
f.deleteSheetRelationships(sheet, linkData.RID)
if len(xlsx.Hyperlinks.Hyperlink) > 1 {
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:rowIdx],
xlsx.Hyperlinks.Hyperlink[rowIdx+1:]...)
} else {
xlsx.Hyperlinks = nil
}
}
}
}
if xlsx.Hyperlinks == nil {
return
}
for i := range xlsx.Hyperlinks.Hyperlink {
link := &xlsx.Hyperlinks.Hyperlink[i] // get reference
colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
if dir == rows {
if rowNum >= num {
link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
}
} else {
if colNum >= num {
link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
}
}
}
}
// adjustAutoFilter provides a function to update the auto filter when
// inserting or deleting rows or columns.
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
if xlsx.AutoFilter == nil {
return nil
}
coordinates, err := f.areaRefToCoordinates(xlsx.AutoFilter.Ref)
if err != nil {
return err
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {
xlsx.AutoFilter = nil
for rowIdx := range xlsx.SheetData.Row {
rowData := &xlsx.SheetData.Row[rowIdx]
if rowData.R > y1 && rowData.R <= y2 {
rowData.Hidden = false
}
}
return nil
}
coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if xlsx.AutoFilter.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
return err
}
return nil
}
// adjustAutoFilterHelper provides a function for adjusting auto filter to
// compare and calculate cell axis by the given adjust direction, operation
// axis and offset.
func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {
if dir == rows {
if coordinates[1] >= num {
coordinates[1] += offset
}
if coordinates[3] >= num {
coordinates[3] += offset
}
} else {
if coordinates[2] >= num {
coordinates[2] += offset
}
}
return coordinates
}
// areaRefToCoordinates provides a function to convert area reference to a
// pair of coordinates.
func (f *File) areaRefToCoordinates(ref string) ([]int, error) {
rng := strings.Split(ref, ":")
return areaRangeToCoordinates(rng[0], rng[1])
}
// areaRangeToCoordinates provides a function to convert cell range to a
// pair of coordinates.
func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) {
coordinates := make([]int, 4)
var err error
coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
if err != nil {
return coordinates, err
}
coordinates[2], coordinates[3], err = CellNameToCoordinates(lastCell)
return coordinates, err
}
// sortCoordinates provides a function to correct the coordinate area, such
// correct C1:B3 to B1:C3.
func sortCoordinates(coordinates []int) error {
if len(coordinates) != 4 {
return errors.New("coordinates length must be 4")
}
if coordinates[2] < coordinates[0] {
coordinates[2], coordinates[0] = coordinates[0], coordinates[2]
}
if coordinates[3] < coordinates[1] {
coordinates[3], coordinates[1] = coordinates[1], coordinates[3]
}
return nil
}
// coordinatesToAreaRef provides a function to convert a pair of coordinates
// to area reference.
func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
if len(coordinates) != 4 {
return "", errors.New("coordinates length must be 4")
}
firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1])
if err != nil {
return "", err
}
lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3])
if err != nil {
return "", err
}
return firstCell + ":" + lastCell, err
}
// adjustMergeCells provides a function to update merged cells when inserting
// or deleting rows or columns.
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
if xlsx.MergeCells == nil {
return nil
}
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
areaData := xlsx.MergeCells.Cells[i]
coordinates, err := f.areaRefToCoordinates(areaData.Ref)
if err != nil {
return err
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if dir == rows {
if y1 == num && y2 == num && offset < 0 {
f.deleteMergeCell(xlsx, i)
i--
}
y1 = f.adjustMergeCellsHelper(y1, num, offset)
y2 = f.adjustMergeCellsHelper(y2, num, offset)
} else {
if x1 == num && x2 == num && offset < 0 {
f.deleteMergeCell(xlsx, i)
i--
}
x1 = f.adjustMergeCellsHelper(x1, num, offset)
x2 = f.adjustMergeCellsHelper(x2, num, offset)
}
if x1 == x2 && y1 == y2 {
f.deleteMergeCell(xlsx, i)
i--
}
if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
return err
}
}
return nil
}
// adjustMergeCellsHelper provides a function for adjusting merge cells to
// compare and calculate cell axis by the given pivot, operation axis and
// offset.
func (f *File) adjustMergeCellsHelper(pivot, num, offset int) int {
if pivot >= num {
pivot += offset
if pivot < 1 {
return 1
}
return pivot
}
return pivot
}
// deleteMergeCell provides a function to delete merged cell by given index.
func (f *File) deleteMergeCell(sheet *xlsxWorksheet, idx int) {
if len(sheet.MergeCells.Cells) > idx {
sheet.MergeCells.Cells = append(sheet.MergeCells.Cells[:idx], sheet.MergeCells.Cells[idx+1:]...)
sheet.MergeCells.Count = len(sheet.MergeCells.Cells)
}
}
// adjustCalcChain provides a function to update the calculation chain when
// inserting or deleting rows or columns.
func (f *File) adjustCalcChain(dir adjustDirection, num, offset int) error {
if f.CalcChain == nil {
return nil
}
for index, c := range f.CalcChain.C {
colNum, rowNum, err := CellNameToCoordinates(c.R)
if err != nil {
return err
}
if dir == rows && num <= rowNum {
if newRow := rowNum + offset; newRow > 0 {
f.CalcChain.C[index].R, _ = CoordinatesToCellName(colNum, newRow)
}
}
if dir == columns && num <= colNum {
if newCol := colNum + offset; newCol > 0 {
f.CalcChain.C[index].R, _ = CoordinatesToCellName(newCol, rowNum)
}
}
}
return nil
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/xml"
"io"
"log"
)
// calcChainReader provides a function to get the pointer to the structure
// after deserialization of xl/calcChain.xml.
func (f *File) calcChainReader() *xlsxCalcChain {
var err error
if f.CalcChain == nil {
f.CalcChain = new(xlsxCalcChain)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))).
Decode(f.CalcChain); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
}
return f.CalcChain
}
// calcChainWriter provides a function to save xl/calcChain.xml after
// serialize structure.
func (f *File) calcChainWriter() {
if f.CalcChain != nil && f.CalcChain.C != nil {
output, _ := xml.Marshal(f.CalcChain)
f.saveFileList("xl/calcChain.xml", output)
}
}
// deleteCalcChain provides a function to remove cell reference on the
// calculation chain.
func (f *File) deleteCalcChain(index int, axis string) {
calc := f.calcChainReader()
if calc != nil {
calc.C = xlsxCalcChainCollection(calc.C).Filter(func(c xlsxCalcChainC) bool {
return !((c.I == index && c.R == axis) || (c.I == index && axis == ""))
})
}
if len(calc.C) == 0 {
f.CalcChain = nil
delete(f.XLSX, "xl/calcChain.xml")
content := f.contentTypesReader()
for k, v := range content.Overrides {
if v.PartName == "/xl/calcChain.xml" {
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
}
}
}
}
type xlsxCalcChainCollection []xlsxCalcChainC
// Filter provides a function to filter calculation chain.
func (c xlsxCalcChainCollection) Filter(fn func(v xlsxCalcChainC) bool) []xlsxCalcChainC {
var results []xlsxCalcChainC
for _, v := range c {
if fn(v) {
results = append(results, v)
}
}
return results
}

View file

@ -0,0 +1,826 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/xml"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
const (
// STCellFormulaTypeArray defined the formula is an array formula.
STCellFormulaTypeArray = "array"
// STCellFormulaTypeDataTable defined the formula is a data table formula.
STCellFormulaTypeDataTable = "dataTable"
// STCellFormulaTypeNormal defined the formula is a regular cell formula.
STCellFormulaTypeNormal = "normal"
// STCellFormulaTypeShared defined the formula is part of a shared formula.
STCellFormulaTypeShared = "shared"
)
// GetCellValue provides a function to get formatted value from cell by given
// worksheet name and axis in XLSX file. If it is possible to apply a format
// to the cell value, it will do so, if not then an error will be returned,
// along with the raw value of the cell.
func (f *File) GetCellValue(sheet, axis string) (string, error) {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
val, err := c.getValueFrom(f, f.sharedStringsReader())
if err != nil {
return val, false, err
}
return val, true, err
})
}
// SetCellValue provides a function to set value of a cell. The specified
// coordinates should not be in the first row of the table. The following
// shows the supported data types:
//
// int
// int8
// int16
// int32
// int64
// uint
// uint8
// uint16
// uint32
// uint64
// float32
// float64
// string
// []byte
// time.Duration
// time.Time
// bool
// nil
//
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
// set numbers format by SetCellStyle() method.
func (f *File) SetCellValue(sheet, axis string, value interface{}) error {
var err error
switch v := value.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
err = f.setCellIntFunc(sheet, axis, v)
case float32:
err = f.SetCellFloat(sheet, axis, float64(v), -1, 32)
case float64:
err = f.SetCellFloat(sheet, axis, v, -1, 64)
case string:
err = f.SetCellStr(sheet, axis, v)
case []byte:
err = f.SetCellStr(sheet, axis, string(v))
case time.Duration:
_, d := setCellDuration(v)
err = f.SetCellDefault(sheet, axis, d)
if err != nil {
return err
}
err = f.setDefaultTimeStyle(sheet, axis, 21)
case time.Time:
err = f.setCellTimeFunc(sheet, axis, v)
case bool:
err = f.SetCellBool(sheet, axis, v)
case nil:
err = f.SetCellStr(sheet, axis, "")
default:
err = f.SetCellStr(sheet, axis, fmt.Sprint(value))
}
return err
}
// setCellIntFunc is a wrapper of SetCellInt.
func (f *File) setCellIntFunc(sheet, axis string, value interface{}) error {
var err error
switch v := value.(type) {
case int:
err = f.SetCellInt(sheet, axis, v)
case int8:
err = f.SetCellInt(sheet, axis, int(v))
case int16:
err = f.SetCellInt(sheet, axis, int(v))
case int32:
err = f.SetCellInt(sheet, axis, int(v))
case int64:
err = f.SetCellInt(sheet, axis, int(v))
case uint:
err = f.SetCellInt(sheet, axis, int(v))
case uint8:
err = f.SetCellInt(sheet, axis, int(v))
case uint16:
err = f.SetCellInt(sheet, axis, int(v))
case uint32:
err = f.SetCellInt(sheet, axis, int(v))
case uint64:
err = f.SetCellInt(sheet, axis, int(v))
}
return err
}
// setCellTimeFunc provides a method to process time type of value for
// SetCellValue.
func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
var isNum bool
cellData.T, cellData.V, isNum, err = setCellTime(value)
if err != nil {
return err
}
if isNum {
err = f.setDefaultTimeStyle(sheet, axis, 22)
if err != nil {
return err
}
}
return err
}
func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
var excelTime float64
excelTime, err = timeToExcelTime(value)
if err != nil {
return
}
isNum = excelTime > 0
if isNum {
t, b = setCellDefault(strconv.FormatFloat(excelTime, 'f', -1, 64))
} else {
t, b = setCellDefault(value.Format(time.RFC3339Nano))
}
return
}
func setCellDuration(value time.Duration) (t string, v string) {
v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32)
return
}
// SetCellInt provides a function to set int type value of a cell by given
// worksheet name, cell coordinates and cell value.
func (f *File) SetCellInt(sheet, axis string, value int) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T, cellData.V = setCellInt(value)
return err
}
func setCellInt(value int) (t string, v string) {
v = strconv.Itoa(value)
return
}
// SetCellBool provides a function to set bool type value of a cell by given
// worksheet name, cell name and cell value.
func (f *File) SetCellBool(sheet, axis string, value bool) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T, cellData.V = setCellBool(value)
return err
}
func setCellBool(value bool) (t string, v string) {
t = "b"
if value {
v = "1"
} else {
v = "0"
}
return
}
// SetCellFloat sets a floating point value into a cell. The prec parameter
// specifies how many places after the decimal will be shown while -1 is a
// special value that will use as many decimal places as necessary to
// represent the number. bitSize is 32 or 64 depending on if a float32 or
// float64 was originally used for the value. For Example:
//
// var x float32 = 1.325
// f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32)
//
func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T, cellData.V = setCellFloat(value, prec, bitSize)
return err
}
func setCellFloat(value float64, prec, bitSize int) (t string, v string) {
v = strconv.FormatFloat(value, 'f', prec, bitSize)
return
}
// SetCellStr provides a function to set string type value of a cell. Total
// number of characters that a cell can contain 32767 characters.
func (f *File) SetCellStr(sheet, axis, value string) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T, cellData.V, cellData.XMLSpace = setCellStr(value)
return err
}
func setCellStr(value string) (t string, v string, ns xml.Attr) {
if len(value) > 32767 {
value = value[0:32767]
}
// Leading and ending space(s) character detection.
if len(value) > 0 && (value[0] == 32 || value[len(value)-1] == 32) {
ns = xml.Attr{
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
Value: "preserve",
}
}
t = "str"
v = value
return
}
// SetCellDefault provides a function to set string type value of a cell as
// default format without escaping the cell.
func (f *File) SetCellDefault(sheet, axis, value string) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T, cellData.V = setCellDefault(value)
return err
}
func setCellDefault(value string) (t string, v string) {
v = value
return
}
// GetCellFormula provides a function to get formula from cell by given
// worksheet name and axis in XLSX file.
func (f *File) GetCellFormula(sheet, axis string) (string, error) {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
if c.F == nil {
return "", false, nil
}
if c.F.T == STCellFormulaTypeShared {
return getSharedForumula(x, c.F.Si), true, nil
}
return c.F.Content, true, nil
})
}
// FormulaOpts can be passed to SetCellFormula to use other formula types.
type FormulaOpts struct {
Type *string // Formula type
Ref *string // Shared formula ref
}
// SetCellFormula provides a function to set cell formula by given string and
// worksheet name.
func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, _, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
if formula == "" {
cellData.F = nil
f.deleteCalcChain(f.getSheetID(sheet), axis)
return err
}
if cellData.F != nil {
cellData.F.Content = formula
} else {
cellData.F = &xlsxF{Content: formula}
}
for _, o := range opts {
if o.Type != nil {
cellData.F.T = *o.Type
}
if o.Ref != nil {
cellData.F.Ref = *o.Ref
}
}
return err
}
// GetCellHyperLink provides a function to get cell hyperlink by given
// worksheet name and axis. Boolean type value link will be ture if the cell
// has a hyperlink and the target is the address of the hyperlink. Otherwise,
// the value of link will be false and the value of the target will be a blank
// string. For example get hyperlink of Sheet1!H6:
//
// link, target, err := f.GetCellHyperLink("Sheet1", "H6")
//
func (f *File) GetCellHyperLink(sheet, axis string) (bool, string, error) {
// Check for correct cell name
if _, _, err := SplitCellName(axis); err != nil {
return false, "", err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return false, "", err
}
axis, err = f.mergeCellsParser(xlsx, axis)
if err != nil {
return false, "", err
}
if xlsx.Hyperlinks != nil {
for _, link := range xlsx.Hyperlinks.Hyperlink {
if link.Ref == axis {
if link.RID != "" {
return true, f.getSheetRelationshipsTargetByID(sheet, link.RID), err
}
return true, link.Location, err
}
}
}
return false, "", err
}
// SetCellHyperLink provides a function to set cell hyperlink by given
// worksheet name and link URL address. LinkType defines two types of
// hyperlink "External" for web site or "Location" for moving to one of cell
// in this workbook. Maximum limit hyperlinks in a worksheet is 65530. The
// below is example for external link.
//
// err := f.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
// // Set underline and font color style for the cell.
// style, err := f.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
// err = f.SetCellStyle("Sheet1", "A3", "A3", style)
//
// A this is another example for "Location":
//
// err := f.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
//
func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error {
// Check for correct cell name
if _, _, err := SplitCellName(axis); err != nil {
return err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
axis, err = f.mergeCellsParser(xlsx, axis)
if err != nil {
return err
}
var linkData xlsxHyperlink
if xlsx.Hyperlinks == nil {
xlsx.Hyperlinks = new(xlsxHyperlinks)
}
if len(xlsx.Hyperlinks.Hyperlink) > 65529 {
return errors.New("over maximum limit hyperlinks in a worksheet")
}
switch linkType {
case "External":
linkData = xlsxHyperlink{
Ref: axis,
}
sheetPath := f.sheetMap[trimSheetName(sheet)]
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType)
linkData.RID = "rId" + strconv.Itoa(rID)
case "Location":
linkData = xlsxHyperlink{
Ref: axis,
Location: link,
}
default:
return fmt.Errorf("invalid link type %q", linkType)
}
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, linkData)
return nil
}
// SetCellRichText provides a function to set cell with rich text by given
// worksheet. For example, set rich text on the A1 cell of the worksheet named
// Sheet1:
//
// package main
//
// import (
// "fmt"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// f := excelize.NewFile()
// if err := f.SetRowHeight("Sheet1", 1, 35); err != nil {
// fmt.Println(err)
// return
// }
// if err := f.SetColWidth("Sheet1", "A", "A", 44); err != nil {
// fmt.Println(err)
// return
// }
// if err := f.SetCellRichText("Sheet1", "A1", []excelize.RichTextRun{
// {
// Text: "blod",
// Font: &excelize.Font{
// Bold: true,
// Color: "2354e8",
// Family: "Times New Roman",
// },
// },
// {
// Text: " and ",
// Font: &excelize.Font{
// Family: "Times New Roman",
// },
// },
// {
// Text: " italic",
// Font: &excelize.Font{
// Bold: true,
// Color: "e83723",
// Italic: true,
// Family: "Times New Roman",
// },
// },
// {
// Text: "text with color and font-family,",
// Font: &excelize.Font{
// Bold: true,
// Color: "2354e8",
// Family: "Times New Roman",
// },
// },
// {
// Text: "\r\nlarge text with ",
// Font: &excelize.Font{
// Size: 14,
// Color: "ad23e8",
// },
// },
// {
// Text: "strike",
// Font: &excelize.Font{
// Color: "e89923",
// Strike: true,
// },
// },
// {
// Text: " and ",
// Font: &excelize.Font{
// Size: 14,
// Color: "ad23e8",
// },
// },
// {
// Text: "underline.",
// Font: &excelize.Font{
// Color: "23e833",
// Underline: "single",
// },
// },
// }); err != nil {
// fmt.Println(err)
// return
// }
// style, err := f.NewStyle(&excelize.Style{
// Alignment: &excelize.Alignment{
// WrapText: true,
// },
// })
// if err != nil {
// fmt.Println(err)
// return
// }
// if err := f.SetCellStyle("Sheet1", "A1", "A1", style); err != nil {
// fmt.Println(err)
// return
// }
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
cellData, col, _, err := f.prepareCell(ws, sheet, cell)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
si := xlsxSI{}
sst := f.sharedStringsReader()
textRuns := []xlsxR{}
for _, textRun := range runs {
run := xlsxR{T: &xlsxT{Val: textRun.Text}}
if strings.ContainsAny(textRun.Text, "\r\n ") {
run.T.Space = "preserve"
}
fnt := textRun.Font
if fnt != nil {
rpr := xlsxRPr{}
if fnt.Bold {
rpr.B = " "
}
if fnt.Italic {
rpr.I = " "
}
if fnt.Strike {
rpr.Strike = " "
}
if fnt.Underline != "" {
rpr.U = &attrValString{Val: &fnt.Underline}
}
if fnt.Family != "" {
rpr.RFont = &attrValString{Val: &fnt.Family}
}
if fnt.Size > 0.0 {
rpr.Sz = &attrValFloat{Val: &fnt.Size}
}
if fnt.Color != "" {
rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
}
run.RPr = &rpr
}
textRuns = append(textRuns, run)
}
si.R = textRuns
sst.SI = append(sst.SI, si)
sst.Count++
sst.UniqueCount++
cellData.T, cellData.V = "s", strconv.Itoa(len(sst.SI)-1)
f.addContentTypePart(0, "sharedStrings")
rels := f.relsReader("xl/_rels/workbook.xml.rels")
for _, rel := range rels.Relationships {
if rel.Target == "sharedStrings.xml" {
return err
}
}
// Update xl/_rels/workbook.xml.rels
f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipSharedStrings, "sharedStrings.xml", "")
return err
}
// SetSheetRow writes an array to row by given worksheet name, starting
// coordinate and a pointer to array type 'slice'. For example, writes an
// array to row 6 start with the cell B6 on Sheet1:
//
// err := f.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
//
func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
col, row, err := CellNameToCoordinates(axis)
if err != nil {
return err
}
// Make sure 'slice' is a Ptr to Slice
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
return errors.New("pointer to slice expected")
}
v = v.Elem()
for i := 0; i < v.Len(); i++ {
cell, err := CoordinatesToCellName(col+i, row)
// Error should never happens here. But keep checking to early detect regresions
// if it will be introduced in future.
if err != nil {
return err
}
if err := f.SetCellValue(sheet, cell, v.Index(i).Interface()); err != nil {
return err
}
}
return err
}
// getCellInfo does common preparation for all SetCell* methods.
func (f *File) prepareCell(xlsx *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int, error) {
var err error
cell, err = f.mergeCellsParser(xlsx, cell)
if err != nil {
return nil, 0, 0, err
}
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return nil, 0, 0, err
}
prepareSheetXML(xlsx, col, row)
return &xlsx.SheetData.Row[row-1].C[col-1], col, row, err
}
// getCellStringFunc does common value extraction workflow for all GetCell*
// methods. Passed function implements specific part of required logic.
func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return "", err
}
axis, err = f.mergeCellsParser(xlsx, axis)
if err != nil {
return "", err
}
_, row, err := CellNameToCoordinates(axis)
if err != nil {
return "", err
}
lastRowNum := 0
if l := len(xlsx.SheetData.Row); l > 0 {
lastRowNum = xlsx.SheetData.Row[l-1].R
}
// keep in mind: row starts from 1
if row > lastRowNum {
return "", nil
}
for rowIdx := range xlsx.SheetData.Row {
rowData := &xlsx.SheetData.Row[rowIdx]
if rowData.R != row {
continue
}
for colIdx := range rowData.C {
colData := &rowData.C[colIdx]
if axis != colData.R {
continue
}
val, ok, err := fn(xlsx, colData)
if err != nil {
return "", err
}
if ok {
return val, nil
}
}
}
return "", nil
}
// formattedValue provides a function to returns a value after formatted. If
// it is possible to apply a format to the cell value, it will do so, if not
// then an error will be returned, along with the raw value of the cell.
func (f *File) formattedValue(s int, v string) string {
if s == 0 {
return v
}
styleSheet := f.stylesReader()
ok := builtInNumFmtFunc[*styleSheet.CellXfs.Xf[s].NumFmtID]
if ok != nil {
return ok(*styleSheet.CellXfs.Xf[s].NumFmtID, v)
}
return v
}
// prepareCellStyle provides a function to prepare style index of cell in
// worksheet by given column index and style index.
func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
if xlsx.Cols != nil && style == 0 {
for _, c := range xlsx.Cols.Col {
if c.Min <= col && col <= c.Max {
style = c.Style
}
}
}
return style
}
// mergeCellsParser provides a function to check merged cells in worksheet by
// given axis.
func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) (string, error) {
axis = strings.ToUpper(axis)
if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
ok, err := f.checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref)
if err != nil {
return axis, err
}
if ok {
axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
}
}
}
return axis, nil
}
// checkCellInArea provides a function to determine if a given coordinate is
// within an area.
func (f *File) checkCellInArea(cell, area string) (bool, error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return false, err
}
rng := strings.Split(area, ":")
if len(rng) != 2 {
return false, err
}
coordinates, err := f.areaRefToCoordinates(area)
if err != nil {
return false, err
}
return cellInRef([]int{col, row}, coordinates), err
}
// cellInRef provides a function to determine if a given range is within an
// range.
func cellInRef(cell, ref []int) bool {
return cell[0] >= ref[0] && cell[0] <= ref[2] && cell[1] >= ref[1] && cell[1] <= ref[3]
}
// isOverlap find if the given two rectangles overlap or not.
func isOverlap(rect1, rect2 []int) bool {
return cellInRef([]int{rect1[0], rect1[1]}, rect2) ||
cellInRef([]int{rect1[2], rect1[1]}, rect2) ||
cellInRef([]int{rect1[0], rect1[3]}, rect2) ||
cellInRef([]int{rect1[2], rect1[3]}, rect2) ||
cellInRef([]int{rect2[0], rect2[1]}, rect1) ||
cellInRef([]int{rect2[2], rect2[1]}, rect1) ||
cellInRef([]int{rect2[0], rect2[3]}, rect1) ||
cellInRef([]int{rect2[2], rect2[3]}, rect1)
}
// getSharedForumula find a cell contains the same formula as another cell,
// the "shared" value can be used for the t attribute and the si attribute can
// be used to refer to the cell containing the formula. Two formulas are
// considered to be the same when their respective representations in
// R1C1-reference notation, are the same.
//
// Note that this function not validate ref tag to check the cell if or not in
// allow area, and always return origin shared formula.
func getSharedForumula(xlsx *xlsxWorksheet, si string) string {
for _, r := range xlsx.SheetData.Row {
for _, c := range r.C {
if c.F != nil && c.F.Ref != "" && c.F.T == STCellFormulaTypeShared && c.F.Si == si {
return c.F.Content
}
}
}
return ""
}

View file

@ -0,0 +1,873 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
)
// This section defines the currently supported chart types.
const (
Area = "area"
AreaStacked = "areaStacked"
AreaPercentStacked = "areaPercentStacked"
Area3D = "area3D"
Area3DStacked = "area3DStacked"
Area3DPercentStacked = "area3DPercentStacked"
Bar = "bar"
BarStacked = "barStacked"
BarPercentStacked = "barPercentStacked"
Bar3DClustered = "bar3DClustered"
Bar3DStacked = "bar3DStacked"
Bar3DPercentStacked = "bar3DPercentStacked"
Bar3DConeClustered = "bar3DConeClustered"
Bar3DConeStacked = "bar3DConeStacked"
Bar3DConePercentStacked = "bar3DConePercentStacked"
Bar3DPyramidClustered = "bar3DPyramidClustered"
Bar3DPyramidStacked = "bar3DPyramidStacked"
Bar3DPyramidPercentStacked = "bar3DPyramidPercentStacked"
Bar3DCylinderClustered = "bar3DCylinderClustered"
Bar3DCylinderStacked = "bar3DCylinderStacked"
Bar3DCylinderPercentStacked = "bar3DCylinderPercentStacked"
Col = "col"
ColStacked = "colStacked"
ColPercentStacked = "colPercentStacked"
Col3D = "col3D"
Col3DClustered = "col3DClustered"
Col3DStacked = "col3DStacked"
Col3DPercentStacked = "col3DPercentStacked"
Col3DCone = "col3DCone"
Col3DConeClustered = "col3DConeClustered"
Col3DConeStacked = "col3DConeStacked"
Col3DConePercentStacked = "col3DConePercentStacked"
Col3DPyramid = "col3DPyramid"
Col3DPyramidClustered = "col3DPyramidClustered"
Col3DPyramidStacked = "col3DPyramidStacked"
Col3DPyramidPercentStacked = "col3DPyramidPercentStacked"
Col3DCylinder = "col3DCylinder"
Col3DCylinderClustered = "col3DCylinderClustered"
Col3DCylinderStacked = "col3DCylinderStacked"
Col3DCylinderPercentStacked = "col3DCylinderPercentStacked"
Doughnut = "doughnut"
Line = "line"
Pie = "pie"
Pie3D = "pie3D"
PieOfPieChart = "pieOfPie"
BarOfPieChart = "barOfPie"
Radar = "radar"
Scatter = "scatter"
Surface3D = "surface3D"
WireframeSurface3D = "wireframeSurface3D"
Contour = "contour"
WireframeContour = "wireframeContour"
Bubble = "bubble"
Bubble3D = "bubble3D"
)
// This section defines the default value of chart properties.
var (
chartView3DRotX = map[string]int{
Area: 0,
AreaStacked: 0,
AreaPercentStacked: 0,
Area3D: 15,
Area3DStacked: 15,
Area3DPercentStacked: 15,
Bar: 0,
BarStacked: 0,
BarPercentStacked: 0,
Bar3DClustered: 15,
Bar3DStacked: 15,
Bar3DPercentStacked: 15,
Bar3DConeClustered: 15,
Bar3DConeStacked: 15,
Bar3DConePercentStacked: 15,
Bar3DPyramidClustered: 15,
Bar3DPyramidStacked: 15,
Bar3DPyramidPercentStacked: 15,
Bar3DCylinderClustered: 15,
Bar3DCylinderStacked: 15,
Bar3DCylinderPercentStacked: 15,
Col: 0,
ColStacked: 0,
ColPercentStacked: 0,
Col3D: 15,
Col3DClustered: 15,
Col3DStacked: 15,
Col3DPercentStacked: 15,
Col3DCone: 15,
Col3DConeClustered: 15,
Col3DConeStacked: 15,
Col3DConePercentStacked: 15,
Col3DPyramid: 15,
Col3DPyramidClustered: 15,
Col3DPyramidStacked: 15,
Col3DPyramidPercentStacked: 15,
Col3DCylinder: 15,
Col3DCylinderClustered: 15,
Col3DCylinderStacked: 15,
Col3DCylinderPercentStacked: 15,
Doughnut: 0,
Line: 0,
Pie: 0,
Pie3D: 30,
PieOfPieChart: 0,
BarOfPieChart: 0,
Radar: 0,
Scatter: 0,
Surface3D: 15,
WireframeSurface3D: 15,
Contour: 90,
WireframeContour: 90,
}
chartView3DRotY = map[string]int{
Area: 0,
AreaStacked: 0,
AreaPercentStacked: 0,
Area3D: 20,
Area3DStacked: 20,
Area3DPercentStacked: 20,
Bar: 0,
BarStacked: 0,
BarPercentStacked: 0,
Bar3DClustered: 20,
Bar3DStacked: 20,
Bar3DPercentStacked: 20,
Bar3DConeClustered: 20,
Bar3DConeStacked: 20,
Bar3DConePercentStacked: 20,
Bar3DPyramidClustered: 20,
Bar3DPyramidStacked: 20,
Bar3DPyramidPercentStacked: 20,
Bar3DCylinderClustered: 20,
Bar3DCylinderStacked: 20,
Bar3DCylinderPercentStacked: 20,
Col: 0,
ColStacked: 0,
ColPercentStacked: 0,
Col3D: 20,
Col3DClustered: 20,
Col3DStacked: 20,
Col3DPercentStacked: 20,
Col3DCone: 20,
Col3DConeClustered: 20,
Col3DConeStacked: 20,
Col3DConePercentStacked: 20,
Col3DPyramid: 20,
Col3DPyramidClustered: 20,
Col3DPyramidStacked: 20,
Col3DPyramidPercentStacked: 20,
Col3DCylinder: 20,
Col3DCylinderClustered: 20,
Col3DCylinderStacked: 20,
Col3DCylinderPercentStacked: 20,
Doughnut: 0,
Line: 0,
Pie: 0,
Pie3D: 0,
PieOfPieChart: 0,
BarOfPieChart: 0,
Radar: 0,
Scatter: 0,
Surface3D: 20,
WireframeSurface3D: 20,
Contour: 0,
WireframeContour: 0,
}
plotAreaChartOverlap = map[string]int{
BarStacked: 100,
BarPercentStacked: 100,
ColStacked: 100,
ColPercentStacked: 100,
}
chartView3DPerspective = map[string]int{
Contour: 0,
WireframeContour: 0,
}
chartView3DRAngAx = map[string]int{
Area: 0,
AreaStacked: 0,
AreaPercentStacked: 0,
Area3D: 1,
Area3DStacked: 1,
Area3DPercentStacked: 1,
Bar: 0,
BarStacked: 0,
BarPercentStacked: 0,
Bar3DClustered: 1,
Bar3DStacked: 1,
Bar3DPercentStacked: 1,
Bar3DConeClustered: 1,
Bar3DConeStacked: 1,
Bar3DConePercentStacked: 1,
Bar3DPyramidClustered: 1,
Bar3DPyramidStacked: 1,
Bar3DPyramidPercentStacked: 1,
Bar3DCylinderClustered: 1,
Bar3DCylinderStacked: 1,
Bar3DCylinderPercentStacked: 1,
Col: 0,
ColStacked: 0,
ColPercentStacked: 0,
Col3D: 1,
Col3DClustered: 1,
Col3DStacked: 1,
Col3DPercentStacked: 1,
Col3DCone: 1,
Col3DConeClustered: 1,
Col3DConeStacked: 1,
Col3DConePercentStacked: 1,
Col3DPyramid: 1,
Col3DPyramidClustered: 1,
Col3DPyramidStacked: 1,
Col3DPyramidPercentStacked: 1,
Col3DCylinder: 1,
Col3DCylinderClustered: 1,
Col3DCylinderStacked: 1,
Col3DCylinderPercentStacked: 1,
Doughnut: 0,
Line: 0,
Pie: 0,
Pie3D: 0,
PieOfPieChart: 0,
BarOfPieChart: 0,
Radar: 0,
Scatter: 0,
Surface3D: 0,
WireframeSurface3D: 0,
Contour: 0,
Bubble: 0,
Bubble3D: 0,
}
chartLegendPosition = map[string]string{
"bottom": "b",
"left": "l",
"right": "r",
"top": "t",
"top_right": "tr",
}
chartValAxNumFmtFormatCode = map[string]string{
Area: "General",
AreaStacked: "General",
AreaPercentStacked: "0%",
Area3D: "General",
Area3DStacked: "General",
Area3DPercentStacked: "0%",
Bar: "General",
BarStacked: "General",
BarPercentStacked: "0%",
Bar3DClustered: "General",
Bar3DStacked: "General",
Bar3DPercentStacked: "0%",
Bar3DConeClustered: "General",
Bar3DConeStacked: "General",
Bar3DConePercentStacked: "0%",
Bar3DPyramidClustered: "General",
Bar3DPyramidStacked: "General",
Bar3DPyramidPercentStacked: "0%",
Bar3DCylinderClustered: "General",
Bar3DCylinderStacked: "General",
Bar3DCylinderPercentStacked: "0%",
Col: "General",
ColStacked: "General",
ColPercentStacked: "0%",
Col3D: "General",
Col3DClustered: "General",
Col3DStacked: "General",
Col3DPercentStacked: "0%",
Col3DCone: "General",
Col3DConeClustered: "General",
Col3DConeStacked: "General",
Col3DConePercentStacked: "0%",
Col3DPyramid: "General",
Col3DPyramidClustered: "General",
Col3DPyramidStacked: "General",
Col3DPyramidPercentStacked: "0%",
Col3DCylinder: "General",
Col3DCylinderClustered: "General",
Col3DCylinderStacked: "General",
Col3DCylinderPercentStacked: "0%",
Doughnut: "General",
Line: "General",
Pie: "General",
Pie3D: "General",
PieOfPieChart: "General",
BarOfPieChart: "General",
Radar: "General",
Scatter: "General",
Surface3D: "General",
WireframeSurface3D: "General",
Contour: "General",
WireframeContour: "General",
Bubble: "General",
Bubble3D: "General",
}
chartValAxCrossBetween = map[string]string{
Area: "midCat",
AreaStacked: "midCat",
AreaPercentStacked: "midCat",
Area3D: "midCat",
Area3DStacked: "midCat",
Area3DPercentStacked: "midCat",
Bar: "between",
BarStacked: "between",
BarPercentStacked: "between",
Bar3DClustered: "between",
Bar3DStacked: "between",
Bar3DPercentStacked: "between",
Bar3DConeClustered: "between",
Bar3DConeStacked: "between",
Bar3DConePercentStacked: "between",
Bar3DPyramidClustered: "between",
Bar3DPyramidStacked: "between",
Bar3DPyramidPercentStacked: "between",
Bar3DCylinderClustered: "between",
Bar3DCylinderStacked: "between",
Bar3DCylinderPercentStacked: "between",
Col: "between",
ColStacked: "between",
ColPercentStacked: "between",
Col3D: "between",
Col3DClustered: "between",
Col3DStacked: "between",
Col3DPercentStacked: "between",
Col3DCone: "between",
Col3DConeClustered: "between",
Col3DConeStacked: "between",
Col3DConePercentStacked: "between",
Col3DPyramid: "between",
Col3DPyramidClustered: "between",
Col3DPyramidStacked: "between",
Col3DPyramidPercentStacked: "between",
Col3DCylinder: "between",
Col3DCylinderClustered: "between",
Col3DCylinderStacked: "between",
Col3DCylinderPercentStacked: "between",
Doughnut: "between",
Line: "between",
Pie: "between",
Pie3D: "between",
PieOfPieChart: "between",
BarOfPieChart: "between",
Radar: "between",
Scatter: "between",
Surface3D: "midCat",
WireframeSurface3D: "midCat",
Contour: "midCat",
WireframeContour: "midCat",
Bubble: "midCat",
Bubble3D: "midCat",
}
plotAreaChartGrouping = map[string]string{
Area: "standard",
AreaStacked: "stacked",
AreaPercentStacked: "percentStacked",
Area3D: "standard",
Area3DStacked: "stacked",
Area3DPercentStacked: "percentStacked",
Bar: "clustered",
BarStacked: "stacked",
BarPercentStacked: "percentStacked",
Bar3DClustered: "clustered",
Bar3DStacked: "stacked",
Bar3DPercentStacked: "percentStacked",
Bar3DConeClustered: "clustered",
Bar3DConeStacked: "stacked",
Bar3DConePercentStacked: "percentStacked",
Bar3DPyramidClustered: "clustered",
Bar3DPyramidStacked: "stacked",
Bar3DPyramidPercentStacked: "percentStacked",
Bar3DCylinderClustered: "clustered",
Bar3DCylinderStacked: "stacked",
Bar3DCylinderPercentStacked: "percentStacked",
Col: "clustered",
ColStacked: "stacked",
ColPercentStacked: "percentStacked",
Col3D: "standard",
Col3DClustered: "clustered",
Col3DStacked: "stacked",
Col3DPercentStacked: "percentStacked",
Col3DCone: "standard",
Col3DConeClustered: "clustered",
Col3DConeStacked: "stacked",
Col3DConePercentStacked: "percentStacked",
Col3DPyramid: "standard",
Col3DPyramidClustered: "clustered",
Col3DPyramidStacked: "stacked",
Col3DPyramidPercentStacked: "percentStacked",
Col3DCylinder: "standard",
Col3DCylinderClustered: "clustered",
Col3DCylinderStacked: "stacked",
Col3DCylinderPercentStacked: "percentStacked",
Line: "standard",
}
plotAreaChartBarDir = map[string]string{
Bar: "bar",
BarStacked: "bar",
BarPercentStacked: "bar",
Bar3DClustered: "bar",
Bar3DStacked: "bar",
Bar3DPercentStacked: "bar",
Bar3DConeClustered: "bar",
Bar3DConeStacked: "bar",
Bar3DConePercentStacked: "bar",
Bar3DPyramidClustered: "bar",
Bar3DPyramidStacked: "bar",
Bar3DPyramidPercentStacked: "bar",
Bar3DCylinderClustered: "bar",
Bar3DCylinderStacked: "bar",
Bar3DCylinderPercentStacked: "bar",
Col: "col",
ColStacked: "col",
ColPercentStacked: "col",
Col3D: "col",
Col3DClustered: "col",
Col3DStacked: "col",
Col3DPercentStacked: "col",
Col3DCone: "col",
Col3DConeStacked: "col",
Col3DConeClustered: "col",
Col3DConePercentStacked: "col",
Col3DPyramid: "col",
Col3DPyramidClustered: "col",
Col3DPyramidStacked: "col",
Col3DPyramidPercentStacked: "col",
Col3DCylinder: "col",
Col3DCylinderClustered: "col",
Col3DCylinderStacked: "col",
Col3DCylinderPercentStacked: "col",
Line: "standard",
}
orientation = map[bool]string{
true: "maxMin",
false: "minMax",
}
catAxPos = map[bool]string{
true: "t",
false: "b",
}
valAxPos = map[bool]string{
true: "r",
false: "l",
}
valTickLblPos = map[string]string{
Contour: "none",
WireframeContour: "none",
}
)
// parseFormatChartSet provides a function to parse the format settings of the
// chart with default value.
func parseFormatChartSet(formatSet string) (*formatChart, error) {
format := formatChart{
Dimension: formatChartDimension{
Width: 480,
Height: 290,
},
Format: formatPicture{
FPrintsWithSheet: true,
FLocksWithSheet: false,
NoChangeAspect: false,
OffsetX: 0,
OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
},
Legend: formatChartLegend{
Position: "bottom",
ShowLegendKey: false,
},
Title: formatChartTitle{
Name: " ",
},
ShowBlanksAs: "gap",
}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
}
// AddChart provides the method to add chart in a sheet by given chart format
// set (such as offset, scale, aspect ratio setting and print settings) and
// properties set. For example, create 3D clustered column chart with data
// Sheet1!$E$1:$L$15:
//
// package main
//
// import (
// "fmt"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
// values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
// f := excelize.NewFile()
// for k, v := range categories {
// f.SetCellValue("Sheet1", k, v)
// }
// for k, v := range values {
// f.SetCellValue("Sheet1", k, v)
// }
// if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"reverse_order":true},"y_axis":{"maximum":7.5,"minimum":0.5}}`); err != nil {
// fmt.Println(err)
// return
// }
// // Save xlsx file by the given path.
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
// The following shows the type of chart supported by excelize:
//
// Type | Chart
// -----------------------------+------------------------------
// area | 2D area chart
// areaStacked | 2D stacked area chart
// areaPercentStacked | 2D 100% stacked area chart
// area3D | 3D area chart
// area3DStacked | 3D stacked area chart
// area3DPercentStacked | 3D 100% stacked area chart
// bar | 2D clustered bar chart
// barStacked | 2D stacked bar chart
// barPercentStacked | 2D 100% stacked bar chart
// bar3DClustered | 3D clustered bar chart
// bar3DStacked | 3D stacked bar chart
// bar3DPercentStacked | 3D 100% stacked bar chart
// bar3DConeClustered | 3D cone clustered bar chart
// bar3DConeStacked | 3D cone stacked bar chart
// bar3DConePercentStacked | 3D cone percent bar chart
// bar3DPyramidClustered | 3D pyramid clustered bar chart
// bar3DPyramidStacked | 3D pyramid stacked bar chart
// bar3DPyramidPercentStacked | 3D pyramid percent stacked bar chart
// bar3DCylinderClustered | 3D cylinder clustered bar chart
// bar3DCylinderStacked | 3D cylinder stacked bar chart
// bar3DCylinderPercentStacked | 3D cylinder percent stacked bar chart
// col | 2D clustered column chart
// colStacked | 2D stacked column chart
// colPercentStacked | 2D 100% stacked column chart
// col3DClustered | 3D clustered column chart
// col3D | 3D column chart
// col3DStacked | 3D stacked column chart
// col3DPercentStacked | 3D 100% stacked column chart
// col3DCone | 3D cone column chart
// col3DConeClustered | 3D cone clustered column chart
// col3DConeStacked | 3D cone stacked column chart
// col3DConePercentStacked | 3D cone percent stacked column chart
// col3DPyramid | 3D pyramid column chart
// col3DPyramidClustered | 3D pyramid clustered column chart
// col3DPyramidStacked | 3D pyramid stacked column chart
// col3DPyramidPercentStacked | 3D pyramid percent stacked column chart
// col3DCylinder | 3D cylinder column chart
// col3DCylinderClustered | 3D cylinder clustered column chart
// col3DCylinderStacked | 3D cylinder stacked column chart
// col3DCylinderPercentStacked | 3D cylinder percent stacked column chart
// doughnut | doughnut chart
// line | line chart
// pie | pie chart
// pie3D | 3D pie chart
// pieOfPie | pie of pie chart
// barOfPie | bar of pie chart
// radar | radar chart
// scatter | scatter chart
// surface3D | 3D surface chart
// wireframeSurface3D | 3D wireframe surface chart
// contour | contour chart
// wireframeContour | wireframe contour chart
// bubble | bubble chart
// bubble3D | 3D bubble chart
//
// In Excel a chart series is a collection of information that defines which data is plotted such as values, axis labels and formatting.
//
// The series options that can be set are:
//
// name
// categories
// values
// line
//
// name: Set the name for the series. The name is displayed in the chart legend and in the formula bar. The name property is optional and if it isn't supplied it will default to Series 1..n. The name can also be a formula such as Sheet1!$A$1
//
// categories: This sets the chart category labels. The category is more or less the same as the X axis. In most chart types the categories property is optional and the chart will just assume a sequential series from 1..n.
//
// values: This is the most important property of a series and is the only mandatory option for every chart object. This option links the chart with the worksheet data that it displays.
//
// line: This sets the line format of the line chart. The line property is optional and if it isn't supplied it will default style. The options that can be set is width. The range of width is 0.25pt - 999pt. If the value of width is outside the range, the default width of the line is 2pt.
//
// Set properties of the chart legend. The options that can be set are:
//
// position
// show_legend_key
//
// position: Set the position of the chart legend. The default legend position is right. The available positions are:
//
// top
// bottom
// left
// right
// top_right
//
// show_legend_key: Set the legend keys shall be shown in data labels. The default value is false.
//
// Set properties of the chart title. The properties that can be set are:
//
// title
//
// name: Set the name (title) for the chart. The name is displayed above the chart. The name can also be a formula such as Sheet1!$A$1 or a list with a sheetname. The name property is optional. The default is to have no chart title.
//
// Specifies how blank cells are plotted on the chart by show_blanks_as. The default value is gap. The options that can be set are:
//
// gap
// span
// zero
//
// gap: Specifies that blank values shall be left as a gap.
//
// sapn: Specifies that blank values shall be spanned with a line.
//
// zero: Specifies that blank values shall be treated as zero.
//
// Set chart offset, scale, aspect ratio setting and print settings by format, same as function AddPicture.
//
// Set the position of the chart plot area by plotarea. The properties that can be set are:
//
// show_bubble_size
// show_cat_name
// show_leader_lines
// show_percent
// show_series_name
// show_val
//
// show_bubble_size: Specifies the bubble size shall be shown in a data label. The show_bubble_size property is optional. The default value is false.
//
// show_cat_name: Specifies that the category name shall be shown in the data label. The show_cat_name property is optional. The default value is true.
//
// show_leader_lines: Specifies leader lines shall be shown for data labels. The show_leader_lines property is optional. The default value is false.
//
// show_percent: Specifies that the percentage shall be shown in a data label. The show_percent property is optional. The default value is false.
//
// show_series_name: Specifies that the series name shall be shown in a data label. The show_series_name property is optional. The default value is false.
//
// show_val: Specifies that the value shall be shown in a data label. The show_val property is optional. The default value is false.
//
// Set the primary horizontal and vertical axis options by x_axis and y_axis. The properties of x_axis that can be set are:
//
// major_grid_lines
// minor_grid_lines
// tick_label_skip
// reverse_order
// maximum
// minimum
//
// The properties of y_axis that can be set are:
//
// major_grid_lines
// minor_grid_lines
// major_unit
// reverse_order
// maximum
// minimum
//
// major_grid_lines: Specifies major gridlines.
//
// minor_grid_lines: Specifies minor gridlines.
//
// major_unit: Specifies the distance between major ticks. Shall contain a positive floating-point number. The major_unit property is optional. The default value is auto.
//
// tick_label_skip: Specifies how many tick labels to skip between label that is drawn. The tick_label_skip property is optional. The default value is auto.
//
// reverse_order: Specifies that the categories or values on reverse order (orientation of the chart). The reverse_order property is optional. The default value is false.
//
// maximum: Specifies that the fixed maximum, 0 is auto. The maximum property is optional. The default value is auto.
//
// minimum: Specifies that the fixed minimum, 0 is auto. The minimum property is optional. The default value is auto.
//
// Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
//
// combo: Specifies the create a chart that combines two or more chart types
// in a single chart. For example, create a clustered column - line chart with
// data Sheet1!$E$1:$L$15:
//
// package main
//
// import (
// "fmt"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
// values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
// f := excelize.NewFile()
// for k, v := range categories {
// f.SetCellValue("Sheet1", k, v)
// }
// for k, v := range values {
// f.SetCellValue("Sheet1", k, v)
// }
// if err := f.AddChart("Sheet1", "E1", `{"type":"col","series":[{"name":"Sheet1!$A$2","categories":"","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Clustered Column - Line Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, `{"type":"line","series":[{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`); err != nil {
// fmt.Println(err)
// return
// }
// // Save xlsx file by the given path.
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
// Read sheet data.
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
formatSet, comboCharts, err := f.getFormatChart(format, combo)
if err != nil {
return err
}
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
err = f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format)
if err != nil {
return err
}
f.addChart(formatSet, comboCharts)
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(drawingID, "drawings")
return err
}
// AddChartSheet provides the method to create a chartsheet by given chart
// format set (such as offset, scale, aspect ratio setting and print settings)
// and properties set. In Excel a chartsheet is a worksheet that only contains
// a chart.
func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
// Check if the worksheet already exists
if f.GetSheetIndex(sheet) != -1 {
return errors.New("the same name worksheet already exists")
}
formatSet, comboCharts, err := f.getFormatChart(format, combo)
if err != nil {
return err
}
cs := xlsxChartsheet{
SheetViews: []*xlsxChartsheetViews{{
SheetView: []*xlsxChartsheetView{{ZoomScaleAttr: 100, ZoomToFitAttr: true}}},
},
}
f.SheetCount++
wb := f.workbookReader()
sheetID := 0
for _, v := range wb.Sheets.Sheet {
if v.SheetID > sheetID {
sheetID = v.SheetID
}
}
sheetID++
path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml"
f.sheetMap[trimSheetName(sheet)] = path
f.Sheet[path] = nil
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
f.addSheetDrawingChart(drawingXML, drawingRID, &formatSet.Format)
f.addChart(formatSet, comboCharts)
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(sheetID, "chartsheet")
f.addContentTypePart(drawingID, "drawings")
// Update xl/_rels/workbook.xml.rels
rID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipChartsheet, fmt.Sprintf("chartsheets/sheet%d.xml", sheetID), "")
// Update xl/workbook.xml
f.setWorkbook(sheet, sheetID, rID)
chartsheet, _ := xml.Marshal(cs)
f.saveFileList(path, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(chartsheet)))
return err
}
// getFormatChart provides a function to check format set of the chart and
// create chart format.
func (f *File) getFormatChart(format string, combo []string) (*formatChart, []*formatChart, error) {
comboCharts := []*formatChart{}
formatSet, err := parseFormatChartSet(format)
if err != nil {
return formatSet, comboCharts, err
}
for _, comboFormat := range combo {
comboChart, err := parseFormatChartSet(comboFormat)
if err != nil {
return formatSet, comboCharts, err
}
if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
return formatSet, comboCharts, errors.New("unsupported chart type " + comboChart.Type)
}
comboCharts = append(comboCharts, comboChart)
}
if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
return formatSet, comboCharts, errors.New("unsupported chart type " + formatSet.Type)
}
return formatSet, comboCharts, err
}
// DeleteChart provides a function to delete chart in XLSX by given worksheet
// and cell name.
func (f *File) DeleteChart(sheet, cell string) (err error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return
}
col--
row--
ws, err := f.workSheetReader(sheet)
if err != nil {
return
}
if ws.Drawing == nil {
return
}
drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
return f.deleteDrawing(col, row, drawingXML, "Chart")
}
// countCharts provides a function to get chart files count storage in the
// folder xl/charts.
func (f *File) countCharts() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/charts/chart") {
count++
}
}
return count
}
// ptToEMUs provides a function to convert pt to EMUs, 1 pt = 12700 EMUs. The
// range of pt is 0.25pt - 999pt. If the value of pt is outside the range, the
// default EMUs will be returned.
func (f *File) ptToEMUs(pt float64) int {
if 0.25 > pt || pt > 999 {
return 25400
}
return int(12700 * pt)
}

View file

@ -0,0 +1,536 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"errors"
"math"
"strings"
"github.com/mohae/deepcopy"
)
// Define the default cell size and EMU unit of measurement.
const (
defaultColWidthPixels float64 = 64
defaultRowHeightPixels float64 = 20
EMU int = 9525
)
// GetColVisible provides a function to get visible of a single column by given
// worksheet name and column name. For example, get visible state of column D
// in Sheet1:
//
// visible, err := f.GetColVisible("Sheet1", "D")
//
func (f *File) GetColVisible(sheet, col string) (bool, error) {
visible := true
colNum, err := ColumnNameToNumber(col)
if err != nil {
return visible, err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return false, err
}
if xlsx.Cols == nil {
return visible, err
}
for c := range xlsx.Cols.Col {
colData := &xlsx.Cols.Col[c]
if colData.Min <= colNum && colNum <= colData.Max {
visible = !colData.Hidden
}
}
return visible, err
}
// SetColVisible provides a function to set visible columns by given worksheet
// name, columns range and visibility.
//
// For example hide column D on Sheet1:
//
// err := f.SetColVisible("Sheet1", "D", false)
//
// Hide the columns from D to F (included):
//
// err := f.SetColVisible("Sheet1", "D:F", false)
//
func (f *File) SetColVisible(sheet, columns string, visible bool) error {
var max int
colsTab := strings.Split(columns, ":")
min, err := ColumnNameToNumber(colsTab[0])
if err != nil {
return err
}
if len(colsTab) == 2 {
max, err = ColumnNameToNumber(colsTab[1])
if err != nil {
return err
}
} else {
max = min
}
if max < min {
min, max = max, min
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
colData := xlsxCol{
Min: min,
Max: max,
Width: 9, // default width
Hidden: !visible,
CustomWidth: true,
}
if xlsx.Cols == nil {
cols := xlsxCols{}
cols.Col = append(cols.Col, colData)
xlsx.Cols = &cols
return nil
}
xlsx.Cols.Col = flatCols(colData, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol {
fc.BestFit = c.BestFit
fc.Collapsed = c.Collapsed
fc.CustomWidth = c.CustomWidth
fc.OutlineLevel = c.OutlineLevel
fc.Phonetic = c.Phonetic
fc.Style = c.Style
fc.Width = c.Width
return fc
})
return nil
}
// GetColOutlineLevel provides a function to get outline level of a single
// column by given worksheet name and column name. For example, get outline
// level of column D in Sheet1:
//
// level, err := f.GetColOutlineLevel("Sheet1", "D")
//
func (f *File) GetColOutlineLevel(sheet, col string) (uint8, error) {
level := uint8(0)
colNum, err := ColumnNameToNumber(col)
if err != nil {
return level, err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return 0, err
}
if xlsx.Cols == nil {
return level, err
}
for c := range xlsx.Cols.Col {
colData := &xlsx.Cols.Col[c]
if colData.Min <= colNum && colNum <= colData.Max {
level = colData.OutlineLevel
}
}
return level, err
}
// SetColOutlineLevel provides a function to set outline level of a single
// column by given worksheet name and column name. The value of parameter
// 'level' is 1-7. For example, set outline level of column D in Sheet1 to 2:
//
// err := f.SetColOutlineLevel("Sheet1", "D", 2)
//
func (f *File) SetColOutlineLevel(sheet, col string, level uint8) error {
if level > 7 || level < 1 {
return errors.New("invalid outline level")
}
colNum, err := ColumnNameToNumber(col)
if err != nil {
return err
}
colData := xlsxCol{
Min: colNum,
Max: colNum,
OutlineLevel: level,
CustomWidth: true,
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if xlsx.Cols == nil {
cols := xlsxCols{}
cols.Col = append(cols.Col, colData)
xlsx.Cols = &cols
return err
}
xlsx.Cols.Col = flatCols(colData, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol {
fc.BestFit = c.BestFit
fc.Collapsed = c.Collapsed
fc.CustomWidth = c.CustomWidth
fc.Hidden = c.Hidden
fc.Phonetic = c.Phonetic
fc.Style = c.Style
fc.Width = c.Width
return fc
})
return err
}
// SetColStyle provides a function to set style of columns by given worksheet
// name, columns range and style ID.
//
// For example set style of column H on Sheet1:
//
// err = f.SetColStyle("Sheet1", "H", style)
//
// Set style of columns C:F on Sheet1:
//
// err = f.SetColStyle("Sheet1", "C:F", style)
//
func (f *File) SetColStyle(sheet, columns string, styleID int) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
var c1, c2 string
var min, max int
cols := strings.Split(columns, ":")
c1 = cols[0]
min, err = ColumnNameToNumber(c1)
if err != nil {
return err
}
if len(cols) == 2 {
c2 = cols[1]
max, err = ColumnNameToNumber(c2)
if err != nil {
return err
}
} else {
max = min
}
if max < min {
min, max = max, min
}
if xlsx.Cols == nil {
xlsx.Cols = &xlsxCols{}
}
xlsx.Cols.Col = flatCols(xlsxCol{
Min: min,
Max: max,
Width: 9,
Style: styleID,
}, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol {
fc.BestFit = c.BestFit
fc.Collapsed = c.Collapsed
fc.CustomWidth = c.CustomWidth
fc.Hidden = c.Hidden
fc.OutlineLevel = c.OutlineLevel
fc.Phonetic = c.Phonetic
fc.Width = c.Width
return fc
})
return nil
}
// SetColWidth provides a function to set the width of a single column or
// multiple columns. For example:
//
// f := excelize.NewFile()
// err := f.SetColWidth("Sheet1", "A", "H", 20)
//
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) error {
min, err := ColumnNameToNumber(startcol)
if err != nil {
return err
}
max, err := ColumnNameToNumber(endcol)
if err != nil {
return err
}
if min > max {
min, max = max, min
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
col := xlsxCol{
Min: min,
Max: max,
Width: width,
CustomWidth: true,
}
if xlsx.Cols == nil {
cols := xlsxCols{}
cols.Col = append(cols.Col, col)
xlsx.Cols = &cols
return err
}
xlsx.Cols.Col = flatCols(col, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol {
fc.BestFit = c.BestFit
fc.Collapsed = c.Collapsed
fc.Hidden = c.Hidden
fc.OutlineLevel = c.OutlineLevel
fc.Phonetic = c.Phonetic
fc.Style = c.Style
return fc
})
return err
}
// flatCols provides a method for the column's operation functions to flatten
// and check the worksheet columns.
func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) []xlsxCol {
fc := []xlsxCol{}
for i := col.Min; i <= col.Max; i++ {
c := deepcopy.Copy(col).(xlsxCol)
c.Min, c.Max = i, i
fc = append(fc, c)
}
inFlat := func(colID int, cols []xlsxCol) (int, bool) {
for idx, c := range cols {
if c.Max == colID && c.Min == colID {
return idx, true
}
}
return -1, false
}
for _, column := range cols {
for i := column.Min; i <= column.Max; i++ {
if idx, ok := inFlat(i, fc); ok {
fc[idx] = replacer(fc[idx], column)
continue
}
c := deepcopy.Copy(column).(xlsxCol)
c.Min, c.Max = i, i
fc = append(fc, c)
}
}
return fc
}
// positionObjectPixels calculate the vertices that define the position of a
// graphical object within the worksheet in pixels.
//
// +------------+------------+
// | A | B |
// +-----+------------+------------+
// | |(x1,y1) | |
// | 1 |(A1)._______|______ |
// | | | | |
// | | | | |
// +-----+----| OBJECT |-----+
// | | | | |
// | 2 | |______________. |
// | | | (B2)|
// | | | (x2,y2)|
// +-----+------------+------------+
//
// Example of an object that covers some of the area from cell A1 to B2.
//
// Based on the width and height of the object we need to calculate 8 vars:
//
// colStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2.
//
// We also calculate the absolute x and y position of the top left vertex of
// the object. This is required for images.
//
// The width and height of the cells that the object occupies can be
// variable and have to be taken into account.
//
// The values of col_start and row_start are passed in from the calling
// function. The values of col_end and row_end are calculated by
// subtracting the width and height of the object from the width and
// height of the underlying cells.
//
// colStart # Col containing upper left corner of object.
// x1 # Distance to left side of object.
//
// rowStart # Row containing top left corner of object.
// y1 # Distance to top of object.
//
// colEnd # Col containing lower right corner of object.
// x2 # Distance to right side of object.
//
// rowEnd # Row containing bottom right corner of object.
// y2 # Distance to bottom of object.
//
// width # Width of object frame.
// height # Height of object frame.
//
// xAbs # Absolute distance to left side of object.
// yAbs # Absolute distance to top side of object.
//
func (f *File) positionObjectPixels(sheet string, col, row, x1, y1, width, height int) (int, int, int, int, int, int, int, int) {
xAbs := 0
yAbs := 0
// Calculate the absolute x offset of the top-left vertex.
for colID := 1; colID <= col; colID++ {
xAbs += f.getColWidth(sheet, colID)
}
xAbs += x1
// Calculate the absolute y offset of the top-left vertex.
// Store the column change to allow optimisations.
for rowID := 1; rowID <= row; rowID++ {
yAbs += f.getRowHeight(sheet, rowID)
}
yAbs += y1
// Adjust start column for offsets that are greater than the col width.
for x1 >= f.getColWidth(sheet, col) {
x1 -= f.getColWidth(sheet, col)
col++
}
// Adjust start row for offsets that are greater than the row height.
for y1 >= f.getRowHeight(sheet, row) {
y1 -= f.getRowHeight(sheet, row)
row++
}
// Initialise end cell to the same as the start cell.
colEnd := col
rowEnd := row
width += x1
height += y1
// Subtract the underlying cell widths to find end cell of the object.
for width >= f.getColWidth(sheet, colEnd+1) {
colEnd++
width -= f.getColWidth(sheet, colEnd)
}
// Subtract the underlying cell heights to find end cell of the object.
for height >= f.getRowHeight(sheet, rowEnd) {
height -= f.getRowHeight(sheet, rowEnd)
rowEnd++
}
// The end vertices are whatever is left from the width and height.
x2 := width
y2 := height
return col, row, xAbs, yAbs, colEnd, rowEnd, x2, y2
}
// getColWidth provides a function to get column width in pixels by given
// sheet name and column index.
func (f *File) getColWidth(sheet string, col int) int {
xlsx, _ := f.workSheetReader(sheet)
if xlsx.Cols != nil {
var width float64
for _, v := range xlsx.Cols.Col {
if v.Min <= col && col <= v.Max {
width = v.Width
}
}
if width != 0 {
return int(convertColWidthToPixels(width))
}
}
// Optimisation for when the column widths haven't changed.
return int(defaultColWidthPixels)
}
// GetColWidth provides a function to get column width by given worksheet name
// and column index.
func (f *File) GetColWidth(sheet, col string) (float64, error) {
colNum, err := ColumnNameToNumber(col)
if err != nil {
return defaultColWidthPixels, err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return defaultColWidthPixels, err
}
if xlsx.Cols != nil {
var width float64
for _, v := range xlsx.Cols.Col {
if v.Min <= colNum && colNum <= v.Max {
width = v.Width
}
}
if width != 0 {
return width, err
}
}
// Optimisation for when the column widths haven't changed.
return defaultColWidthPixels, err
}
// InsertCol provides a function to insert a new column before given column
// index. For example, create a new column before column C in Sheet1:
//
// err := f.InsertCol("Sheet1", "C")
//
func (f *File) InsertCol(sheet, col string) error {
num, err := ColumnNameToNumber(col)
if err != nil {
return err
}
return f.adjustHelper(sheet, columns, num, 1)
}
// RemoveCol provides a function to remove single column by given worksheet
// name and column index. For example, remove column C in Sheet1:
//
// err := f.RemoveCol("Sheet1", "C")
//
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) RemoveCol(sheet, col string) error {
num, err := ColumnNameToNumber(col)
if err != nil {
return err
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
for rowIdx := range xlsx.SheetData.Row {
rowData := &xlsx.SheetData.Row[rowIdx]
for colIdx := range rowData.C {
colName, _, _ := SplitCellName(rowData.C[colIdx].R)
if colName == col {
rowData.C = append(rowData.C[:colIdx], rowData.C[colIdx+1:]...)[:len(rowData.C)-1]
break
}
}
}
return f.adjustHelper(sheet, columns, num, -1)
}
// convertColWidthToPixels provieds function to convert the width of a cell
// from user's units to pixels. Excel rounds the column width to the nearest
// pixel. If the width hasn't been set by the user we use the default value.
// If the column is hidden it has a value of zero.
func convertColWidthToPixels(width float64) float64 {
var padding float64 = 5
var pixels float64
var maxDigitWidth float64 = 7
if width == 0 {
return pixels
}
if width < 1 {
pixels = (width * 12) + 0.5
return math.Ceil(pixels)
}
pixels = (width*maxDigitWidth + 0.5) + padding
return math.Ceil(pixels)
}

View file

@ -0,0 +1,364 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"log"
"path/filepath"
"strconv"
"strings"
)
// parseFormatCommentsSet provides a function to parse the format settings of
// the comment with default value.
func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
format := formatComment{
Author: "Author:",
Text: " ",
}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
}
// GetComments retrieves all comments and returns a map of worksheet name to
// the worksheet comments.
func (f *File) GetComments() (comments map[string][]Comment) {
comments = map[string][]Comment{}
for n, path := range f.sheetMap {
if d := f.commentsReader("xl" + strings.TrimPrefix(f.getSheetComments(filepath.Base(path)), "..")); d != nil {
sheetComments := []Comment{}
for _, comment := range d.CommentList.Comment {
sheetComment := Comment{}
if comment.AuthorID < len(d.Authors) {
sheetComment.Author = d.Authors[comment.AuthorID].Author
}
sheetComment.Ref = comment.Ref
sheetComment.AuthorID = comment.AuthorID
if comment.Text.T != nil {
sheetComment.Text += *comment.Text.T
}
for _, text := range comment.Text.R {
if text.T != nil {
sheetComment.Text += text.T.Val
}
}
sheetComments = append(sheetComments, sheetComment)
}
comments[n] = sheetComments
}
}
return
}
// getSheetComments provides the method to get the target comment reference by
// given worksheet file path.
func (f *File) getSheetComments(sheetFile string) string {
var rels = "xl/worksheets/_rels/" + sheetFile + ".rels"
if sheetRels := f.relsReader(rels); sheetRels != nil {
for _, v := range sheetRels.Relationships {
if v.Type == SourceRelationshipComments {
return v.Target
}
}
}
return ""
}
// AddComment provides the method to add comment in a sheet by given worksheet
// index, cell and format set (such as author and text). Note that the max
// author length is 255 and the max text length is 32512. For example, add a
// comment in Sheet1!$A$30:
//
// err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
//
func (f *File) AddComment(sheet, cell, format string) error {
formatSet, err := parseFormatCommentsSet(format)
if err != nil {
return err
}
// Read sheet data.
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
commentID := f.countComments() + 1
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
sheetRelationshipsComments := "../comments" + strconv.Itoa(commentID) + ".xml"
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
if xlsx.LegacyDrawing != nil {
// The worksheet already has a comments relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, xlsx.LegacyDrawing.RID)
commentID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1)
} else {
// Add first comment for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "")
f.addSheetLegacyDrawing(sheet, rID)
}
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
var colCount int
for i, l := range strings.Split(formatSet.Text, "\n") {
if ll := len(l); ll > colCount {
if i == 0 {
ll += len(formatSet.Author)
}
colCount = ll
}
}
err = f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
if err != nil {
return err
}
f.addComment(commentsXML, cell, formatSet)
f.addContentTypePart(commentID, "comments")
return err
}
// addDrawingVML provides a function to create comment as
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) error {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
yAxis := col - 1
xAxis := row - 1
vml := f.VMLDrawing[drawingVML]
if vml == nil {
vml = &vmlDrawing{
XMLNSv: "urn:schemas-microsoft-com:vml",
XMLNSo: "urn:schemas-microsoft-com:office:office",
XMLNSx: "urn:schemas-microsoft-com:office:excel",
XMLNSmv: "http://macVmlSchemaUri",
Shapelayout: &xlsxShapelayout{
Ext: "edit",
IDmap: &xlsxIDmap{
Ext: "edit",
Data: commentID,
},
},
Shapetype: &xlsxShapetype{
ID: "_x0000_t202",
Coordsize: "21600,21600",
Spt: 202,
Path: "m0,0l0,21600,21600,21600,21600,0xe",
Stroke: &xlsxStroke{
Joinstyle: "miter",
},
VPath: &vPath{
Gradientshapeok: "t",
Connecttype: "miter",
},
},
}
}
sp := encodeShape{
Fill: &vFill{
Color2: "#fbfe82",
Angle: -180,
Type: "gradient",
Fill: &oFill{
Ext: "view",
Type: "gradientUnscaled",
},
},
Shadow: &vShadow{
On: "t",
Color: "black",
Obscured: "t",
},
Path: &vPath{
Connecttype: "none",
},
Textbox: &vTextbox{
Style: "mso-direction-alt:auto",
Div: &xlsxDiv{
Style: "text-align:left",
},
},
ClientData: &xClientData{
ObjectType: "Note",
Anchor: fmt.Sprintf(
"%d, 23, %d, 0, %d, %d, %d, 5",
1+yAxis, 1+xAxis, 2+yAxis+lineCount, colCount+yAxis, 2+xAxis+lineCount),
AutoFill: "True",
Row: xAxis,
Column: yAxis,
},
}
s, _ := xml.Marshal(sp)
shape := xlsxShape{
ID: "_x0000_s1025",
Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6",
Strokecolor: "#edeaa1",
Val: string(s[13 : len(s)-14]),
}
d := f.decodeVMLDrawingReader(drawingVML)
if d != nil {
for _, v := range d.Shape {
s := xlsxShape{
ID: "_x0000_s1025",
Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6",
Strokecolor: "#edeaa1",
Val: v.Val,
}
vml.Shape = append(vml.Shape, s)
}
}
vml.Shape = append(vml.Shape, shape)
f.VMLDrawing[drawingVML] = vml
return err
}
// addComment provides a function to create chart as xl/comments%d.xml by
// given cell and format sets.
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
a := formatSet.Author
t := formatSet.Text
if len(a) > 255 {
a = a[0:255]
}
if len(t) > 32512 {
t = t[0:32512]
}
comments := f.commentsReader(commentsXML)
if comments == nil {
comments = &xlsxComments{
Authors: []xlsxAuthor{
{
Author: formatSet.Author,
},
},
}
}
defaultFont := f.GetDefaultFont()
cmt := xlsxComment{
Ref: cell,
AuthorID: 0,
Text: xlsxText{
R: []xlsxR{
{
RPr: &xlsxRPr{
B: " ",
Sz: &attrValFloat{Val: float64Ptr(9)},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: stringPtr(defaultFont)},
Family: &attrValInt{Val: intPtr(2)},
},
T: &xlsxT{Val: a},
},
{
RPr: &xlsxRPr{
Sz: &attrValFloat{Val: float64Ptr(9)},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: stringPtr(defaultFont)},
Family: &attrValInt{Val: intPtr(2)},
},
T: &xlsxT{Val: t},
},
},
},
}
comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
f.Comments[commentsXML] = comments
}
// countComments provides a function to get comments files count storage in
// the folder xl.
func (f *File) countComments() int {
c1, c2 := 0, 0
for k := range f.XLSX {
if strings.Contains(k, "xl/comments") {
c1++
}
}
for rel := range f.Comments {
if strings.Contains(rel, "xl/comments") {
c2++
}
}
if c1 < c2 {
return c2
}
return c1
}
// decodeVMLDrawingReader provides a function to get the pointer to the
// structure after deserialization of xl/drawings/vmlDrawing%d.xml.
func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
var err error
if f.DecodeVMLDrawing[path] == nil {
c, ok := f.XLSX[path]
if ok {
f.DecodeVMLDrawing[path] = new(decodeVmlDrawing)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))).
Decode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
}
}
return f.DecodeVMLDrawing[path]
}
// vmlDrawingWriter provides a function to save xl/drawings/vmlDrawing%d.xml
// after serialize structure.
func (f *File) vmlDrawingWriter() {
for path, vml := range f.VMLDrawing {
if vml != nil {
v, _ := xml.Marshal(vml)
f.XLSX[path] = v
}
}
}
// commentsReader provides a function to get the pointer to the structure
// after deserialization of xl/comments%d.xml.
func (f *File) commentsReader(path string) *xlsxComments {
var err error
if f.Comments[path] == nil {
content, ok := f.XLSX[path]
if ok {
f.Comments[path] = new(xlsxComments)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))).
Decode(f.Comments[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
}
}
return f.Comments[path]
}
// commentsWriter provides a function to save xl/comments%d.xml after
// serialize structure.
func (f *File) commentsWriter() {
for path, c := range f.Comments {
if c != nil {
v, _ := xml.Marshal(c)
f.saveFileList(path, v)
}
}
}

View file

@ -0,0 +1,265 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"fmt"
"strings"
)
// DataValidationType defined the type of data validation.
type DataValidationType int
// Data validation types.
const (
_DataValidationType = iota
typeNone // inline use
DataValidationTypeCustom
DataValidationTypeDate
DataValidationTypeDecimal
typeList // inline use
DataValidationTypeTextLeng
DataValidationTypeTime
// DataValidationTypeWhole Integer
DataValidationTypeWhole
)
const (
// dataValidationFormulaStrLen 255 characters+ 2 quotes
dataValidationFormulaStrLen = 257
// dataValidationFormulaStrLenErr
dataValidationFormulaStrLenErr = "data validation must be 0-255 characters"
)
// DataValidationErrorStyle defined the style of data validation error alert.
type DataValidationErrorStyle int
// Data validation error styles.
const (
_ DataValidationErrorStyle = iota
DataValidationErrorStyleStop
DataValidationErrorStyleWarning
DataValidationErrorStyleInformation
)
// Data validation error styles.
const (
styleStop = "stop"
styleWarning = "warning"
styleInformation = "information"
)
// DataValidationOperator operator enum.
type DataValidationOperator int
// Data validation operators.
const (
_DataValidationOperator = iota
DataValidationOperatorBetween
DataValidationOperatorEqual
DataValidationOperatorGreaterThan
DataValidationOperatorGreaterThanOrEqual
DataValidationOperatorLessThan
DataValidationOperatorLessThanOrEqual
DataValidationOperatorNotBetween
DataValidationOperatorNotEqual
)
// NewDataValidation return data validation struct.
func NewDataValidation(allowBlank bool) *DataValidation {
return &DataValidation{
AllowBlank: allowBlank,
ShowErrorMessage: false,
ShowInputMessage: false,
}
}
// SetError set error notice.
func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
dd.Error = &msg
dd.ErrorTitle = &title
strStyle := styleStop
switch style {
case DataValidationErrorStyleStop:
strStyle = styleStop
case DataValidationErrorStyleWarning:
strStyle = styleWarning
case DataValidationErrorStyleInformation:
strStyle = styleInformation
}
dd.ShowErrorMessage = true
dd.ErrorStyle = &strStyle
}
// SetInput set prompt notice.
func (dd *DataValidation) SetInput(title, msg string) {
dd.ShowInputMessage = true
dd.PromptTitle = &title
dd.Prompt = &msg
}
// SetDropList data validation list.
func (dd *DataValidation) SetDropList(keys []string) error {
formula := "\"" + strings.Join(keys, ",") + "\""
if dataValidationFormulaStrLen < len(formula) {
return fmt.Errorf(dataValidationFormulaStrLenErr)
}
dd.Formula1 = fmt.Sprintf("<formula1>%s</formula1>", formula)
dd.Type = convDataValidationType(typeList)
return nil
}
// SetRange provides function to set data validation range in drop list.
func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error {
formula1 := fmt.Sprintf("%d", f1)
formula2 := fmt.Sprintf("%d", f2)
if dataValidationFormulaStrLen+21 < len(dd.Formula1) || dataValidationFormulaStrLen+21 < len(dd.Formula2) {
return fmt.Errorf(dataValidationFormulaStrLenErr)
}
dd.Formula1 = fmt.Sprintf("<formula1>%s</formula1>", formula1)
dd.Formula2 = fmt.Sprintf("<formula2>%s</formula2>", formula2)
dd.Type = convDataValidationType(t)
dd.Operator = convDataValidationOperatior(o)
return nil
}
// SetSqrefDropList provides set data validation on a range with source
// reference range of the worksheet by given data validation object and
// worksheet name. The data validation object can be created by
// NewDataValidation function. For example, set data validation on
// Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
// in-cell dropdown by allowing list source:
//
// dvRange := excelize.NewDataValidation(true)
// dvRange.Sqref = "A7:B8"
// dvRange.SetSqrefDropList("$E$1:$E$3", true)
// f.AddDataValidation("Sheet1", dvRange)
//
func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error {
if isCurrentSheet {
dd.Formula1 = fmt.Sprintf("<formula1>%s</formula1>", sqref)
dd.Type = convDataValidationType(typeList)
return nil
}
return fmt.Errorf("cross-sheet sqref cell are not supported")
}
// SetSqref provides function to set data validation range in drop list.
func (dd *DataValidation) SetSqref(sqref string) {
if dd.Sqref == "" {
dd.Sqref = sqref
} else {
dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref)
}
}
// convDataValidationType get excel data validation type.
func convDataValidationType(t DataValidationType) string {
typeMap := map[DataValidationType]string{
typeNone: "none",
DataValidationTypeCustom: "custom",
DataValidationTypeDate: "date",
DataValidationTypeDecimal: "decimal",
typeList: "list",
DataValidationTypeTextLeng: "textLength",
DataValidationTypeTime: "time",
DataValidationTypeWhole: "whole",
}
return typeMap[t]
}
// convDataValidationOperatior get excel data validation operator.
func convDataValidationOperatior(o DataValidationOperator) string {
typeMap := map[DataValidationOperator]string{
DataValidationOperatorBetween: "between",
DataValidationOperatorEqual: "equal",
DataValidationOperatorGreaterThan: "greaterThan",
DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
DataValidationOperatorLessThan: "lessThan",
DataValidationOperatorLessThanOrEqual: "lessThanOrEqual",
DataValidationOperatorNotBetween: "notBetween",
DataValidationOperatorNotEqual: "notEqual",
}
return typeMap[o]
}
// AddDataValidation provides set data validation on a range of the worksheet
// by given data validation object and worksheet name. The data validation
// object can be created by NewDataValidation function.
//
// Example 1, set data validation on Sheet1!A1:B2 with validation criteria
// settings, show error alert after invalid data is entered with "Stop" style
// and custom title "error body":
//
// dvRange := excelize.NewDataValidation(true)
// dvRange.Sqref = "A1:B2"
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
// dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
// err := f.AddDataValidation("Sheet1", dvRange)
//
// Example 2, set data validation on Sheet1!A3:B4 with validation criteria
// settings, and show input message when cell is selected:
//
// dvRange = excelize.NewDataValidation(true)
// dvRange.Sqref = "A3:B4"
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
// dvRange.SetInput("input title", "input body")
// err = f.AddDataValidation("Sheet1", dvRange)
//
// Example 3, set data validation on Sheet1!A5:B6 with validation criteria
// settings, create in-cell dropdown by allowing list source:
//
// dvRange = excelize.NewDataValidation(true)
// dvRange.Sqref = "A5:B6"
// dvRange.SetDropList([]string{"1", "2", "3"})
// err = f.AddDataValidation("Sheet1", dvRange)
//
func (f *File) AddDataValidation(sheet string, dv *DataValidation) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if nil == ws.DataValidations {
ws.DataValidations = new(xlsxDataValidations)
}
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dv)
ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
return err
}
// DeleteDataValidation delete data validation by given worksheet name and
// reference sequence.
func (f *File) DeleteDataValidation(sheet, sqref string) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if ws.DataValidations == nil {
return nil
}
dv := ws.DataValidations
for i := 0; i < len(dv.DataValidation); i++ {
if dv.DataValidation[i].Sqref == sqref {
dv.DataValidation = append(dv.DataValidation[:i], dv.DataValidation[i+1:]...)
i--
}
}
dv.Count = len(dv.DataValidation)
if dv.Count == 0 {
ws.DataValidations = nil
}
return nil
}

View file

@ -0,0 +1,182 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"errors"
"math"
"time"
)
const (
dayNanoseconds = 24 * time.Hour
maxDuration = 290 * 364 * dayNanoseconds
)
var (
excelMinTime1900 = time.Date(1899, time.December, 31, 0, 0, 0, 0, time.UTC)
excelBuggyPeriodStart = time.Date(1900, time.March, 1, 0, 0, 0, 0, time.UTC).Add(-time.Nanosecond)
)
// timeToExcelTime provides a function to convert time to Excel time.
func timeToExcelTime(t time.Time) (float64, error) {
// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
// Force user to explicit convet passed value to UTC time.
// Because for example 1900-01-01 00:00:00 +0300 MSK converts to 1900-01-01 00:00:00 +0230 LMT
// probably due to daylight saving.
if t.Location() != time.UTC {
return 0.0, errors.New("only UTC time expected")
}
if t.Before(excelMinTime1900) {
return 0.0, nil
}
tt := t
diff := t.Sub(excelMinTime1900)
result := float64(0)
for diff >= maxDuration {
result += float64(maxDuration / dayNanoseconds)
tt = tt.Add(-maxDuration)
diff = tt.Sub(excelMinTime1900)
}
rem := diff % dayNanoseconds
result += float64(diff-rem)/float64(dayNanoseconds) + float64(rem)/float64(dayNanoseconds)
// Excel dates after 28th February 1900 are actually one day out.
// Excel behaves as though the date 29th February 1900 existed, which it didn't.
// Microsoft intentionally included this bug in Excel so that it would remain compatible with the spreadsheet
// program that had the majority market share at the time; Lotus 1-2-3.
// https://www.myonlinetraininghub.com/excel-date-and-time
if t.After(excelBuggyPeriodStart) {
result += 1.0
}
return result, nil
}
// shiftJulianToNoon provides a function to process julian date to noon.
func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
switch {
case -0.5 < julianFraction && julianFraction < 0.5:
julianFraction += 0.5
case julianFraction >= 0.5:
julianDays++
julianFraction -= 0.5
case julianFraction <= -0.5:
julianDays--
julianFraction += 1.5
}
return julianDays, julianFraction
}
// fractionOfADay provides a function to return the integer values for hour,
// minutes, seconds and nanoseconds that comprised a given fraction of a day.
// values would round to 1 us.
func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
const (
c1us = 1e3
c1s = 1e9
c1day = 24 * 60 * 60 * c1s
)
frac := int64(c1day*fraction + c1us/2)
nanoseconds = int((frac%c1s)/c1us) * c1us
frac /= c1s
seconds = int(frac % 60)
frac /= 60
minutes = int(frac % 60)
hours = int(frac / 60)
return
}
// julianDateToGregorianTime provides a function to convert julian date to
// gregorian time.
func julianDateToGregorianTime(part1, part2 float64) time.Time {
part1I, part1F := math.Modf(part1)
part2I, part2F := math.Modf(part2)
julianDays := part1I + part2I
julianFraction := part1F + part2F
julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))
hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
}
// doTheFliegelAndVanFlandernAlgorithm; By this point generations of
// programmers have repeated the algorithm sent to the editor of
// "Communications of the ACM" in 1968 (published in CACM, volume 11, number
// 10, October 1968, p.657). None of those programmers seems to have found it
// necessary to explain the constants or variable names set out by Henry F.
// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and
// expand an explanation here - that day is not today.
func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
l := jd + 68569
n := (4 * l) / 146097
l = l - (146097*n+3)/4
i := (4000 * (l + 1)) / 1461001
l = l - (1461*i)/4 + 31
j := (80 * l) / 2447
d := l - (2447*j)/80
l = j / 11
m := j + 2 - (12 * l)
y := 100*(n-49) + i + l
return d, m, y
}
// timeFromExcelTime provides a function to convert an excelTime
// representation (stored as a floating point number) to a time.Time.
func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years
var date time.Time
var intPart = int64(excelTime)
// Excel uses Julian dates prior to March 1st 1900, and Gregorian
// thereafter.
if intPart <= 61 {
const OFFSET1900 = 15018.0
const OFFSET1904 = 16480.0
const MJD0 float64 = 2400000.5
var date time.Time
if date1904 {
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)
} else {
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)
}
return date
}
var floatPart = excelTime - float64(intPart)
var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
if date1904 {
date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC)
} else {
date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
}
// Duration is limited to aprox. 290 years
for intPart > MDD {
durationDays := time.Duration(MDD) * time.Hour * 24
date = date.Add(durationDays)
intPart = intPart - MDD
}
durationDays := time.Duration(intPart) * time.Hour * 24
durationPart := time.Duration(dayNanoSeconds * floatPart)
return date.Add(durationDays).Add(durationPart)
}
// ExcelDateToTime converts a float-based excel date representation to a time.Time.
func ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) {
if excelDate < 0 {
return time.Time{}, newInvalidExcelDateError(excelDate)
}
return timeFromExcelTime(excelDate, use1904Format), nil
}

View file

@ -0,0 +1,156 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"reflect"
)
// SetDocProps provides a function to set document core properties. The
// properties that can be set are:
//
// Property | Description
// ----------------+-----------------------------------------------------------------------------
// Title | The name given to the resource.
// |
// Subject | The topic of the content of the resource.
// |
// Creator | An entity primarily responsible for making the content of the resource.
// |
// Keywords | A delimited set of keywords to support searching and indexing. This is
// | typically a list of terms that are not available elsewhere in the properties.
// |
// Description | An explanation of the content of the resource.
// |
// LastModifiedBy | The user who performed the last modification. The identification is
// | environment-specific.
// |
// Language | The language of the intellectual content of the resource.
// |
// Identifier | An unambiguous reference to the resource within a given context.
// |
// Revision | The topic of the content of the resource.
// |
// ContentStatus | The status of the content. For example: Values might include "Draft",
// | "Reviewed" and "Final"
// |
// Category | A categorization of the content of this package.
// |
// Version | The version number. This value is set by the user or by the application.
//
// For example:
//
// err := f.SetDocProps(&excelize.DocProperties{
// Category: "category",
// ContentStatus: "Draft",
// Created: "2019-06-04T22:00:10Z",
// Creator: "Go Excelize",
// Description: "This file created by Go Excelize",
// Identifier: "xlsx",
// Keywords: "Spreadsheet",
// LastModifiedBy: "Go Author",
// Modified: "2019-06-04T22:00:10Z",
// Revision: "0",
// Subject: "Test Subject",
// Title: "Test Title",
// Language: "en-US",
// Version: "1.0.0",
// })
//
func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
var (
core *decodeCoreProperties
newProps *xlsxCoreProperties
fields []string
output []byte
immutable, mutable reflect.Value
field, val string
)
core = new(decodeCoreProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
Decode(core); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
}
newProps, err = &xlsxCoreProperties{
Dc: NameSpaceDublinCore,
Dcterms: NameSpaceDublinCoreTerms,
Dcmitype: NameSpaceDublinCoreMetadataIntiative,
XSI: NameSpaceXMLSchemaInstance,
Title: core.Title,
Subject: core.Subject,
Creator: core.Creator,
Keywords: core.Keywords,
Description: core.Description,
LastModifiedBy: core.LastModifiedBy,
Language: core.Language,
Identifier: core.Identifier,
Revision: core.Revision,
ContentStatus: core.ContentStatus,
Category: core.Category,
Version: core.Version,
}, nil
newProps.Created.Text, newProps.Created.Type, newProps.Modified.Text, newProps.Modified.Type =
core.Created.Text, core.Created.Type, core.Modified.Text, core.Modified.Type
fields = []string{
"Category", "ContentStatus", "Creator", "Description", "Identifier", "Keywords",
"LastModifiedBy", "Revision", "Subject", "Title", "Language", "Version",
}
immutable, mutable = reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem()
for _, field = range fields {
if val = immutable.FieldByName(field).String(); val != "" {
mutable.FieldByName(field).SetString(val)
}
}
if docProperties.Created != "" {
newProps.Created.Text = docProperties.Created
}
if docProperties.Modified != "" {
newProps.Modified.Text = docProperties.Modified
}
output, err = xml.Marshal(newProps)
f.saveFileList("docProps/core.xml", output)
return
}
// GetDocProps provides a function to get document core properties.
func (f *File) GetDocProps() (ret *DocProperties, err error) {
var core = new(decodeCoreProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
Decode(core); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
}
ret, err = &DocProperties{
Category: core.Category,
ContentStatus: core.ContentStatus,
Created: core.Created.Text,
Creator: core.Creator,
Description: core.Description,
Identifier: core.Identifier,
Keywords: core.Keywords,
LastModifiedBy: core.LastModifiedBy,
Modified: core.Modified.Text,
Revision: core.Revision,
Subject: core.Subject,
Title: core.Title,
Language: core.Language,
Version: core.Version,
}, nil
return
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "fmt"
func newInvalidColumnNameError(col string) error {
return fmt.Errorf("invalid column name %q", col)
}
func newInvalidRowNumberError(row int) error {
return fmt.Errorf("invalid row number %d", row)
}
func newInvalidCellNameError(cell string) error {
return fmt.Errorf("invalid cell name %q", cell)
}
func newInvalidExcelDateError(dateValue float64) error {
return fmt.Errorf("invalid date value %f, negative values are not supported supported", dateValue)
}

View file

@ -0,0 +1,360 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
// spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
// complex components by high compatibility, and provided streaming API for
// generating or reading data from a worksheet with huge amounts of data. This
// library needs Go version 1.10 or later.
//
// See https://xuri.me/excelize for more information about this package.
package excelize
import (
"archive/zip"
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"golang.org/x/net/html/charset"
)
// File define a populated XLSX file struct.
type File struct {
checked map[string]bool
sheetMap map[string]string
CalcChain *xlsxCalcChain
Comments map[string]*xlsxComments
ContentTypes *xlsxTypes
Drawings map[string]*xlsxWsDr
Path string
SharedStrings *xlsxSST
Sheet map[string]*xlsxWorksheet
SheetCount int
Styles *xlsxStyleSheet
Theme *xlsxTheme
DecodeVMLDrawing map[string]*decodeVmlDrawing
VMLDrawing map[string]*vmlDrawing
WorkBook *xlsxWorkbook
Relationships map[string]*xlsxRelationships
XLSX map[string][]byte
CharsetReader charsetTranscoderFn
}
type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
// OpenFile take the name of an XLSX file and returns a populated XLSX file
// struct for it.
func OpenFile(filename string) (*File, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
f, err := OpenReader(file)
if err != nil {
return nil, err
}
f.Path = filename
return f, nil
}
// newFile is object builder
func newFile() *File {
return &File{
checked: make(map[string]bool),
sheetMap: make(map[string]string),
Comments: make(map[string]*xlsxComments),
Drawings: make(map[string]*xlsxWsDr),
Sheet: make(map[string]*xlsxWorksheet),
DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
VMLDrawing: make(map[string]*vmlDrawing),
Relationships: make(map[string]*xlsxRelationships),
CharsetReader: charset.NewReaderLabel,
}
}
// OpenReader take an io.Reader and return a populated XLSX file.
func OpenReader(r io.Reader) (*File, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
if err != nil {
identifier := []byte{
// checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier
0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00,
0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00,
0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00,
0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
}
if bytes.Contains(b, identifier) {
return nil, errors.New("not support encrypted file currently")
}
return nil, err
}
file, sheetCount, err := ReadZipReader(zr)
if err != nil {
return nil, err
}
f := newFile()
f.SheetCount, f.XLSX = sheetCount, file
f.CalcChain = f.calcChainReader()
f.sheetMap = f.getSheetMap()
f.Styles = f.stylesReader()
f.Theme = f.themeReader()
return f, nil
}
// CharsetTranscoder Set user defined codepage transcoder function for open
// XLSX from non UTF-8 encoding.
func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f }
// Creates new XML decoder with charset reader.
func (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) {
ret = xml.NewDecoder(rdr)
ret.CharsetReader = f.CharsetReader
return
}
// setDefaultTimeStyle provides a function to set default numbers format for
// time.Time type cell value by given worksheet name, cell coordinates and
// number format code.
func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
s, err := f.GetCellStyle(sheet, axis)
if err != nil {
return err
}
if s == 0 {
style, _ := f.NewStyle(&Style{NumFmt: format})
_ = f.SetCellStyle(sheet, axis, axis, style)
}
return err
}
// workSheetReader provides a function to get the pointer to the structure
// after deserialization by given worksheet name.
func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) {
var (
name string
ok bool
)
if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok {
err = fmt.Errorf("sheet %s is not exist", sheet)
return
}
if xlsx = f.Sheet[name]; f.Sheet[name] == nil {
if strings.HasPrefix(name, "xl/chartsheets") {
err = fmt.Errorf("sheet %s is chart sheet", sheet)
return
}
xlsx = new(xlsxWorksheet)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
Decode(xlsx); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
}
err = nil
if f.checked == nil {
f.checked = make(map[string]bool)
}
if ok = f.checked[name]; !ok {
checkSheet(xlsx)
if err = checkRow(xlsx); err != nil {
return
}
f.checked[name] = true
}
f.Sheet[name] = xlsx
}
return
}
// checkSheet provides a function to fill each row element and make that is
// continuous in a worksheet of XML.
func checkSheet(xlsx *xlsxWorksheet) {
row := len(xlsx.SheetData.Row)
if row >= 1 {
lastRow := xlsx.SheetData.Row[row-1].R
if lastRow >= row {
row = lastRow
}
}
sheetData := xlsxSheetData{Row: make([]xlsxRow, row)}
for _, r := range xlsx.SheetData.Row {
sheetData.Row[r.R-1] = r
}
for i := 1; i <= row; i++ {
sheetData.Row[i-1].R = i
}
xlsx.SheetData = sheetData
}
// addRels provides a function to add relationships by given XML path,
// relationship type, target and target mode.
func (f *File) addRels(relPath, relType, target, targetMode string) int {
rels := f.relsReader(relPath)
if rels == nil {
rels = &xlsxRelationships{}
}
var rID int
for _, rel := range rels.Relationships {
ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
if ID > rID {
rID = ID
}
}
rID++
var ID bytes.Buffer
ID.WriteString("rId")
ID.WriteString(strconv.Itoa(rID))
rels.Relationships = append(rels.Relationships, xlsxRelationship{
ID: ID.String(),
Type: relType,
Target: target,
TargetMode: targetMode,
})
f.Relationships[relPath] = rels
return rID
}
// replaceRelationshipsNameSpaceBytes provides a function to replace
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
var newXmlns = []byte(templateNamespaceIDMap)
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
}
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
// Office Excel 2007 and 2010. This function will be remove value tag when met a
// cell have a linked value. Reference
// https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating
//
// Notice: after open XLSX file Excel will be update linked value and generate
// new value and will prompt save file or not.
//
// For example:
//
// <row r="19" spans="2:2">
// <c r="B19">
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
// <v>100</v>
// </c>
// </row>
//
// to
//
// <row r="19" spans="2:2">
// <c r="B19">
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
// </c>
// </row>
//
func (f *File) UpdateLinkedValue() error {
wb := f.workbookReader()
// recalculate formulas
wb.CalcPr = nil
for _, name := range f.GetSheetList() {
xlsx, err := f.workSheetReader(name)
if err != nil {
return err
}
for indexR := range xlsx.SheetData.Row {
for indexC, col := range xlsx.SheetData.Row[indexR].C {
if col.F != nil && col.V != "" {
xlsx.SheetData.Row[indexR].C[indexC].V = ""
xlsx.SheetData.Row[indexR].C[indexC].T = ""
}
}
}
}
return nil
}
// AddVBAProject provides the method to add vbaProject.bin file which contains
// functions and/or macros. The file extension should be .xlsm. For example:
//
// if err := f.SetSheetPrOptions("Sheet1", excelize.CodeName("Sheet1")); err != nil {
// fmt.Println(err)
// }
// if err := f.AddVBAProject("vbaProject.bin"); err != nil {
// fmt.Println(err)
// }
// if err := f.SaveAs("macros.xlsm"); err != nil {
// fmt.Println(err)
// }
//
func (f *File) AddVBAProject(bin string) error {
var err error
// Check vbaProject.bin exists first.
if _, err = os.Stat(bin); os.IsNotExist(err) {
return err
}
if path.Ext(bin) != ".bin" {
return errors.New("unsupported VBA project extension")
}
f.setContentTypePartVBAProjectExtensions()
wb := f.relsReader("xl/_rels/workbook.xml.rels")
var rID int
var ok bool
for _, rel := range wb.Relationships {
if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
ok = true
continue
}
t, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
if t > rID {
rID = t
}
}
rID++
if !ok {
wb.Relationships = append(wb.Relationships, xlsxRelationship{
ID: "rId" + strconv.Itoa(rID),
Target: "vbaProject.bin",
Type: SourceRelationshipVBAProject,
})
}
file, _ := ioutil.ReadFile(bin)
f.XLSX["xl/vbaProject.bin"] = file
return err
}
// setContentTypePartVBAProjectExtensions provides a function to set the
// content type for relationship parts and the main document part.
func (f *File) setContentTypePartVBAProjectExtensions() {
var ok bool
content := f.contentTypesReader()
for _, v := range content.Defaults {
if v.Extension == "bin" {
ok = true
}
}
for idx, o := range content.Overrides {
if o.PartName == "/xl/workbook.xml" {
content.Overrides[idx].ContentType = ContentTypeMacro
}
}
if !ok {
content.Defaults = append(content.Defaults, xlsxDefault{
Extension: "bin",
ContentType: ContentTypeVBA,
})
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,116 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"archive/zip"
"bytes"
"fmt"
"io"
"os"
)
// NewFile provides a function to create new file by default template. For
// example:
//
// xlsx := NewFile()
//
func NewFile() *File {
file := make(map[string][]byte)
file["_rels/.rels"] = []byte(XMLHeader + templateRels)
file["docProps/app.xml"] = []byte(XMLHeader + templateDocpropsApp)
file["docProps/core.xml"] = []byte(XMLHeader + templateDocpropsCore)
file["xl/_rels/workbook.xml.rels"] = []byte(XMLHeader + templateWorkbookRels)
file["xl/theme/theme1.xml"] = []byte(XMLHeader + templateTheme)
file["xl/worksheets/sheet1.xml"] = []byte(XMLHeader + templateSheet)
file["xl/styles.xml"] = []byte(XMLHeader + templateStyles)
file["xl/workbook.xml"] = []byte(XMLHeader + templateWorkbook)
file["[Content_Types].xml"] = []byte(XMLHeader + templateContentTypes)
f := newFile()
f.SheetCount, f.XLSX = 1, file
f.CalcChain = f.calcChainReader()
f.Comments = make(map[string]*xlsxComments)
f.ContentTypes = f.contentTypesReader()
f.Drawings = make(map[string]*xlsxWsDr)
f.Styles = f.stylesReader()
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
f.VMLDrawing = make(map[string]*vmlDrawing)
f.WorkBook = f.workbookReader()
f.Relationships = make(map[string]*xlsxRelationships)
f.Relationships["xl/_rels/workbook.xml.rels"] = f.relsReader("xl/_rels/workbook.xml.rels")
f.Sheet["xl/worksheets/sheet1.xml"], _ = f.workSheetReader("Sheet1")
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
f.Theme = f.themeReader()
return f
}
// Save provides a function to override the xlsx file with origin path.
func (f *File) Save() error {
if f.Path == "" {
return fmt.Errorf("no path defined for file, consider File.WriteTo or File.Write")
}
return f.SaveAs(f.Path)
}
// SaveAs provides a function to create or update to an xlsx file at the
// provided path.
func (f *File) SaveAs(name string) error {
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
return err
}
defer file.Close()
return f.Write(file)
}
// Write provides a function to write to an io.Writer.
func (f *File) Write(w io.Writer) error {
_, err := f.WriteTo(w)
return err
}
// WriteTo implements io.WriterTo to write the file.
func (f *File) WriteTo(w io.Writer) (int64, error) {
buf, err := f.WriteToBuffer()
if err != nil {
return 0, err
}
return buf.WriteTo(w)
}
// WriteToBuffer provides a function to get bytes.Buffer from the saved file.
func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
zw := zip.NewWriter(buf)
f.calcChainWriter()
f.commentsWriter()
f.contentTypesWriter()
f.drawingsWriter()
f.vmlDrawingWriter()
f.workBookWriter()
f.workSheetWriter()
f.relsWriter()
f.sharedStringsWriter()
f.styleSheetWriter()
for path, content := range f.XLSX {
fi, err := zw.Create(path)
if err != nil {
zw.Close()
return buf, err
}
_, err = fi.Write(content)
if err != nil {
zw.Close()
return buf, err
}
}
return buf, zw.Close()
}

View file

@ -0,0 +1,17 @@
module github.com/360EntSecGroup-Skylar/excelize/v2
go 1.12
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/stretchr/testify v1.5.1
github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f
golang.org/x/text v0.3.2 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)

View file

@ -0,0 +1,39 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91 h1:gp02YctZuIPTk0t7qI+wvg3VQwTPyNmSGG6ZqOsjSL8=
github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View file

@ -0,0 +1,139 @@
// Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package excelize
import (
"image/color"
"math"
)
// HSLModel converts any color.Color to a HSL color.
var HSLModel = color.ModelFunc(hslModel)
// HSL represents a cylindrical coordinate of points in an RGB color model.
//
// Values are in the range 0 to 1.
type HSL struct {
H, S, L float64
}
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the HSL.
func (c HSL) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := HSLToRGB(c.H, c.S, c.L)
return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
}
// hslModel converts a color.Color to HSL.
func hslModel(c color.Color) color.Color {
if _, ok := c.(HSL); ok {
return c
}
r, g, b, _ := c.RGBA()
h, s, l := RGBToHSL(uint8(r>>8), uint8(g>>8), uint8(b>>8))
return HSL{h, s, l}
}
// RGBToHSL converts an RGB triple to a HSL triple.
func RGBToHSL(r, g, b uint8) (h, s, l float64) {
fR := float64(r) / 255
fG := float64(g) / 255
fB := float64(b) / 255
max := math.Max(math.Max(fR, fG), fB)
min := math.Min(math.Min(fR, fG), fB)
l = (max + min) / 2
if max == min {
// Achromatic.
h, s = 0, 0
} else {
// Chromatic.
d := max - min
if l > 0.5 {
s = d / (2.0 - max - min)
} else {
s = d / (max + min)
}
switch max {
case fR:
h = (fG - fB) / d
if fG < fB {
h += 6
}
case fG:
h = (fB-fR)/d + 2
case fB:
h = (fR-fG)/d + 4
}
h /= 6
}
return
}
// HSLToRGB converts an HSL triple to a RGB triple.
func HSLToRGB(h, s, l float64) (r, g, b uint8) {
var fR, fG, fB float64
if s == 0 {
fR, fG, fB = l, l, l
} else {
var q float64
if l < 0.5 {
q = l * (1 + s)
} else {
q = l + s - s*l
}
p := 2*l - q
fR = hueToRGB(p, q, h+1.0/3)
fG = hueToRGB(p, q, h)
fB = hueToRGB(p, q, h-1.0/3)
}
r = uint8((fR * 255) + 0.5)
g = uint8((fG * 255) + 0.5)
b = uint8((fB * 255) + 0.5)
return
}
// hueToRGB is a helper function for HSLToRGB.
func hueToRGB(p, q, t float64) float64 {
if t < 0 {
t++
}
if t > 1 {
t--
}
if t < 1.0/6 {
return p + (q-p)*6*t
}
if t < 0.5 {
return q
}
if t < 2.0/3 {
return p + (q-p)*(2.0/3-t)*6
}
return p
}

View file

@ -0,0 +1,353 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"archive/zip"
"bytes"
"container/list"
"fmt"
"io"
"log"
"strconv"
"strings"
"unsafe"
)
// ReadZipReader can be used to read an XLSX in memory without touching the
// filesystem.
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
fileList := make(map[string][]byte, len(r.File))
worksheets := 0
for _, v := range r.File {
fileList[v.Name] = readFile(v)
if strings.HasPrefix(v.Name, "xl/worksheets/sheet") {
worksheets++
}
}
return fileList, worksheets, nil
}
// readXML provides a function to read XML content as string.
func (f *File) readXML(name string) []byte {
if content, ok := f.XLSX[name]; ok {
return content
}
return []byte{}
}
// saveFileList provides a function to update given file content in file list
// of XLSX.
func (f *File) saveFileList(name string, content []byte) {
newContent := make([]byte, 0, len(XMLHeader)+len(content))
newContent = append(newContent, []byte(XMLHeader)...)
newContent = append(newContent, content...)
f.XLSX[name] = newContent
}
// Read file content as string in a archive file.
func readFile(file *zip.File) []byte {
rc, err := file.Open()
if err != nil {
log.Fatal(err)
}
dat := make([]byte, 0, file.FileInfo().Size())
buff := bytes.NewBuffer(dat)
_, _ = io.Copy(buff, rc)
rc.Close()
return buff.Bytes()
}
// SplitCellName splits cell name to column name and row number.
//
// Example:
//
// excelize.SplitCellName("AK74") // return "AK", 74, nil
//
func SplitCellName(cell string) (string, int, error) {
alpha := func(r rune) bool {
return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
}
if strings.IndexFunc(cell, alpha) == 0 {
i := strings.LastIndexFunc(cell, alpha)
if i >= 0 && i < len(cell)-1 {
col, rowstr := cell[:i+1], cell[i+1:]
if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
return col, row, nil
}
}
}
return "", -1, newInvalidCellNameError(cell)
}
// JoinCellName joins cell name from column name and row number.
func JoinCellName(col string, row int) (string, error) {
normCol := strings.Map(func(rune rune) rune {
switch {
case 'A' <= rune && rune <= 'Z':
return rune
case 'a' <= rune && rune <= 'z':
return rune - 32
}
return -1
}, col)
if len(col) == 0 || len(col) != len(normCol) {
return "", newInvalidColumnNameError(col)
}
if row < 1 {
return "", newInvalidRowNumberError(row)
}
return normCol + strconv.Itoa(row), nil
}
// ColumnNameToNumber provides a function to convert Excel sheet column name
// to int. Column name case insensitive. The function returns an error if
// column name incorrect.
//
// Example:
//
// excelize.ColumnNameToNumber("AK") // returns 37, nil
//
func ColumnNameToNumber(name string) (int, error) {
if len(name) == 0 {
return -1, newInvalidColumnNameError(name)
}
col := 0
multi := 1
for i := len(name) - 1; i >= 0; i-- {
r := name[i]
if r >= 'A' && r <= 'Z' {
col += int(r-'A'+1) * multi
} else if r >= 'a' && r <= 'z' {
col += int(r-'a'+1) * multi
} else {
return -1, newInvalidColumnNameError(name)
}
multi *= 26
}
return col, nil
}
// ColumnNumberToName provides a function to convert the integer to Excel
// sheet column title.
//
// Example:
//
// excelize.ColumnNumberToName(37) // returns "AK", nil
//
func ColumnNumberToName(num int) (string, error) {
if num < 1 {
return "", fmt.Errorf("incorrect column number %d", num)
}
var col string
for num > 0 {
col = string((num-1)%26+65) + col
num = (num - 1) / 26
}
return col, nil
}
// CellNameToCoordinates converts alphanumeric cell name to [X, Y] coordinates
// or returns an error.
//
// Example:
//
// CellCoordinates("A1") // returns 1, 1, nil
// CellCoordinates("Z3") // returns 26, 3, nil
//
func CellNameToCoordinates(cell string) (int, int, error) {
const msg = "cannot convert cell %q to coordinates: %v"
colname, row, err := SplitCellName(cell)
if err != nil {
return -1, -1, fmt.Errorf(msg, cell, err)
}
col, err := ColumnNameToNumber(colname)
if err != nil {
return -1, -1, fmt.Errorf(msg, cell, err)
}
return col, row, nil
}
// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell
// name or returns an error.
//
// Example:
//
// CoordinatesToCellName(1, 1) // returns "A1", nil
//
func CoordinatesToCellName(col, row int) (string, error) {
if col < 1 || row < 1 {
return "", fmt.Errorf("invalid cell coordinates [%d, %d]", col, row)
}
colname, err := ColumnNumberToName(col)
if err != nil {
// Error should never happens here.
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
}
return fmt.Sprintf("%s%d", colname, row), nil
}
// boolPtr returns a pointer to a bool with the given value.
func boolPtr(b bool) *bool { return &b }
// intPtr returns a pointer to a int with the given value.
func intPtr(i int) *int { return &i }
// float64Ptr returns a pofloat64er to a float64 with the given value.
func float64Ptr(f float64) *float64 { return &f }
// stringPtr returns a pointer to a string with the given value.
func stringPtr(s string) *string { return &s }
// defaultTrue returns true if b is nil, or the pointed value.
func defaultTrue(b *bool) bool {
if b == nil {
return true
}
return *b
}
// parseFormatSet provides a method to convert format string to []byte and
// handle empty string.
func parseFormatSet(formatSet string) []byte {
if formatSet != "" {
return []byte(formatSet)
}
return []byte("{}")
}
// namespaceStrictToTransitional provides a method to convert Strict and
// Transitional namespaces.
func namespaceStrictToTransitional(content []byte) []byte {
var namespaceTranslationDic = map[string]string{
StrictSourceRelationship: SourceRelationship,
StrictSourceRelationshipChart: SourceRelationshipChart,
StrictSourceRelationshipComments: SourceRelationshipComments,
StrictSourceRelationshipImage: SourceRelationshipImage,
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
}
for s, n := range namespaceTranslationDic {
content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
}
return content
}
// stringToBytes cast a string to bytes pointer and assign the value of this
// pointer.
func stringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&s))
}
// bytesReplace replace old bytes with given new.
func bytesReplace(s, old, new []byte, n int) []byte {
if n == 0 {
return s
}
if len(old) < len(new) {
return bytes.Replace(s, old, new, n)
}
if n < 0 {
n = len(s)
}
var wid, i, j, w int
for i, j = 0, 0; i < len(s) && j < n; j++ {
wid = bytes.Index(s[i:], old)
if wid < 0 {
break
}
w += copy(s[w:], s[i:i+wid])
w += copy(s[w:], new)
i += wid + len(old)
}
w += copy(s[w:], s[i:])
return s[0:w]
}
// genSheetPasswd provides a method to generate password for worksheet
// protection by given plaintext. When an Excel sheet is being protected with
// a password, a 16-bit (two byte) long hash is generated. To verify a
// password, it is compared to the hash. Obviously, if the input data volume
// is great, numerous passwords will match the same hash. Here is the
// algorithm to create the hash value:
//
// take the ASCII values of all characters shift left the first character 1 bit,
// the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits,
// the highest bit of the 16-bit value is always 0 [signed short])
// XOR all these values
// XOR the count of characters
// XOR the constant 0xCE4B
func genSheetPasswd(plaintext string) string {
var password int64 = 0x0000
var charPos uint = 1
for _, v := range plaintext {
value := int64(v) << charPos
charPos++
rotatedBits := value >> 15 // rotated bits beyond bit 15
value &= 0x7fff // first 15 bits
password ^= (value | rotatedBits)
}
password ^= int64(len(plaintext))
password ^= 0xCE4B
return strings.ToUpper(strconv.FormatInt(password, 16))
}
// Stack defined an abstract data type that serves as a collection of elements.
type Stack struct {
list *list.List
}
// NewStack create a new stack.
func NewStack() *Stack {
list := list.New()
return &Stack{list}
}
// Push a value onto the top of the stack.
func (stack *Stack) Push(value interface{}) {
stack.list.PushBack(value)
}
// Pop the top item of the stack and return it.
func (stack *Stack) Pop() interface{} {
e := stack.list.Back()
if e != nil {
stack.list.Remove(e)
return e.Value
}
return nil
}
// Peek view the top item on the stack.
func (stack *Stack) Peek() interface{} {
e := stack.list.Back()
if e != nil {
return e.Value
}
return nil
}
// Len return the number of items in the stack.
func (stack *Stack) Len() int {
return stack.list.Len()
}
// Empty the stack.
func (stack *Stack) Empty() bool {
return stack.list.Len() == 0
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,194 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"fmt"
"strings"
)
// MergeCell provides a function to merge cells by given coordinate area and
// sheet name. For example create a merged cell of D3:E9 on Sheet1:
//
// err := f.MergeCell("Sheet1", "D3", "E9")
//
// If you create a merged cell that overlaps with another existing merged cell,
// those merged cells that already exist will be removed.
//
// B1(x1,y1) D1(x2,y1)
// +------------------------+
// | |
// A4(x3,y3) | C4(x4,y3) |
// +------------------------+ |
// | | | |
// | |B5(x1,y2) | D5(x2,y2)|
// | +------------------------+
// | |
// |A8(x3,y4) C8(x4,y4)|
// +------------------------+
//
func (f *File) MergeCell(sheet, hcell, vcell string) error {
rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell)
if err != nil {
return err
}
// Correct the coordinate area, such correct C1:B3 to B1:C3.
_ = sortCoordinates(rect1)
hcell, _ = CoordinatesToCellName(rect1[0], rect1[1])
vcell, _ = CoordinatesToCellName(rect1[2], rect1[3])
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
ref := hcell + ":" + vcell
if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
cellData := xlsx.MergeCells.Cells[i]
if cellData == nil {
continue
}
cc := strings.Split(cellData.Ref, ":")
if len(cc) != 2 {
return fmt.Errorf("invalid area %q", cellData.Ref)
}
rect2, err := f.areaRefToCoordinates(cellData.Ref)
if err != nil {
return err
}
// Delete the merged cells of the overlapping area.
if isOverlap(rect1, rect2) {
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
i--
if rect1[0] > rect2[0] {
rect1[0], rect2[0] = rect2[0], rect1[0]
}
if rect1[2] < rect2[2] {
rect1[2], rect2[2] = rect2[2], rect1[2]
}
if rect1[1] > rect2[1] {
rect1[1], rect2[1] = rect2[1], rect1[1]
}
if rect1[3] < rect2[3] {
rect1[3], rect2[3] = rect2[3], rect1[3]
}
hcell, _ = CoordinatesToCellName(rect1[0], rect1[1])
vcell, _ = CoordinatesToCellName(rect1[2], rect1[3])
ref = hcell + ":" + vcell
}
}
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &xlsxMergeCell{Ref: ref})
} else {
xlsx.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref}}}
}
return err
}
// UnmergeCell provides a function to unmerge a given coordinate area.
// For example unmerge area D3:E9 on Sheet1:
//
// err := f.UnmergeCell("Sheet1", "D3", "E9")
//
// Attention: overlapped areas will also be unmerged.
func (f *File) UnmergeCell(sheet string, hcell, vcell string) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell)
if err != nil {
return err
}
// Correct the coordinate area, such correct C1:B3 to B1:C3.
_ = sortCoordinates(rect1)
// return nil since no MergeCells in the sheet
if xlsx.MergeCells == nil {
return nil
}
i := 0
for _, cellData := range xlsx.MergeCells.Cells {
if cellData == nil {
continue
}
cc := strings.Split(cellData.Ref, ":")
if len(cc) != 2 {
return fmt.Errorf("invalid area %q", cellData.Ref)
}
rect2, err := f.areaRefToCoordinates(cellData.Ref)
if err != nil {
return err
}
if isOverlap(rect1, rect2) {
continue
}
xlsx.MergeCells.Cells[i] = cellData
i++
}
xlsx.MergeCells.Cells = xlsx.MergeCells.Cells[:i]
return nil
}
// GetMergeCells provides a function to get all merged cells from a worksheet
// currently.
func (f *File) GetMergeCells(sheet string) ([]MergeCell, error) {
var mergeCells []MergeCell
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return mergeCells, err
}
if xlsx.MergeCells != nil {
mergeCells = make([]MergeCell, 0, len(xlsx.MergeCells.Cells))
for i := range xlsx.MergeCells.Cells {
ref := xlsx.MergeCells.Cells[i].Ref
axis := strings.Split(ref, ":")[0]
val, _ := f.GetCellValue(sheet, axis)
mergeCells = append(mergeCells, []string{ref, val})
}
}
return mergeCells, err
}
// MergeCell define a merged cell data.
// It consists of the following structure.
// example: []string{"D4:E10", "cell value"}
type MergeCell []string
// GetCellValue returns merged cell value.
func (m *MergeCell) GetCellValue() string {
return (*m)[1]
}
// GetStartAxis returns the merge start axis.
// example: "C2"
func (m *MergeCell) GetStartAxis() string {
axis := strings.Split((*m)[0], ":")
return axis[0]
}
// GetEndAxis returns the merge end axis.
// example: "D4"
func (m *MergeCell) GetEndAxis() string {
axis := strings.Split((*m)[0], ":")
return axis[1]
}

View file

@ -0,0 +1,580 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"image"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
)
// parseFormatPictureSet provides a function to parse the format settings of
// the picture with default value.
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
format := formatPicture{
FPrintsWithSheet: true,
FLocksWithSheet: false,
NoChangeAspect: false,
OffsetX: 0,
OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
}
err := json.Unmarshal(parseFormatSet(formatSet), &format)
return &format, err
}
// AddPicture provides the method to add picture in a sheet by given picture
// format set (such as offset, scale, aspect ratio setting and print settings)
// and file path. For example:
//
// package main
//
// import (
// _ "image/gif"
// _ "image/jpeg"
// _ "image/png"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// f := excelize.NewFile()
// // Insert a picture.
// if err := f.AddPicture("Sheet1", "A2", "image.jpg", ""); err != nil {
// fmt.Println(err)
// }
// // Insert a picture scaling in the cell with location hyperlink.
// if err := f.AddPicture("Sheet1", "D2", "image.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`); err != nil {
// fmt.Println(err)
// }
// // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
// if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External", "print_obj": true, "lock_aspect_ratio": false, "locked": false, "positioning": "oneCell"}`); err != nil {
// fmt.Println(err)
// }
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
// LinkType defines two types of hyperlink "External" for web site or
// "Location" for moving to one of cell in this workbook. When the
// "hyperlink_type" is "Location", coordinates need to start with "#".
//
// Positioning defines two types of the position of a picture in an Excel
// spreadsheet, "oneCell" (Move but don't size with cells) or "absolute"
// (Don't move or size with cells). If you don't set this parameter, default
// positioning is move and size with cells.
func (f *File) AddPicture(sheet, cell, picture, format string) error {
var err error
// Check picture exists first.
if _, err = os.Stat(picture); os.IsNotExist(err) {
return err
}
ext, ok := supportImageTypes[path.Ext(picture)]
if !ok {
return errors.New("unsupported image extension")
}
file, _ := ioutil.ReadFile(picture)
_, name := filepath.Split(picture)
return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
}
// AddPictureFromBytes provides the method to add picture in a sheet by given
// picture format set (such as offset, scale, aspect ratio setting and print
// settings), file base name, extension name and file bytes. For example:
//
// package main
//
// import (
// "fmt"
// _ "image/jpeg"
// "io/ioutil"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// f := excelize.NewFile()
//
// file, err := ioutil.ReadFile("image.jpg")
// if err != nil {
// fmt.Println(err)
// }
// if err := f.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file); err != nil {
// fmt.Println(err)
// }
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
var drawingHyperlinkRID int
var hyperlinkType string
ext, ok := supportImageTypes[extension]
if !ok {
return errors.New("unsupported image extension")
}
formatSet, err := parseFormatPictureSet(format)
if err != nil {
return err
}
img, _, err := image.DecodeConfig(bytes.NewReader(file))
if err != nil {
return err
}
// Read sheet data.
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
drawingID := f.countDrawings() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
// Add picture with hyperlink.
if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
if formatSet.HyperlinkType == "External" {
hyperlinkType = formatSet.HyperlinkType
}
drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
}
err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
if err != nil {
return err
}
f.addContentTypePart(drawingID, "drawings")
return err
}
// deleteSheetRelationships provides a function to delete relationships in
// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
// relationship index.
func (f *File) deleteSheetRelationships(sheet, rID string) {
name, ok := f.sheetMap[trimSheetName(sheet)]
if !ok {
name = strings.ToLower(sheet) + ".xml"
}
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
}
for k, v := range sheetRels.Relationships {
if v.ID == rID {
sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)
}
}
f.Relationships[rels] = sheetRels
}
// addSheetLegacyDrawing provides a function to add legacy drawing element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
xlsx, _ := f.workSheetReader(sheet)
xlsx.LegacyDrawing = &xlsxLegacyDrawing{
RID: "rId" + strconv.Itoa(rID),
}
}
// addSheetDrawing provides a function to add drawing element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetDrawing(sheet string, rID int) {
xlsx, _ := f.workSheetReader(sheet)
xlsx.Drawing = &xlsxDrawing{
RID: "rId" + strconv.Itoa(rID),
}
}
// addSheetPicture provides a function to add picture element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetPicture(sheet string, rID int) {
xlsx, _ := f.workSheetReader(sheet)
xlsx.Picture = &xlsxPicture{
RID: "rId" + strconv.Itoa(rID),
}
}
// countDrawings provides a function to get drawing files count storage in the
// folder xl/drawings.
func (f *File) countDrawings() int {
c1, c2 := 0, 0
for k := range f.XLSX {
if strings.Contains(k, "xl/drawings/drawing") {
c1++
}
}
for rel := range f.Drawings {
if strings.Contains(rel, "xl/drawings/drawing") {
c2++
}
}
if c1 < c2 {
return c2
}
return c1
}
// addDrawingPicture provides a function to add picture by given sheet,
// drawingXML, cell, file name, width, height relationship index and format
// sets.
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) error {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
width = int(float64(width) * formatSet.XScale)
height = int(float64(height) * formatSet.YScale)
col--
row--
colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning
from := xlsxFrom{}
from.Col = colStart
from.ColOff = formatSet.OffsetX * EMU
from.Row = rowStart
from.RowOff = formatSet.OffsetY * EMU
to := xlsxTo{}
to.Col = colEnd
to.ColOff = x2 * EMU
to.Row = rowEnd
to.RowOff = y2 * EMU
twoCellAnchor.From = &from
twoCellAnchor.To = &to
pic := xlsxPic{}
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = formatSet.NoChangeAspect
pic.NvPicPr.CNvPr.ID = cNvPrID
pic.NvPicPr.CNvPr.Descr = file
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
if hyperlinkRID != 0 {
pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
R: SourceRelationship,
RID: "rId" + strconv.Itoa(hyperlinkRID),
}
}
pic.BlipFill.Blip.R = SourceRelationship
pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
pic.SpPr.PrstGeom.Prst = "rect"
twoCellAnchor.Pic = &pic
twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet,
FPrintsWithSheet: formatSet.FPrintsWithSheet,
}
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content
return err
}
// countMedia provides a function to get media files count storage in the
// folder xl/media/image.
func (f *File) countMedia() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/media/image") {
count++
}
}
return count
}
// addMedia provides a function to add a picture into folder xl/media/image by
// given file and extension name. Duplicate images are only actually stored once
// and drawings that use it will reference the same image.
func (f *File) addMedia(file []byte, ext string) string {
count := f.countMedia()
for name, existing := range f.XLSX {
if !strings.HasPrefix(name, "xl/media/image") {
continue
}
if bytes.Equal(file, existing) {
return name
}
}
media := "xl/media/image" + strconv.Itoa(count+1) + ext
f.XLSX[media] = file
return media
}
// setContentTypePartImageExtensions provides a function to set the content
// type for relationship parts and the Main Document part.
func (f *File) setContentTypePartImageExtensions() {
var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false}
content := f.contentTypesReader()
for _, v := range content.Defaults {
_, ok := imageTypes[v.Extension]
if ok {
imageTypes[v.Extension] = true
}
}
for k, v := range imageTypes {
if !v {
content.Defaults = append(content.Defaults, xlsxDefault{
Extension: k,
ContentType: "image/" + k,
})
}
}
}
// setContentTypePartVMLExtensions provides a function to set the content type
// for relationship parts and the Main Document part.
func (f *File) setContentTypePartVMLExtensions() {
vml := false
content := f.contentTypesReader()
for _, v := range content.Defaults {
if v.Extension == "vml" {
vml = true
}
}
if !vml {
content.Defaults = append(content.Defaults, xlsxDefault{
Extension: "vml",
ContentType: ContentTypeVML,
})
}
}
// addContentTypePart provides a function to add content type part
// relationships in the file [Content_Types].xml by given index.
func (f *File) addContentTypePart(index int, contentType string) {
setContentType := map[string]func(){
"comments": f.setContentTypePartVMLExtensions,
"drawings": f.setContentTypePartImageExtensions,
}
partNames := map[string]string{
"chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
"chartsheet": "/xl/chartsheets/sheet" + strconv.Itoa(index) + ".xml",
"comments": "/xl/comments" + strconv.Itoa(index) + ".xml",
"drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
"table": "/xl/tables/table" + strconv.Itoa(index) + ".xml",
"pivotTable": "/xl/pivotTables/pivotTable" + strconv.Itoa(index) + ".xml",
"pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml",
"sharedStrings": "/xl/sharedStrings.xml",
}
contentTypes := map[string]string{
"chart": ContentTypeDrawingML,
"chartsheet": ContentTypeSpreadSheetMLChartsheet,
"comments": ContentTypeSpreadSheetMLComments,
"drawings": ContentTypeDrawing,
"table": ContentTypeSpreadSheetMLTable,
"pivotTable": ContentTypeSpreadSheetMLPivotTable,
"pivotCache": ContentTypeSpreadSheetMLPivotCacheDefinition,
"sharedStrings": ContentTypeSpreadSheetMLSharedStrings,
}
s, ok := setContentType[contentType]
if ok {
s()
}
content := f.contentTypesReader()
for _, v := range content.Overrides {
if v.PartName == partNames[contentType] {
return
}
}
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: partNames[contentType],
ContentType: contentTypes[contentType],
})
}
// getSheetRelationshipsTargetByID provides a function to get Target attribute
// value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
// relationship index.
func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
name, ok := f.sheetMap[trimSheetName(sheet)]
if !ok {
name = strings.ToLower(sheet) + ".xml"
}
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
}
for _, v := range sheetRels.Relationships {
if v.ID == rID {
return v.Target
}
}
return ""
}
// GetPicture provides a function to get picture base name and raw content
// embed in XLSX by given worksheet and cell name. This function returns the
// file name in XLSX and file contents as []byte data types. For example:
//
// f, err := excelize.OpenFile("Book1.xlsx")
// if err != nil {
// fmt.Println(err)
// return
// }
// file, raw, err := f.GetPicture("Sheet1", "A2")
// if err != nil {
// fmt.Println(err)
// return
// }
// if err := ioutil.WriteFile(file, raw, 0644); err != nil {
// fmt.Println(err)
// }
//
func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return "", nil, err
}
col--
row--
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return "", nil, err
}
if xlsx.Drawing == nil {
return "", nil, err
}
target := f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
drawingXML := strings.Replace(target, "..", "xl", -1)
_, ok := f.XLSX[drawingXML]
if !ok {
return "", nil, err
}
drawingRelationships := strings.Replace(
strings.Replace(target, "../drawings", "xl/drawings/_rels", -1), ".xml", ".xml.rels", -1)
return f.getPicture(row, col, drawingXML, drawingRelationships)
}
// DeletePicture provides a function to delete charts in XLSX by given
// worksheet and cell name. Note that the image file won't be deleted from the
// document currently.
func (f *File) DeletePicture(sheet, cell string) (err error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return
}
col--
row--
ws, err := f.workSheetReader(sheet)
if err != nil {
return
}
if ws.Drawing == nil {
return
}
drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
return f.deleteDrawing(col, row, drawingXML, "Pic")
}
// getPicture provides a function to get picture base name and raw content
// embed in XLSX by given coordinates and drawing relationships.
func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {
var (
wsDr *xlsxWsDr
ok bool
deWsDr *decodeWsDr
drawRel *xlsxRelationship
deTwoCellAnchor *decodeTwoCellAnchor
)
wsDr, _ = f.drawingParser(drawingXML)
if ret, buf = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 {
return
}
deWsDr = new(decodeWsDr)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
Decode(deWsDr); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
}
err = nil
for _, anchor := range deWsDr.TwoCellAnchor {
deTwoCellAnchor = new(decodeTwoCellAnchor)
if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>")).
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
}
if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil {
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret, buf = filepath.Base(drawRel.Target), f.XLSX[strings.Replace(drawRel.Target, "..", "xl", -1)]
return
}
}
}
}
return
}
// getPictureFromWsDr provides a function to get picture base name and raw
// content in worksheet drawing by given coordinates and drawing
// relationships.
func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte) {
var (
ok bool
anchor *xdrCellAnchor
drawRel *xlsxRelationship
)
for _, anchor = range wsDr.TwoCellAnchor {
if anchor.From != nil && anchor.Pic != nil {
if anchor.From.Col == col && anchor.From.Row == row {
drawRel = f.getDrawingRelationships(drawingRelationships,
anchor.Pic.BlipFill.Blip.Embed)
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret, buf = filepath.Base(drawRel.Target), f.XLSX[strings.Replace(drawRel.Target, "..", "xl", -1)]
return
}
}
}
}
return
}
// getDrawingRelationships provides a function to get drawing relationships
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
// relationship ID.
func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
if drawingRels := f.relsReader(rels); drawingRels != nil {
for _, v := range drawingRels.Relationships {
if v.ID == rID {
return &v
}
}
}
return nil
}
// drawingsWriter provides a function to save xl/drawings/drawing%d.xml after
// serialize structure.
func (f *File) drawingsWriter() {
for path, d := range f.Drawings {
if d != nil {
v, _ := xml.Marshal(d)
f.saveFileList(path, v)
}
}
}

View file

@ -0,0 +1,593 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
)
// PivotTableOption directly maps the format settings of the pivot table.
type PivotTableOption struct {
DataRange string
PivotTableRange string
Rows []PivotTableField
Columns []PivotTableField
Data []PivotTableField
Filter []PivotTableField
}
// PivotTableField directly maps the field settings of the pivot table.
// Subtotal specifies the aggregation function that applies to this data
// field. The default value is sum. The possible values for this attribute
// are:
//
// Average
// Count
// CountNums
// Max
// Min
// Product
// StdDev
// StdDevp
// Sum
// Var
// Varp
//
// Name specifies the name of the data field. Maximum 255 characters
// are allowed in data field name, excess characters will be truncated.
type PivotTableField struct {
Data string
Name string
Subtotal string
}
// AddPivotTable provides the method to add pivot table by given pivot table
// options.
//
// For example, create a pivot table on the Sheet1!$G$2:$M$34 area with the
// region Sheet1!$A$1:$E$31 as the data source, summarize by sum for sales:
//
// package main
//
// import (
// "fmt"
// "math/rand"
//
// "github.com/360EntSecGroup-Skylar/excelize"
// )
//
// func main() {
// f := excelize.NewFile()
// // Create some data in a sheet
// month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
// year := []int{2017, 2018, 2019}
// types := []string{"Meat", "Dairy", "Beverages", "Produce"}
// region := []string{"East", "West", "North", "South"}
// f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"})
// for i := 0; i < 30; i++ {
// f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)])
// f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)])
// f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)])
// f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000))
// f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)])
// }
// if err := f.AddPivotTable(&excelize.PivotTableOption{
// DataRange: "Sheet1!$A$1:$E$31",
// PivotTableRange: "Sheet1!$G$2:$M$34",
// Rows: []excelize.PivotTableField{{Data: "Month"}, {Data: "Year"}},
// Filter: []excelize.PivotTableField{{Data: "Region"}},
// Columns: []excelize.PivotTableField{{Data: "Type"}},
// Data: []excelize.PivotTableField{{Data: "Sales", Name: "Summarize", Subtotal: "Sum"}},
// }); err != nil {
// fmt.Println(err)
// }
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
func (f *File) AddPivotTable(opt *PivotTableOption) error {
// parameter validation
dataSheet, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opt)
if err != nil {
return err
}
pivotTableID := f.countPivotTables() + 1
pivotCacheID := f.countPivotCache() + 1
sheetRelationshipsPivotTableXML := "../pivotTables/pivotTable" + strconv.Itoa(pivotTableID) + ".xml"
pivotTableXML := strings.Replace(sheetRelationshipsPivotTableXML, "..", "xl", -1)
pivotCacheXML := "xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(pivotCacheID) + ".xml"
err = f.addPivotCache(pivotCacheID, pivotCacheXML, opt, dataSheet)
if err != nil {
return err
}
// workbook pivot cache
workBookPivotCacheRID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipPivotCache, fmt.Sprintf("pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
cacheID := f.addWorkbookPivotCache(workBookPivotCacheRID)
pivotCacheRels := "xl/pivotTables/_rels/pivotTable" + strconv.Itoa(pivotTableID) + ".xml.rels"
// rId not used
_ = f.addRels(pivotCacheRels, SourceRelationshipPivotCache, fmt.Sprintf("../pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
err = f.addPivotTable(cacheID, pivotTableID, pivotTableXML, opt)
if err != nil {
return err
}
pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
f.addContentTypePart(pivotTableID, "pivotTable")
f.addContentTypePart(pivotCacheID, "pivotCache")
return nil
}
// parseFormatPivotTableSet provides a function to validate pivot table
// properties.
func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet, string, error) {
if opt == nil {
return nil, "", errors.New("parameter is required")
}
dataSheetName, _, err := f.adjustRange(opt.DataRange)
if err != nil {
return nil, "", fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
pivotTableSheetName, _, err := f.adjustRange(opt.PivotTableRange)
if err != nil {
return nil, "", fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
}
dataSheet, err := f.workSheetReader(dataSheetName)
if err != nil {
return dataSheet, "", err
}
pivotTableSheetPath, ok := f.sheetMap[trimSheetName(pivotTableSheetName)]
if !ok {
return dataSheet, pivotTableSheetPath, fmt.Errorf("sheet %s is not exist", pivotTableSheetName)
}
return dataSheet, pivotTableSheetPath, err
}
// adjustRange adjust range, for example: adjust Sheet1!$E$31:$A$1 to Sheet1!$A$1:$E$31
func (f *File) adjustRange(rangeStr string) (string, []int, error) {
if len(rangeStr) < 1 {
return "", []int{}, errors.New("parameter is required")
}
rng := strings.Split(rangeStr, "!")
if len(rng) != 2 {
return "", []int{}, errors.New("parameter is invalid")
}
trimRng := strings.Replace(rng[1], "$", "", -1)
coordinates, err := f.areaRefToCoordinates(trimRng)
if err != nil {
return rng[0], []int{}, err
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if x1 == x2 && y1 == y2 {
return rng[0], []int{}, errors.New("parameter is invalid")
}
// Correct the coordinate area, such correct C1:B3 to B1:C3.
if x2 < x1 {
x1, x2 = x2, x1
}
if y2 < y1 {
y1, y2 = y2, y1
}
return rng[0], []int{x1, y1, x2, y2}, nil
}
// getPivotFieldsOrder provides a function to get order list of pivot table
// fields.
func (f *File) getPivotFieldsOrder(dataRange string) ([]string, error) {
order := []string{}
dataSheet, coordinates, err := f.adjustRange(dataRange)
if err != nil {
return order, fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
for col := coordinates[0]; col <= coordinates[2]; col++ {
coordinate, _ := CoordinatesToCellName(col, coordinates[1])
name, err := f.GetCellValue(dataSheet, coordinate)
if err != nil {
return order, err
}
order = append(order, name)
}
return order, nil
}
// addPivotCache provides a function to create a pivot cache by given properties.
func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotTableOption, ws *xlsxWorksheet) error {
// validate data range
dataSheet, coordinates, err := f.adjustRange(opt.DataRange)
if err != nil {
return fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
// data range has been checked
order, _ := f.getPivotFieldsOrder(opt.DataRange)
hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
pc := xlsxPivotCacheDefinition{
SaveData: false,
RefreshOnLoad: true,
CacheSource: &xlsxCacheSource{
Type: "worksheet",
WorksheetSource: &xlsxWorksheetSource{
Ref: hcell + ":" + vcell,
Sheet: dataSheet,
},
},
CacheFields: &xlsxCacheFields{},
}
for _, name := range order {
pc.CacheFields.CacheField = append(pc.CacheFields.CacheField, &xlsxCacheField{
Name: name,
SharedItems: &xlsxSharedItems{
Count: 0,
},
})
}
pc.CacheFields.Count = len(pc.CacheFields.CacheField)
pivotCache, err := xml.Marshal(pc)
f.saveFileList(pivotCacheXML, pivotCache)
return err
}
// addPivotTable provides a function to create a pivot table by given pivot
// table ID and properties.
func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, opt *PivotTableOption) error {
// validate pivot table range
_, coordinates, err := f.adjustRange(opt.PivotTableRange)
if err != nil {
return fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
}
hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
pt := xlsxPivotTableDefinition{
Name: fmt.Sprintf("Pivot Table%d", pivotTableID),
CacheID: cacheID,
DataCaption: "Values",
Location: &xlsxLocation{
Ref: hcell + ":" + vcell,
FirstDataCol: 1,
FirstDataRow: 1,
FirstHeaderRow: 1,
},
PivotFields: &xlsxPivotFields{},
RowItems: &xlsxRowItems{
Count: 1,
I: []*xlsxI{
{
[]*xlsxX{{}, {}},
},
},
},
ColItems: &xlsxColItems{
Count: 1,
I: []*xlsxI{{}},
},
PivotTableStyleInfo: &xlsxPivotTableStyleInfo{
Name: "PivotStyleLight16",
ShowRowHeaders: true,
ShowColHeaders: true,
ShowLastColumn: true,
},
}
// pivot fields
_ = f.addPivotFields(&pt, opt)
// count pivot fields
pt.PivotFields.Count = len(pt.PivotFields.PivotField)
// data range has been checked
_ = f.addPivotRowFields(&pt, opt)
_ = f.addPivotColFields(&pt, opt)
_ = f.addPivotPageFields(&pt, opt)
_ = f.addPivotDataFields(&pt, opt)
pivotTable, err := xml.Marshal(pt)
f.saveFileList(pivotTableXML, pivotTable)
return err
}
// addPivotRowFields provides a method to add row fields for pivot table by
// given pivot table options.
func (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
// row fields
rowFieldsIndex, err := f.getPivotFieldsIndex(opt.Rows, opt)
if err != nil {
return err
}
for _, fieldIdx := range rowFieldsIndex {
if pt.RowFields == nil {
pt.RowFields = &xlsxRowFields{}
}
pt.RowFields.Field = append(pt.RowFields.Field, &xlsxField{
X: fieldIdx,
})
}
// count row fields
if pt.RowFields != nil {
pt.RowFields.Count = len(pt.RowFields.Field)
}
return err
}
// addPivotPageFields provides a method to add page fields for pivot table by
// given pivot table options.
func (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
// page fields
pageFieldsIndex, err := f.getPivotFieldsIndex(opt.Filter, opt)
if err != nil {
return err
}
pageFieldsName := f.getPivotTableFieldsName(opt.Filter)
for idx, pageField := range pageFieldsIndex {
if pt.PageFields == nil {
pt.PageFields = &xlsxPageFields{}
}
pt.PageFields.PageField = append(pt.PageFields.PageField, &xlsxPageField{
Name: pageFieldsName[idx],
Fld: pageField,
})
}
// count page fields
if pt.PageFields != nil {
pt.PageFields.Count = len(pt.PageFields.PageField)
}
return err
}
// addPivotDataFields provides a method to add data fields for pivot table by
// given pivot table options.
func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
// data fields
dataFieldsIndex, err := f.getPivotFieldsIndex(opt.Data, opt)
if err != nil {
return err
}
dataFieldsSubtotals := f.getPivotTableFieldsSubtotal(opt.Data)
dataFieldsName := f.getPivotTableFieldsName(opt.Data)
for idx, dataField := range dataFieldsIndex {
if pt.DataFields == nil {
pt.DataFields = &xlsxDataFields{}
}
pt.DataFields.DataField = append(pt.DataFields.DataField, &xlsxDataField{
Name: dataFieldsName[idx],
Fld: dataField,
Subtotal: dataFieldsSubtotals[idx],
})
}
// count data fields
if pt.DataFields != nil {
pt.DataFields.Count = len(pt.DataFields.DataField)
}
return err
}
// inStrSlice provides a method to check if an element is present in an array,
// and return the index of its location, otherwise return -1.
func inStrSlice(a []string, x string) int {
for idx, n := range a {
if x == n {
return idx
}
}
return -1
}
// inPivotTableField provides a method to check if an element is present in
// pivot table fields list, and return the index of its location, otherwise
// return -1.
func inPivotTableField(a []PivotTableField, x string) int {
for idx, n := range a {
if x == n.Data {
return idx
}
}
return -1
}
// addPivotColFields create pivot column fields by given pivot table
// definition and option.
func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
if len(opt.Columns) == 0 {
return nil
}
pt.ColFields = &xlsxColFields{}
// col fields
colFieldsIndex, err := f.getPivotFieldsIndex(opt.Columns, opt)
if err != nil {
return err
}
for _, fieldIdx := range colFieldsIndex {
pt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{
X: fieldIdx,
})
}
// count col fields
pt.ColFields.Count = len(pt.ColFields.Field)
return err
}
// addPivotFields create pivot fields based on the column order of the first
// row in the data region by given pivot table definition and option.
func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
order, err := f.getPivotFieldsOrder(opt.DataRange)
if err != nil {
return err
}
for _, name := range order {
if inPivotTableField(opt.Rows, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
Axis: "axisRow",
Name: f.getPivotTableFieldName(name, opt.Rows),
Items: &xlsxItems{
Count: 1,
Item: []*xlsxItem{
{T: "default"},
},
},
})
continue
}
if inPivotTableField(opt.Filter, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
Axis: "axisPage",
Name: f.getPivotTableFieldName(name, opt.Columns),
Items: &xlsxItems{
Count: 1,
Item: []*xlsxItem{
{T: "default"},
},
},
})
continue
}
if inPivotTableField(opt.Columns, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
Axis: "axisCol",
Name: f.getPivotTableFieldName(name, opt.Columns),
Items: &xlsxItems{
Count: 1,
Item: []*xlsxItem{
{T: "default"},
},
},
})
continue
}
if inPivotTableField(opt.Data, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
DataField: true,
})
continue
}
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{})
}
return err
}
// countPivotTables provides a function to get drawing files count storage in
// the folder xl/pivotTables.
func (f *File) countPivotTables() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/pivotTables/pivotTable") {
count++
}
}
return count
}
// countPivotCache provides a function to get drawing files count storage in
// the folder xl/pivotCache.
func (f *File) countPivotCache() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/pivotCache/pivotCacheDefinition") {
count++
}
}
return count
}
// getPivotFieldsIndex convert the column of the first row in the data region
// to a sequential index by given fields and pivot option.
func (f *File) getPivotFieldsIndex(fields []PivotTableField, opt *PivotTableOption) ([]int, error) {
pivotFieldsIndex := []int{}
orders, err := f.getPivotFieldsOrder(opt.DataRange)
if err != nil {
return pivotFieldsIndex, err
}
for _, field := range fields {
if pos := inStrSlice(orders, field.Data); pos != -1 {
pivotFieldsIndex = append(pivotFieldsIndex, pos)
}
}
return pivotFieldsIndex, nil
}
// getPivotTableFieldsSubtotal prepare fields subtotal by given pivot table fields.
func (f *File) getPivotTableFieldsSubtotal(fields []PivotTableField) []string {
field := make([]string, len(fields))
enums := []string{"average", "count", "countNums", "max", "min", "product", "stdDev", "stdDevp", "sum", "var", "varp"}
inEnums := func(enums []string, val string) string {
for _, enum := range enums {
if strings.ToLower(enum) == strings.ToLower(val) {
return enum
}
}
return "sum"
}
for idx, fld := range fields {
field[idx] = inEnums(enums, fld.Subtotal)
}
return field
}
// getPivotTableFieldsName prepare fields name list by given pivot table
// fields.
func (f *File) getPivotTableFieldsName(fields []PivotTableField) []string {
field := make([]string, len(fields))
for idx, fld := range fields {
if len(fld.Name) > 255 {
field[idx] = fld.Name[0:255]
continue
}
field[idx] = fld.Name
}
return field
}
// getPivotTableFieldName prepare field name by given pivot table fields.
func (f *File) getPivotTableFieldName(name string, fields []PivotTableField) string {
fieldsName := f.getPivotTableFieldsName(fields)
for idx, field := range fields {
if field.Data == name {
return fieldsName[idx]
}
}
return ""
}
// addWorkbookPivotCache add the association ID of the pivot cache in xl/workbook.xml.
func (f *File) addWorkbookPivotCache(RID int) int {
wb := f.workbookReader()
if wb.PivotCaches == nil {
wb.PivotCaches = &xlsxPivotCaches{}
}
cacheID := 1
for _, pivotCache := range wb.PivotCaches.PivotCache {
if pivotCache.CacheID > cacheID {
cacheID = pivotCache.CacheID
}
}
cacheID++
wb.PivotCaches.PivotCache = append(wb.PivotCaches.PivotCache, xlsxPivotCache{
CacheID: cacheID,
RID: fmt.Sprintf("rId%d", RID),
})
return cacheID
}

View file

@ -0,0 +1,656 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"log"
"math"
"strconv"
)
// GetRows return all the rows in a sheet by given worksheet name (case
// sensitive). For example:
//
// rows, err := f.Rows("Sheet1")
// if err != nil {
// fmt.Println(err)
// return
// }
// for rows.Next() {
// row, err := rows.Columns()
// if err != nil {
// fmt.Println(err)
// }
// for _, colCell := range row {
// fmt.Print(colCell, "\t")
// }
// fmt.Println()
// }
//
func (f *File) GetRows(sheet string) ([][]string, error) {
rows, err := f.Rows(sheet)
if err != nil {
return nil, err
}
results := make([][]string, 0, 64)
for rows.Next() {
if rows.Error() != nil {
break
}
row, err := rows.Columns()
if err != nil {
break
}
results = append(results, row)
}
return results, nil
}
// Rows defines an iterator to a sheet
type Rows struct {
err error
curRow, totalRow, stashRow int
sheet string
rows []xlsxRow
f *File
decoder *xml.Decoder
}
// Next will return true if find the next row element.
func (rows *Rows) Next() bool {
rows.curRow++
return rows.curRow <= rows.totalRow
}
// Error will return the error when the find next row element
func (rows *Rows) Error() error {
return rows.err
}
// Columns return the current row's column values
func (rows *Rows) Columns() ([]string, error) {
var (
err error
inElement string
row, cellCol int
columns []string
)
if rows.stashRow >= rows.curRow {
return columns, err
}
d := rows.f.sharedStringsReader()
for {
token, _ := rows.decoder.Token()
if token == nil {
break
}
switch startElement := token.(type) {
case xml.StartElement:
inElement = startElement.Name.Local
if inElement == "row" {
for _, attr := range startElement.Attr {
if attr.Name.Local == "r" {
row, err = strconv.Atoi(attr.Value)
if err != nil {
return columns, err
}
if row > rows.curRow {
rows.stashRow = row - 1
return columns, err
}
}
}
}
if inElement == "c" {
colCell := xlsxC{}
_ = rows.decoder.DecodeElement(&colCell, &startElement)
cellCol, _, err = CellNameToCoordinates(colCell.R)
if err != nil {
return columns, err
}
blank := cellCol - len(columns)
for i := 1; i < blank; i++ {
columns = append(columns, "")
}
val, _ := colCell.getValueFrom(rows.f, d)
columns = append(columns, val)
}
case xml.EndElement:
inElement = startElement.Name.Local
if inElement == "row" {
return columns, err
}
}
}
return columns, err
}
// ErrSheetNotExist defines an error of sheet is not exist
type ErrSheetNotExist struct {
SheetName string
}
func (err ErrSheetNotExist) Error() string {
return fmt.Sprintf("sheet %s is not exist", string(err.SheetName))
}
// Rows return a rows iterator. For example:
//
// rows, err := f.Rows("Sheet1")
// if err != nil {
// fmt.Println(err)
// return
// }
// for rows.Next() {
// row, err := rows.Columns()
// if err != nil {
// fmt.Println(err)
// }
// for _, colCell := range row {
// fmt.Print(colCell, "\t")
// }
// fmt.Println()
// }
//
func (f *File) Rows(sheet string) (*Rows, error) {
name, ok := f.sheetMap[trimSheetName(sheet)]
if !ok {
return nil, ErrSheetNotExist{sheet}
}
if f.Sheet[name] != nil {
// flush data
output, _ := xml.Marshal(f.Sheet[name])
f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
}
var (
err error
inElement string
row int
rows Rows
)
decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
for {
token, _ := decoder.Token()
if token == nil {
break
}
switch startElement := token.(type) {
case xml.StartElement:
inElement = startElement.Name.Local
if inElement == "row" {
for _, attr := range startElement.Attr {
if attr.Name.Local == "r" {
row, err = strconv.Atoi(attr.Value)
if err != nil {
return &rows, err
}
}
}
rows.totalRow = row
}
default:
}
}
rows.f = f
rows.sheet = name
rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
return &rows, nil
}
// SetRowHeight provides a function to set the height of a single row. For
// example, set the height of the first row in Sheet1:
//
// err := f.SetRowHeight("Sheet1", 1, 50)
//
func (f *File) SetRowHeight(sheet string, row int, height float64) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
prepareSheetXML(xlsx, 0, row)
rowIdx := row - 1
xlsx.SheetData.Row[rowIdx].Ht = height
xlsx.SheetData.Row[rowIdx].CustomHeight = true
return nil
}
// getRowHeight provides a function to get row height in pixels by given sheet
// name and row index.
func (f *File) getRowHeight(sheet string, row int) int {
xlsx, _ := f.workSheetReader(sheet)
for i := range xlsx.SheetData.Row {
v := &xlsx.SheetData.Row[i]
if v.R == row+1 && v.Ht != 0 {
return int(convertRowHeightToPixels(v.Ht))
}
}
// Optimisation for when the row heights haven't changed.
return int(defaultRowHeightPixels)
}
// GetRowHeight provides a function to get row height by given worksheet name
// and row index. For example, get the height of the first row in Sheet1:
//
// height, err := f.GetRowHeight("Sheet1", 1)
//
func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
if row < 1 {
return defaultRowHeightPixels, newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return defaultRowHeightPixels, err
}
if row > len(xlsx.SheetData.Row) {
return defaultRowHeightPixels, nil // it will be better to use 0, but we take care with BC
}
for _, v := range xlsx.SheetData.Row {
if v.R == row && v.Ht != 0 {
return v.Ht, nil
}
}
// Optimisation for when the row heights haven't changed.
return defaultRowHeightPixels, nil
}
// sharedStringsReader provides a function to get the pointer to the structure
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
var err error
if f.SharedStrings == nil {
var sharedStrings xlsxSST
ss := f.readXML("xl/sharedStrings.xml")
if len(ss) == 0 {
ss = f.readXML("xl/SharedStrings.xml")
}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
Decode(&sharedStrings); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
f.SharedStrings = &sharedStrings
}
return f.SharedStrings
}
// getValueFrom return a value from a column/row cell, this function is
// inteded to be used with for range on rows an argument with the xlsx opened
// file.
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
switch xlsx.T {
case "s":
if xlsx.V != "" {
xlsxSI := 0
xlsxSI, _ = strconv.Atoi(xlsx.V)
if len(d.SI) > xlsxSI {
return f.formattedValue(xlsx.S, d.SI[xlsxSI].String()), nil
}
}
return f.formattedValue(xlsx.S, xlsx.V), nil
case "str":
return f.formattedValue(xlsx.S, xlsx.V), nil
case "inlineStr":
if xlsx.IS != nil {
return f.formattedValue(xlsx.S, xlsx.IS.String()), nil
}
return f.formattedValue(xlsx.S, xlsx.V), nil
default:
return f.formattedValue(xlsx.S, xlsx.V), nil
}
}
// SetRowVisible provides a function to set visible of a single row by given
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
//
// err := f.SetRowVisible("Sheet1", 2, false)
//
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
prepareSheetXML(xlsx, 0, row)
xlsx.SheetData.Row[row-1].Hidden = !visible
return nil
}
// GetRowVisible provides a function to get visible of a single row by given
// worksheet name and Excel row number. For example, get visible state of row
// 2 in Sheet1:
//
// visible, err := f.GetRowVisible("Sheet1", 2)
//
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
if row < 1 {
return false, newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return false, err
}
if row > len(xlsx.SheetData.Row) {
return false, nil
}
return !xlsx.SheetData.Row[row-1].Hidden, nil
}
// SetRowOutlineLevel provides a function to set outline level number of a
// single row by given worksheet name and Excel row number. The value of
// parameter 'level' is 1-7. For example, outline row 2 in Sheet1 to level 1:
//
// err := f.SetRowOutlineLevel("Sheet1", 2, 1)
//
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
if level > 7 || level < 1 {
return errors.New("invalid outline level")
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
prepareSheetXML(xlsx, 0, row)
xlsx.SheetData.Row[row-1].OutlineLevel = level
return nil
}
// GetRowOutlineLevel provides a function to get outline level number of a
// single row by given worksheet name and Excel row number. For example, get
// outline number of row 2 in Sheet1:
//
// level, err := f.GetRowOutlineLevel("Sheet1", 2)
//
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
if row < 1 {
return 0, newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return 0, err
}
if row > len(xlsx.SheetData.Row) {
return 0, nil
}
return xlsx.SheetData.Row[row-1].OutlineLevel, nil
}
// RemoveRow provides a function to remove single row by given worksheet name
// and Excel row number. For example, remove row 3 in Sheet1:
//
// err := f.RemoveRow("Sheet1", 3)
//
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) RemoveRow(sheet string, row int) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if row > len(xlsx.SheetData.Row) {
return f.adjustHelper(sheet, rows, row, -1)
}
keep := 0
for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
v := &xlsx.SheetData.Row[rowIdx]
if v.R != row {
xlsx.SheetData.Row[keep] = *v
keep++
}
}
xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
return f.adjustHelper(sheet, rows, row, -1)
}
// InsertRow provides a function to insert a new row after given Excel row
// number starting from 1. For example, create a new row before row 3 in
// Sheet1:
//
// err := f.InsertRow("Sheet1", 3)
//
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) InsertRow(sheet string, row int) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
return f.adjustHelper(sheet, rows, row, 1)
}
// DuplicateRow inserts a copy of specified row (by its Excel row number) below
//
// err := f.DuplicateRow("Sheet1", 2)
//
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) DuplicateRow(sheet string, row int) error {
return f.DuplicateRowTo(sheet, row, row+1)
}
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
//
// err := f.DuplicateRowTo("Sheet1", 2, 7)
//
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
if row < 1 {
return newInvalidRowNumberError(row)
}
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
return nil
}
var ok bool
var rowCopy xlsxRow
for i, r := range xlsx.SheetData.Row {
if r.R == row {
rowCopy = xlsx.SheetData.Row[i]
ok = true
break
}
}
if !ok {
return nil
}
if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
return err
}
idx2 := -1
for i, r := range xlsx.SheetData.Row {
if r.R == row2 {
idx2 = i
break
}
}
if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
return nil
}
rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
f.ajustSingleRowDimensions(&rowCopy, row2)
if idx2 != -1 {
xlsx.SheetData.Row[idx2] = rowCopy
} else {
xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
}
return f.duplicateMergeCells(sheet, xlsx, row, row2)
}
// duplicateMergeCells merge cells in the destination row if there are single
// row merged cells in the copied row.
func (f *File) duplicateMergeCells(sheet string, xlsx *xlsxWorksheet, row, row2 int) error {
if xlsx.MergeCells == nil {
return nil
}
if row > row2 {
row++
}
for _, rng := range xlsx.MergeCells.Cells {
coordinates, err := f.areaRefToCoordinates(rng.Ref)
if err != nil {
return err
}
if coordinates[1] < row2 && row2 < coordinates[3] {
return nil
}
}
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
areaData := xlsx.MergeCells.Cells[i]
coordinates, _ := f.areaRefToCoordinates(areaData.Ref)
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if y1 == y2 && y1 == row {
from, _ := CoordinatesToCellName(x1, row2)
to, _ := CoordinatesToCellName(x2, row2)
if err := f.MergeCell(sheet, from, to); err != nil {
return err
}
i++
}
}
return nil
}
// checkRow provides a function to check and fill each column element for all
// rows and make that is continuous in a worksheet of XML. For example:
//
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
// <c r="A15" s="2" />
// <c r="B15" s="2" />
// <c r="F15" s="1" />
// <c r="G15" s="1" />
// </row>
//
// in this case, we should to change it to
//
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
// <c r="A15" s="2" />
// <c r="B15" s="2" />
// <c r="C15" s="2" />
// <c r="D15" s="2" />
// <c r="E15" s="2" />
// <c r="F15" s="1" />
// <c r="G15" s="1" />
// </row>
//
// Noteice: this method could be very slow for large spreadsheets (more than
// 3000 rows one sheet).
func checkRow(xlsx *xlsxWorksheet) error {
for rowIdx := range xlsx.SheetData.Row {
rowData := &xlsx.SheetData.Row[rowIdx]
colCount := len(rowData.C)
if colCount == 0 {
continue
}
// check and fill the cell without r attribute in a row element
rCount := 0
for idx, cell := range rowData.C {
rCount++
if cell.R != "" {
lastR, _, err := CellNameToCoordinates(cell.R)
if err != nil {
return err
}
if lastR > rCount {
rCount = lastR
}
continue
}
rowData.C[idx].R, _ = CoordinatesToCellName(rCount, rowIdx+1)
}
lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
if err != nil {
return err
}
if colCount < lastCol {
oldList := rowData.C
newlist := make([]xlsxC, 0, lastCol)
rowData.C = xlsx.SheetData.Row[rowIdx].C[:0]
for colIdx := 0; colIdx < lastCol; colIdx++ {
cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
if err != nil {
return err
}
newlist = append(newlist, xlsxC{R: cellName})
}
rowData.C = newlist
for colIdx := range oldList {
colData := &oldList[colIdx]
colNum, _, err := CellNameToCoordinates(colData.R)
if err != nil {
return err
}
xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
}
}
}
return nil
}
// convertRowHeightToPixels provides a function to convert the height of a
// cell from user's units to pixels. If the height hasn't been set by the user
// we use the default value. If the row is hidden it has a value of zero.
func convertRowHeightToPixels(height float64) float64 {
var pixels float64
if height == 0 {
return pixels
}
pixels = math.Ceil(4.0 / 3.0 * height)
return pixels
}

View file

@ -0,0 +1,459 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/json"
"strconv"
"strings"
)
// parseFormatShapeSet provides a function to parse the format settings of the
// shape with default value.
func parseFormatShapeSet(formatSet string) (*formatShape, error) {
format := formatShape{
Width: 160,
Height: 160,
Format: formatPicture{
FPrintsWithSheet: true,
FLocksWithSheet: false,
NoChangeAspect: false,
OffsetX: 0,
OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
},
}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
}
// AddShape provides the method to add shape in a sheet by given worksheet
// index, shape format set (such as offset, scale, aspect ratio setting and
// print settings) and properties set. For example, add text box (rect shape)
// in Sheet1:
//
// err := f.AddShape("Sheet1", "G6", `{"type":"rect","color":{"line":"#4286F4","fill":"#8eb9ff"},"paragraph":[{"text":"Rectangle Shape","font":{"bold":true,"italic":true,"family":"Times New Roman","size":36,"color":"#777777","underline":"sng"}}],"width":180,"height": 90}`)
//
// The following shows the type of shape supported by excelize:
//
// accentBorderCallout1 (Callout 1 with Border and Accent Shape)
// accentBorderCallout2 (Callout 2 with Border and Accent Shape)
// accentBorderCallout3 (Callout 3 with Border and Accent Shape)
// accentCallout1 (Callout 1 Shape)
// accentCallout2 (Callout 2 Shape)
// accentCallout3 (Callout 3 Shape)
// actionButtonBackPrevious (Back or Previous Button Shape)
// actionButtonBeginning (Beginning Button Shape)
// actionButtonBlank (Blank Button Shape)
// actionButtonDocument (Document Button Shape)
// actionButtonEnd (End Button Shape)
// actionButtonForwardNext (Forward or Next Button Shape)
// actionButtonHelp (Help Button Shape)
// actionButtonHome (Home Button Shape)
// actionButtonInformation (Information Button Shape)
// actionButtonMovie (Movie Button Shape)
// actionButtonReturn (Return Button Shape)
// actionButtonSound (Sound Button Shape)
// arc (Curved Arc Shape)
// bentArrow (Bent Arrow Shape)
// bentConnector2 (Bent Connector 2 Shape)
// bentConnector3 (Bent Connector 3 Shape)
// bentConnector4 (Bent Connector 4 Shape)
// bentConnector5 (Bent Connector 5 Shape)
// bentUpArrow (Bent Up Arrow Shape)
// bevel (Bevel Shape)
// blockArc (Block Arc Shape)
// borderCallout1 (Callout 1 with Border Shape)
// borderCallout2 (Callout 2 with Border Shape)
// borderCallout3 (Callout 3 with Border Shape)
// bracePair (Brace Pair Shape)
// bracketPair (Bracket Pair Shape)
// callout1 (Callout 1 Shape)
// callout2 (Callout 2 Shape)
// callout3 (Callout 3 Shape)
// can (Can Shape)
// chartPlus (Chart Plus Shape)
// chartStar (Chart Star Shape)
// chartX (Chart X Shape)
// chevron (Chevron Shape)
// chord (Chord Shape)
// circularArrow (Circular Arrow Shape)
// cloud (Cloud Shape)
// cloudCallout (Callout Cloud Shape)
// corner (Corner Shape)
// cornerTabs (Corner Tabs Shape)
// cube (Cube Shape)
// curvedConnector2 (Curved Connector 2 Shape)
// curvedConnector3 (Curved Connector 3 Shape)
// curvedConnector4 (Curved Connector 4 Shape)
// curvedConnector5 (Curved Connector 5 Shape)
// curvedDownArrow (Curved Down Arrow Shape)
// curvedLeftArrow (Curved Left Arrow Shape)
// curvedRightArrow (Curved Right Arrow Shape)
// curvedUpArrow (Curved Up Arrow Shape)
// decagon (Decagon Shape)
// diagStripe (Diagonal Stripe Shape)
// diamond (Diamond Shape)
// dodecagon (Dodecagon Shape)
// donut (Donut Shape)
// doubleWave (Double Wave Shape)
// downArrow (Down Arrow Shape)
// downArrowCallout (Callout Down Arrow Shape)
// ellipse (Ellipse Shape)
// ellipseRibbon (Ellipse Ribbon Shape)
// ellipseRibbon2 (Ellipse Ribbon 2 Shape)
// flowChartAlternateProcess (Alternate Process Flow Shape)
// flowChartCollate (Collate Flow Shape)
// flowChartConnector (Connector Flow Shape)
// flowChartDecision (Decision Flow Shape)
// flowChartDelay (Delay Flow Shape)
// flowChartDisplay (Display Flow Shape)
// flowChartDocument (Document Flow Shape)
// flowChartExtract (Extract Flow Shape)
// flowChartInputOutput (Input Output Flow Shape)
// flowChartInternalStorage (Internal Storage Flow Shape)
// flowChartMagneticDisk (Magnetic Disk Flow Shape)
// flowChartMagneticDrum (Magnetic Drum Flow Shape)
// flowChartMagneticTape (Magnetic Tape Flow Shape)
// flowChartManualInput (Manual Input Flow Shape)
// flowChartManualOperation (Manual Operation Flow Shape)
// flowChartMerge (Merge Flow Shape)
// flowChartMultidocument (Multi-Document Flow Shape)
// flowChartOfflineStorage (Offline Storage Flow Shape)
// flowChartOffpageConnector (Off-Page Connector Flow Shape)
// flowChartOnlineStorage (Online Storage Flow Shape)
// flowChartOr (Or Flow Shape)
// flowChartPredefinedProcess (Predefined Process Flow Shape)
// flowChartPreparation (Preparation Flow Shape)
// flowChartProcess (Process Flow Shape)
// flowChartPunchedCard (Punched Card Flow Shape)
// flowChartPunchedTape (Punched Tape Flow Shape)
// flowChartSort (Sort Flow Shape)
// flowChartSummingJunction (Summing Junction Flow Shape)
// flowChartTerminator (Terminator Flow Shape)
// foldedCorner (Folded Corner Shape)
// frame (Frame Shape)
// funnel (Funnel Shape)
// gear6 (Gear 6 Shape)
// gear9 (Gear 9 Shape)
// halfFrame (Half Frame Shape)
// heart (Heart Shape)
// heptagon (Heptagon Shape)
// hexagon (Hexagon Shape)
// homePlate (Home Plate Shape)
// horizontalScroll (Horizontal Scroll Shape)
// irregularSeal1 (Irregular Seal 1 Shape)
// irregularSeal2 (Irregular Seal 2 Shape)
// leftArrow (Left Arrow Shape)
// leftArrowCallout (Callout Left Arrow Shape)
// leftBrace (Left Brace Shape)
// leftBracket (Left Bracket Shape)
// leftCircularArrow (Left Circular Arrow Shape)
// leftRightArrow (Left Right Arrow Shape)
// leftRightArrowCallout (Callout Left Right Arrow Shape)
// leftRightCircularArrow (Left Right Circular Arrow Shape)
// leftRightRibbon (Left Right Ribbon Shape)
// leftRightUpArrow (Left Right Up Arrow Shape)
// leftUpArrow (Left Up Arrow Shape)
// lightningBolt (Lightning Bolt Shape)
// line (Line Shape)
// lineInv (Line Inverse Shape)
// mathDivide (Divide Math Shape)
// mathEqual (Equal Math Shape)
// mathMinus (Minus Math Shape)
// mathMultiply (Multiply Math Shape)
// mathNotEqual (Not Equal Math Shape)
// mathPlus (Plus Math Shape)
// moon (Moon Shape)
// nonIsoscelesTrapezoid (Non-Isosceles Trapezoid Shape)
// noSmoking (No Smoking Shape)
// notchedRightArrow (Notched Right Arrow Shape)
// octagon (Octagon Shape)
// parallelogram (Parallelogram Shape)
// pentagon (Pentagon Shape)
// pie (Pie Shape)
// pieWedge (Pie Wedge Shape)
// plaque (Plaque Shape)
// plaqueTabs (Plaque Tabs Shape)
// plus (Plus Shape)
// quadArrow (Quad-Arrow Shape)
// quadArrowCallout (Callout Quad-Arrow Shape)
// rect (Rectangle Shape)
// ribbon (Ribbon Shape)
// ribbon2 (Ribbon 2 Shape)
// rightArrow (Right Arrow Shape)
// rightArrowCallout (Callout Right Arrow Shape)
// rightBrace (Right Brace Shape)
// rightBracket (Right Bracket Shape)
// round1Rect (One Round Corner Rectangle Shape)
// round2DiagRect (Two Diagonal Round Corner Rectangle Shape)
// round2SameRect (Two Same-side Round Corner Rectangle Shape)
// roundRect (Round Corner Rectangle Shape)
// rtTriangle (Right Triangle Shape)
// smileyFace (Smiley Face Shape)
// snip1Rect (One Snip Corner Rectangle Shape)
// snip2DiagRect (Two Diagonal Snip Corner Rectangle Shape)
// snip2SameRect (Two Same-side Snip Corner Rectangle Shape)
// snipRoundRect (One Snip One Round Corner Rectangle Shape)
// squareTabs (Square Tabs Shape)
// star10 (Ten Pointed Star Shape)
// star12 (Twelve Pointed Star Shape)
// star16 (Sixteen Pointed Star Shape)
// star24 (Twenty Four Pointed Star Shape)
// star32 (Thirty Two Pointed Star Shape)
// star4 (Four Pointed Star Shape)
// star5 (Five Pointed Star Shape)
// star6 (Six Pointed Star Shape)
// star7 (Seven Pointed Star Shape)
// star8 (Eight Pointed Star Shape)
// straightConnector1 (Straight Connector 1 Shape)
// stripedRightArrow (Striped Right Arrow Shape)
// sun (Sun Shape)
// swooshArrow (Swoosh Arrow Shape)
// teardrop (Teardrop Shape)
// trapezoid (Trapezoid Shape)
// triangle (Triangle Shape)
// upArrow (Up Arrow Shape)
// upArrowCallout (Callout Up Arrow Shape)
// upDownArrow (Up Down Arrow Shape)
// upDownArrowCallout (Callout Up Down Arrow Shape)
// uturnArrow (U-Turn Arrow Shape)
// verticalScroll (Vertical Scroll Shape)
// wave (Wave Shape)
// wedgeEllipseCallout (Callout Wedge Ellipse Shape)
// wedgeRectCallout (Callout Wedge Rectangle Shape)
// wedgeRoundRectCallout (Callout Wedge Round Rectangle Shape)
//
// The following shows the type of text underline supported by excelize:
//
// none
// words
// sng
// dbl
// heavy
// dotted
// dottedHeavy
// dash
// dashHeavy
// dashLong
// dashLongHeavy
// dotDash
// dotDashHeavy
// dotDotDash
// dotDotDashHeavy
// wavy
// wavyHeavy
// wavyDbl
//
func (f *File) AddShape(sheet, cell, format string) error {
formatSet, err := parseFormatShapeSet(format)
if err != nil {
return err
}
// Read sheet data.
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
// Add first shape for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
drawingID := f.countDrawings() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
if xlsx.Drawing != nil {
// The worksheet already has a shape or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
} else {
// Add first shape for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
f.addSheetDrawing(sheet, rID)
}
err = f.addDrawingShape(sheet, drawingXML, cell, formatSet)
if err != nil {
return err
}
f.addContentTypePart(drawingID, "drawings")
return err
}
// addDrawingShape provides a function to add preset geometry by given sheet,
// drawingXMLand format sets.
func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) error {
fromCol, fromRow, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
colIdx := fromCol - 1
rowIdx := fromRow - 1
textUnderlineType := map[string]bool{
"none": true,
"words": true,
"sng": true,
"dbl": true,
"heavy": true,
"dotted": true,
"dottedHeavy": true,
"dash": true,
"dashHeavy": true,
"dashLong": true,
"dashLongHeavy": true,
"dotDash": true,
"dotDashHeavy": true,
"dotDotDash": true,
"dotDotDashHeavy": true,
"wavy": true,
"wavyHeavy": true,
"wavyDbl": true,
}
width := int(float64(formatSet.Width) * formatSet.Format.XScale)
height := int(float64(formatSet.Height) * formatSet.Format.YScale)
colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY,
width, height)
content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Format.Positioning
from := xlsxFrom{}
from.Col = colStart
from.ColOff = formatSet.Format.OffsetX * EMU
from.Row = rowStart
from.RowOff = formatSet.Format.OffsetY * EMU
to := xlsxTo{}
to.Col = colEnd
to.ColOff = x2 * EMU
to.Row = rowEnd
to.RowOff = y2 * EMU
twoCellAnchor.From = &from
twoCellAnchor.To = &to
shape := xdrSp{
NvSpPr: &xdrNvSpPr{
CNvPr: &xlsxCNvPr{
ID: cNvPrID,
Name: "Shape " + strconv.Itoa(cNvPrID),
},
CNvSpPr: &xdrCNvSpPr{
TxBox: true,
},
},
SpPr: &xlsxSpPr{
PrstGeom: xlsxPrstGeom{
Prst: formatSet.Type,
},
},
Style: &xdrStyle{
LnRef: setShapeRef(formatSet.Color.Line, 2),
FillRef: setShapeRef(formatSet.Color.Fill, 1),
EffectRef: setShapeRef(formatSet.Color.Effect, 0),
FontRef: &aFontRef{
Idx: "minor",
SchemeClr: &attrValString{
Val: stringPtr("tx1"),
},
},
},
TxBody: &xdrTxBody{
BodyPr: &aBodyPr{
VertOverflow: "clip",
HorzOverflow: "clip",
Wrap: "none",
RtlCol: false,
Anchor: "t",
},
},
}
if len(formatSet.Paragraph) < 1 {
formatSet.Paragraph = []formatShapeParagraph{
{
Font: Font{
Bold: false,
Italic: false,
Underline: "none",
Family: f.GetDefaultFont(),
Size: 11,
Color: "#000000",
},
Text: " ",
},
}
}
for _, p := range formatSet.Paragraph {
u := p.Font.Underline
_, ok := textUnderlineType[u]
if !ok {
u = "none"
}
text := p.Text
if text == "" {
text = " "
}
paragraph := &aP{
R: &aR{
RPr: aRPr{
I: p.Font.Italic,
B: p.Font.Bold,
Lang: "en-US",
AltLang: "en-US",
U: u,
Sz: p.Font.Size * 100,
Latin: &aLatin{Typeface: p.Font.Family},
},
T: text,
},
EndParaRPr: &aEndParaRPr{
Lang: "en-US",
},
}
srgbClr := strings.Replace(strings.ToUpper(p.Font.Color), "#", "", -1)
if len(srgbClr) == 6 {
paragraph.R.RPr.SolidFill = &aSolidFill{
SrgbClr: &attrValString{
Val: stringPtr(srgbClr),
},
}
}
shape.TxBody.P = append(shape.TxBody.P, paragraph)
}
twoCellAnchor.Sp = &shape
twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.Format.FLocksWithSheet,
FPrintsWithSheet: formatSet.Format.FPrintsWithSheet,
}
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content
return err
}
// setShapeRef provides a function to set color with hex model by given actual
// color value.
func setShapeRef(color string, i int) *aRef {
if color == "" {
return &aRef{
Idx: 0,
ScrgbClr: &aScrgbClr{
R: 0,
G: 0,
B: 0,
},
}
}
return &aRef{
Idx: i,
SrgbClr: &attrValString{
Val: stringPtr(strings.Replace(strings.ToUpper(color), "#", "", -1)),
},
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,359 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
// SheetPrOption is an option of a view of a worksheet. See SetSheetPrOptions().
type SheetPrOption interface {
setSheetPrOption(view *xlsxSheetPr)
}
// SheetPrOptionPtr is a writable SheetPrOption. See GetSheetPrOptions().
type SheetPrOptionPtr interface {
SheetPrOption
getSheetPrOption(view *xlsxSheetPr)
}
type (
// CodeName is a SheetPrOption
CodeName string
// EnableFormatConditionsCalculation is a SheetPrOption
EnableFormatConditionsCalculation bool
// Published is a SheetPrOption
Published bool
// FitToPage is a SheetPrOption
FitToPage bool
// AutoPageBreaks is a SheetPrOption
AutoPageBreaks bool
// OutlineSummaryBelow is an outlinePr, within SheetPr option
OutlineSummaryBelow bool
)
// setSheetPrOption implements the SheetPrOption interface.
func (o OutlineSummaryBelow) setSheetPrOption(pr *xlsxSheetPr) {
if pr.OutlinePr == nil {
pr.OutlinePr = new(xlsxOutlinePr)
}
pr.OutlinePr.SummaryBelow = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *OutlineSummaryBelow) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: true
if pr == nil || pr.OutlinePr == nil {
*o = true
return
}
*o = OutlineSummaryBelow(defaultTrue(&pr.OutlinePr.SummaryBelow))
}
// setSheetPrOption implements the SheetPrOption interface and specifies a
// stable name of the sheet.
func (o CodeName) setSheetPrOption(pr *xlsxSheetPr) {
pr.CodeName = string(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// stable name of the sheet.
func (o *CodeName) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = ""
return
}
*o = CodeName(pr.CodeName)
}
// setSheetPrOption implements the SheetPrOption interface and flag indicating
// whether the conditional formatting calculations shall be evaluated.
func (o EnableFormatConditionsCalculation) setSheetPrOption(pr *xlsxSheetPr) {
pr.EnableFormatConditionsCalculation = boolPtr(bool(o))
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// settings of whether the conditional formatting calculations shall be
// evaluated.
func (o *EnableFormatConditionsCalculation) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = true
return
}
*o = EnableFormatConditionsCalculation(defaultTrue(pr.EnableFormatConditionsCalculation))
}
// setSheetPrOption implements the SheetPrOption interface and flag indicating
// whether the worksheet is published.
func (o Published) setSheetPrOption(pr *xlsxSheetPr) {
pr.Published = boolPtr(bool(o))
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// settings of whether the worksheet is published.
func (o *Published) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = true
return
}
*o = Published(defaultTrue(pr.Published))
}
// setSheetPrOption implements the SheetPrOption interface.
func (o FitToPage) setSheetPrOption(pr *xlsxSheetPr) {
if pr.PageSetUpPr == nil {
if !o {
return
}
pr.PageSetUpPr = new(xlsxPageSetUpPr)
}
pr.PageSetUpPr.FitToPage = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *FitToPage) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: false
if pr == nil || pr.PageSetUpPr == nil {
*o = false
return
}
*o = FitToPage(pr.PageSetUpPr.FitToPage)
}
// setSheetPrOption implements the SheetPrOption interface.
func (o AutoPageBreaks) setSheetPrOption(pr *xlsxSheetPr) {
if pr.PageSetUpPr == nil {
if !o {
return
}
pr.PageSetUpPr = new(xlsxPageSetUpPr)
}
pr.PageSetUpPr.AutoPageBreaks = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: false
if pr == nil || pr.PageSetUpPr == nil {
*o = false
return
}
*o = AutoPageBreaks(pr.PageSetUpPr.AutoPageBreaks)
}
// SetSheetPrOptions provides a function to sets worksheet properties.
//
// Available options:
// CodeName(string)
// EnableFormatConditionsCalculation(bool)
// Published(bool)
// FitToPage(bool)
// AutoPageBreaks(bool)
// OutlineSummaryBelow(bool)
func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
sheet, err := f.workSheetReader(name)
if err != nil {
return err
}
pr := sheet.SheetPr
if pr == nil {
pr = new(xlsxSheetPr)
sheet.SheetPr = pr
}
for _, opt := range opts {
opt.setSheetPrOption(pr)
}
return err
}
// GetSheetPrOptions provides a function to gets worksheet properties.
//
// Available options:
// CodeName(string)
// EnableFormatConditionsCalculation(bool)
// Published(bool)
// FitToPage(bool)
// AutoPageBreaks(bool)
// OutlineSummaryBelow(bool)
func (f *File) GetSheetPrOptions(name string, opts ...SheetPrOptionPtr) error {
sheet, err := f.workSheetReader(name)
if err != nil {
return err
}
pr := sheet.SheetPr
for _, opt := range opts {
opt.getSheetPrOption(pr)
}
return err
}
type (
// PageMarginBottom specifies the bottom margin for the page.
PageMarginBottom float64
// PageMarginFooter specifies the footer margin for the page.
PageMarginFooter float64
// PageMarginHeader specifies the header margin for the page.
PageMarginHeader float64
// PageMarginLeft specifies the left margin for the page.
PageMarginLeft float64
// PageMarginRight specifies the right margin for the page.
PageMarginRight float64
// PageMarginTop specifies the top margin for the page.
PageMarginTop float64
)
// setPageMargins provides a method to set the bottom margin for the worksheet.
func (p PageMarginBottom) setPageMargins(pm *xlsxPageMargins) {
pm.Bottom = float64(p)
}
// setPageMargins provides a method to get the bottom margin for the worksheet.
func (p *PageMarginBottom) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.75
if pm == nil || pm.Bottom == 0 {
*p = 0.75
return
}
*p = PageMarginBottom(pm.Bottom)
}
// setPageMargins provides a method to set the footer margin for the worksheet.
func (p PageMarginFooter) setPageMargins(pm *xlsxPageMargins) {
pm.Footer = float64(p)
}
// setPageMargins provides a method to get the footer margin for the worksheet.
func (p *PageMarginFooter) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.3
if pm == nil || pm.Footer == 0 {
*p = 0.3
return
}
*p = PageMarginFooter(pm.Footer)
}
// setPageMargins provides a method to set the header margin for the worksheet.
func (p PageMarginHeader) setPageMargins(pm *xlsxPageMargins) {
pm.Header = float64(p)
}
// setPageMargins provides a method to get the header margin for the worksheet.
func (p *PageMarginHeader) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.3
if pm == nil || pm.Header == 0 {
*p = 0.3
return
}
*p = PageMarginHeader(pm.Header)
}
// setPageMargins provides a method to set the left margin for the worksheet.
func (p PageMarginLeft) setPageMargins(pm *xlsxPageMargins) {
pm.Left = float64(p)
}
// setPageMargins provides a method to get the left margin for the worksheet.
func (p *PageMarginLeft) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.7
if pm == nil || pm.Left == 0 {
*p = 0.7
return
}
*p = PageMarginLeft(pm.Left)
}
// setPageMargins provides a method to set the right margin for the worksheet.
func (p PageMarginRight) setPageMargins(pm *xlsxPageMargins) {
pm.Right = float64(p)
}
// setPageMargins provides a method to get the right margin for the worksheet.
func (p *PageMarginRight) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.7
if pm == nil || pm.Right == 0 {
*p = 0.7
return
}
*p = PageMarginRight(pm.Right)
}
// setPageMargins provides a method to set the top margin for the worksheet.
func (p PageMarginTop) setPageMargins(pm *xlsxPageMargins) {
pm.Top = float64(p)
}
// setPageMargins provides a method to get the top margin for the worksheet.
func (p *PageMarginTop) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.75
if pm == nil || pm.Top == 0 {
*p = 0.75
return
}
*p = PageMarginTop(pm.Top)
}
// PageMarginsOptions is an option of a page margin of a worksheet. See
// SetPageMargins().
type PageMarginsOptions interface {
setPageMargins(layout *xlsxPageMargins)
}
// PageMarginsOptionsPtr is a writable PageMarginsOptions. See
// GetPageMargins().
type PageMarginsOptionsPtr interface {
PageMarginsOptions
getPageMargins(layout *xlsxPageMargins)
}
// SetPageMargins provides a function to set worksheet page margins.
//
// Available options:
// PageMarginBotom(float64)
// PageMarginFooter(float64)
// PageMarginHeader(float64)
// PageMarginLeft(float64)
// PageMarginRight(float64)
// PageMarginTop(float64)
func (f *File) SetPageMargins(sheet string, opts ...PageMarginsOptions) error {
s, err := f.workSheetReader(sheet)
if err != nil {
return err
}
pm := s.PageMargins
if pm == nil {
pm = new(xlsxPageMargins)
s.PageMargins = pm
}
for _, opt := range opts {
opt.setPageMargins(pm)
}
return err
}
// GetPageMargins provides a function to get worksheet page margins.
//
// Available options:
// PageMarginBotom(float64)
// PageMarginFooter(float64)
// PageMarginHeader(float64)
// PageMarginLeft(float64)
// PageMarginRight(float64)
// PageMarginTop(float64)
func (f *File) GetPageMargins(sheet string, opts ...PageMarginsOptionsPtr) error {
s, err := f.workSheetReader(sheet)
if err != nil {
return err
}
pm := s.PageMargins
for _, opt := range opts {
opt.getPageMargins(pm)
}
return err
}

View file

@ -0,0 +1,215 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "fmt"
// SheetViewOption is an option of a view of a worksheet. See
// SetSheetViewOptions().
type SheetViewOption interface {
setSheetViewOption(view *xlsxSheetView)
}
// SheetViewOptionPtr is a writable SheetViewOption. See
// GetSheetViewOptions().
type SheetViewOptionPtr interface {
SheetViewOption
getSheetViewOption(view *xlsxSheetView)
}
type (
// DefaultGridColor is a SheetViewOption. It specifies a flag indicating that
// the consuming application should use the default grid lines color (system
// dependent). Overrides any color specified in colorId.
DefaultGridColor bool
// RightToLeft is a SheetViewOption. It specifies a flag indicating whether
// the sheet is in 'right to left' display mode. When in this mode, Column A
// is on the far right, Column B ;is one column left of Column A, and so on.
// Also, information in cells is displayed in the Right to Left format.
RightToLeft bool
// ShowFormulas is a SheetViewOption. It specifies a flag indicating whether
// this sheet should display formulas.
ShowFormulas bool
// ShowGridLines is a SheetViewOption. It specifies a flag indicating whether
// this sheet should display gridlines.
ShowGridLines bool
// ShowRowColHeaders is a SheetViewOption. It specifies a flag indicating
// whether the sheet should display row and column headings.
ShowRowColHeaders bool
// ZoomScale is a SheetViewOption. It specifies a window zoom magnification
// for current view representing percent values. This attribute is restricted
// to values ranging from 10 to 400. Horizontal & Vertical scale together.
ZoomScale float64
// TopLeftCell is a SheetViewOption. It specifies a location of the top left
// visible cell Location of the top left visible cell in the bottom right
// pane (when in Left-to-Right mode).
TopLeftCell string
// ShowZeros is a SheetViewOption. It specifies a flag indicating
// whether to "show a zero in cells that have zero value".
// When using a formula to reference another cell which is empty, the referenced value becomes 0
// when the flag is true. (Default setting is true.)
ShowZeros bool
/* TODO
// ShowWhiteSpace is a SheetViewOption. It specifies a flag indicating
// whether page layout view shall display margins. False means do not display
// left, right, top (header), and bottom (footer) margins (even when there is
// data in the header or footer).
ShowWhiteSpace bool
// WindowProtection is a SheetViewOption.
WindowProtection bool
*/
)
// Defaults for each option are described in XML schema for CT_SheetView
func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
view.TopLeftCell = string(o)
}
func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
*o = TopLeftCell(string(view.TopLeftCell))
}
func (o DefaultGridColor) setSheetViewOption(view *xlsxSheetView) {
view.DefaultGridColor = boolPtr(bool(o))
}
func (o *DefaultGridColor) getSheetViewOption(view *xlsxSheetView) {
*o = DefaultGridColor(defaultTrue(view.DefaultGridColor)) // Excel default: true
}
func (o RightToLeft) setSheetViewOption(view *xlsxSheetView) {
view.RightToLeft = bool(o) // Excel default: false
}
func (o *RightToLeft) getSheetViewOption(view *xlsxSheetView) {
*o = RightToLeft(view.RightToLeft)
}
func (o ShowFormulas) setSheetViewOption(view *xlsxSheetView) {
view.ShowFormulas = bool(o) // Excel default: false
}
func (o *ShowFormulas) getSheetViewOption(view *xlsxSheetView) {
*o = ShowFormulas(view.ShowFormulas) // Excel default: false
}
func (o ShowGridLines) setSheetViewOption(view *xlsxSheetView) {
view.ShowGridLines = boolPtr(bool(o))
}
func (o *ShowGridLines) getSheetViewOption(view *xlsxSheetView) {
*o = ShowGridLines(defaultTrue(view.ShowGridLines)) // Excel default: true
}
func (o ShowZeros) setSheetViewOption(view *xlsxSheetView) {
view.ShowZeros = boolPtr(bool(o))
}
func (o *ShowZeros) getSheetViewOption(view *xlsxSheetView) {
*o = ShowZeros(defaultTrue(view.ShowZeros)) // Excel default: true
}
func (o ShowRowColHeaders) setSheetViewOption(view *xlsxSheetView) {
view.ShowRowColHeaders = boolPtr(bool(o))
}
func (o *ShowRowColHeaders) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRowColHeaders(defaultTrue(view.ShowRowColHeaders)) // Excel default: true
}
func (o ZoomScale) setSheetViewOption(view *xlsxSheetView) {
// This attribute is restricted to values ranging from 10 to 400.
if float64(o) >= 10 && float64(o) <= 400 {
view.ZoomScale = float64(o)
}
}
func (o *ZoomScale) getSheetViewOption(view *xlsxSheetView) {
*o = ZoomScale(view.ZoomScale)
}
// getSheetView returns the SheetView object
func (f *File) getSheetView(sheetName string, viewIndex int) (*xlsxSheetView, error) {
xlsx, err := f.workSheetReader(sheetName)
if err != nil {
return nil, err
}
if viewIndex < 0 {
if viewIndex < -len(xlsx.SheetViews.SheetView) {
return nil, fmt.Errorf("view index %d out of range", viewIndex)
}
viewIndex = len(xlsx.SheetViews.SheetView) + viewIndex
} else if viewIndex >= len(xlsx.SheetViews.SheetView) {
return nil, fmt.Errorf("view index %d out of range", viewIndex)
}
return &(xlsx.SheetViews.SheetView[viewIndex]), err
}
// SetSheetViewOptions sets sheet view options. The viewIndex may be negative
// and if so is counted backward (-1 is the last view).
//
// Available options:
//
// DefaultGridColor(bool)
// RightToLeft(bool)
// ShowFormulas(bool)
// ShowGridLines(bool)
// ShowRowColHeaders(bool)
// ZoomScale(float64)
// TopLeftCell(string)
//
// Example:
//
// err = f.SetSheetViewOptions("Sheet1", -1, ShowGridLines(false))
//
func (f *File) SetSheetViewOptions(name string, viewIndex int, opts ...SheetViewOption) error {
view, err := f.getSheetView(name, viewIndex)
if err != nil {
return err
}
for _, opt := range opts {
opt.setSheetViewOption(view)
}
return nil
}
// GetSheetViewOptions gets the value of sheet view options. The viewIndex may
// be negative and if so is counted backward (-1 is the last view).
//
// Available options:
//
// DefaultGridColor(bool)
// RightToLeft(bool)
// ShowFormulas(bool)
// ShowGridLines(bool)
// ShowRowColHeaders(bool)
// ZoomScale(float64)
// TopLeftCell(string)
//
// Example:
//
// var showGridLines excelize.ShowGridLines
// err = f.GetSheetViewOptions("Sheet1", -1, &showGridLines)
//
func (f *File) GetSheetViewOptions(name string, viewIndex int, opts ...SheetViewOptionPtr) error {
view, err := f.getSheetView(name, viewIndex)
if err != nil {
return err
}
for _, opt := range opts {
opt.getSheetViewOption(view)
}
return nil
}

View file

@ -0,0 +1,542 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/xml"
"errors"
"io"
"strings"
)
// addSparklineGroupByStyle provides a function to create x14:sparklineGroups
// element by given sparkline style ID.
func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup {
groups := []*xlsxX14SparklineGroup{
{
ColorSeries: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 5},
ColorMarkers: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 4, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 4, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 4},
ColorLow: &xlsxTabColor{Theme: 4},
}, // 0
{
ColorSeries: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 5},
ColorMarkers: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 4, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 4, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 4},
ColorLow: &xlsxTabColor{Theme: 4},
}, // 1
{
ColorSeries: &xlsxTabColor{Theme: 5, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 6},
ColorMarkers: &xlsxTabColor{Theme: 5, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 5, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 5, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 5},
ColorLow: &xlsxTabColor{Theme: 5},
}, // 2
{
ColorSeries: &xlsxTabColor{Theme: 6, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 7},
ColorMarkers: &xlsxTabColor{Theme: 6, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 6, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 6, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 6},
ColorLow: &xlsxTabColor{Theme: 6},
}, // 3
{
ColorSeries: &xlsxTabColor{Theme: 7, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 8},
ColorMarkers: &xlsxTabColor{Theme: 7, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 7, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 7, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 7},
ColorLow: &xlsxTabColor{Theme: 7},
}, // 4
{
ColorSeries: &xlsxTabColor{Theme: 8, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 9},
ColorMarkers: &xlsxTabColor{Theme: 8, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 8, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 8, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 8},
ColorLow: &xlsxTabColor{Theme: 8},
}, // 5
{
ColorSeries: &xlsxTabColor{Theme: 9, Tint: -0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 4},
ColorMarkers: &xlsxTabColor{Theme: 9, Tint: -0.499984740745262},
ColorFirst: &xlsxTabColor{Theme: 9, Tint: 0.39997558519241921},
ColorLast: &xlsxTabColor{Theme: 9, Tint: 0.39997558519241921},
ColorHigh: &xlsxTabColor{Theme: 9},
ColorLow: &xlsxTabColor{Theme: 9},
}, // 6
{
ColorSeries: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 5},
ColorMarkers: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 5},
ColorLow: &xlsxTabColor{Theme: 5},
}, // 7
{
ColorSeries: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 6},
ColorMarkers: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
}, // 8
{
ColorSeries: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 7},
ColorMarkers: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
}, // 9
{
ColorSeries: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 8},
ColorMarkers: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
}, // 10
{
ColorSeries: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 9},
ColorMarkers: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
}, // 11
{
ColorSeries: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorNegative: &xlsxTabColor{Theme: 4},
ColorMarkers: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
}, // 12
{
ColorSeries: &xlsxTabColor{Theme: 4},
ColorNegative: &xlsxTabColor{Theme: 5},
ColorMarkers: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
}, // 13
{
ColorSeries: &xlsxTabColor{Theme: 5},
ColorNegative: &xlsxTabColor{Theme: 6},
ColorMarkers: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
}, // 14
{
ColorSeries: &xlsxTabColor{Theme: 6},
ColorNegative: &xlsxTabColor{Theme: 7},
ColorMarkers: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
}, // 15
{
ColorSeries: &xlsxTabColor{Theme: 7},
ColorNegative: &xlsxTabColor{Theme: 8},
ColorMarkers: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
}, // 16
{
ColorSeries: &xlsxTabColor{Theme: 8},
ColorNegative: &xlsxTabColor{Theme: 9},
ColorMarkers: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
}, // 17
{
ColorSeries: &xlsxTabColor{Theme: 9},
ColorNegative: &xlsxTabColor{Theme: 4},
ColorMarkers: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
}, // 18
{
ColorSeries: &xlsxTabColor{Theme: 4, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 4, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 4, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 4, Tint: -0.499984740745262},
}, // 19
{
ColorSeries: &xlsxTabColor{Theme: 5, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 5, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 5, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 5, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 5, Tint: -0.499984740745262},
}, // 20
{
ColorSeries: &xlsxTabColor{Theme: 6, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 6, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 6, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 6, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 6, Tint: -0.499984740745262},
}, // 21
{
ColorSeries: &xlsxTabColor{Theme: 7, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 7, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 7, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 7, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 7, Tint: -0.499984740745262},
}, // 22
{
ColorSeries: &xlsxTabColor{Theme: 8, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 8, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 8, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 8, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 8, Tint: -0.499984740745262},
}, // 23
{
ColorSeries: &xlsxTabColor{Theme: 9, Tint: 0.39997558519241921},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: -0.499984740745262},
ColorMarkers: &xlsxTabColor{Theme: 9, Tint: 0.79998168889431442},
ColorFirst: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 9, Tint: -0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 9, Tint: -0.499984740745262},
ColorLow: &xlsxTabColor{Theme: 9, Tint: -0.499984740745262},
}, // 24
{
ColorSeries: &xlsxTabColor{Theme: 1, Tint: 0.499984740745262},
ColorNegative: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
ColorMarkers: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 1, Tint: 0.249977111117893},
}, // 25
{
ColorSeries: &xlsxTabColor{Theme: 1, Tint: 0.34998626667073579},
ColorNegative: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
ColorMarkers: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
ColorFirst: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
ColorLast: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
ColorHigh: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
ColorLow: &xlsxTabColor{Theme: 0, Tint: 0.249977111117893},
}, // 26
{
ColorSeries: &xlsxTabColor{RGB: "FF323232"},
ColorNegative: &xlsxTabColor{RGB: "FFD00000"},
ColorMarkers: &xlsxTabColor{RGB: "FFD00000"},
ColorFirst: &xlsxTabColor{RGB: "FFD00000"},
ColorLast: &xlsxTabColor{RGB: "FFD00000"},
ColorHigh: &xlsxTabColor{RGB: "FFD00000"},
ColorLow: &xlsxTabColor{RGB: "FFD00000"},
}, // 27
{
ColorSeries: &xlsxTabColor{RGB: "FF000000"},
ColorNegative: &xlsxTabColor{RGB: "FF0070C0"},
ColorMarkers: &xlsxTabColor{RGB: "FF0070C0"},
ColorFirst: &xlsxTabColor{RGB: "FF0070C0"},
ColorLast: &xlsxTabColor{RGB: "FF0070C0"},
ColorHigh: &xlsxTabColor{RGB: "FF0070C0"},
ColorLow: &xlsxTabColor{RGB: "FF0070C0"},
}, // 28
{
ColorSeries: &xlsxTabColor{RGB: "FF376092"},
ColorNegative: &xlsxTabColor{RGB: "FFD00000"},
ColorMarkers: &xlsxTabColor{RGB: "FFD00000"},
ColorFirst: &xlsxTabColor{RGB: "FFD00000"},
ColorLast: &xlsxTabColor{RGB: "FFD00000"},
ColorHigh: &xlsxTabColor{RGB: "FFD00000"},
ColorLow: &xlsxTabColor{RGB: "FFD00000"},
}, // 29
{
ColorSeries: &xlsxTabColor{RGB: "FF0070C0"},
ColorNegative: &xlsxTabColor{RGB: "FF000000"},
ColorMarkers: &xlsxTabColor{RGB: "FF000000"},
ColorFirst: &xlsxTabColor{RGB: "FF000000"},
ColorLast: &xlsxTabColor{RGB: "FF000000"},
ColorHigh: &xlsxTabColor{RGB: "FF000000"},
ColorLow: &xlsxTabColor{RGB: "FF000000"},
}, // 30
{
ColorSeries: &xlsxTabColor{RGB: "FF5F5F5F"},
ColorNegative: &xlsxTabColor{RGB: "FFFFB620"},
ColorMarkers: &xlsxTabColor{RGB: "FFD70077"},
ColorFirst: &xlsxTabColor{RGB: "FF5687C2"},
ColorLast: &xlsxTabColor{RGB: "FF359CEB"},
ColorHigh: &xlsxTabColor{RGB: "FF56BE79"},
ColorLow: &xlsxTabColor{RGB: "FFFF5055"},
}, // 31
{
ColorSeries: &xlsxTabColor{RGB: "FF5687C2"},
ColorNegative: &xlsxTabColor{RGB: "FFFFB620"},
ColorMarkers: &xlsxTabColor{RGB: "FFD70077"},
ColorFirst: &xlsxTabColor{RGB: "FF777777"},
ColorLast: &xlsxTabColor{RGB: "FF359CEB"},
ColorHigh: &xlsxTabColor{RGB: "FF56BE79"},
ColorLow: &xlsxTabColor{RGB: "FFFF5055"},
}, // 32
{
ColorSeries: &xlsxTabColor{RGB: "FFC6EFCE"},
ColorNegative: &xlsxTabColor{RGB: "FFFFC7CE"},
ColorMarkers: &xlsxTabColor{RGB: "FF8CADD6"},
ColorFirst: &xlsxTabColor{RGB: "FFFFDC47"},
ColorLast: &xlsxTabColor{RGB: "FFFFEB9C"},
ColorHigh: &xlsxTabColor{RGB: "FF60D276"},
ColorLow: &xlsxTabColor{RGB: "FFFF5367"},
}, // 33
{
ColorSeries: &xlsxTabColor{RGB: "FF00B050"},
ColorNegative: &xlsxTabColor{RGB: "FFFF0000"},
ColorMarkers: &xlsxTabColor{RGB: "FF0070C0"},
ColorFirst: &xlsxTabColor{RGB: "FFFFC000"},
ColorLast: &xlsxTabColor{RGB: "FFFFC000"},
ColorHigh: &xlsxTabColor{RGB: "FF00B050"},
ColorLow: &xlsxTabColor{RGB: "FFFF0000"},
}, // 34
{
ColorSeries: &xlsxTabColor{Theme: 3},
ColorNegative: &xlsxTabColor{Theme: 9},
ColorMarkers: &xlsxTabColor{Theme: 8},
ColorFirst: &xlsxTabColor{Theme: 4},
ColorLast: &xlsxTabColor{Theme: 5},
ColorHigh: &xlsxTabColor{Theme: 6},
ColorLow: &xlsxTabColor{Theme: 7},
}, // 35
{
ColorSeries: &xlsxTabColor{Theme: 1},
ColorNegative: &xlsxTabColor{Theme: 9},
ColorMarkers: &xlsxTabColor{Theme: 8},
ColorFirst: &xlsxTabColor{Theme: 4},
ColorLast: &xlsxTabColor{Theme: 5},
ColorHigh: &xlsxTabColor{Theme: 6},
ColorLow: &xlsxTabColor{Theme: 7},
}, // 36
}
return groups[ID]
}
// AddSparkline provides a function to add sparklines to the worksheet by
// given formatting options. Sparklines are small charts that fit in a single
// cell and are used to show trends in data. Sparklines are a feature of Excel
// 2010 and later only. You can write them to an XLSX file that can be read by
// Excel 2007 but they won't be displayed. For example, add a grouped
// sparkline. Changes are applied to all three:
//
// err := f.AddSparkline("Sheet1", &excelize.SparklineOption{
// Location: []string{"A1", "A2", "A3"},
// Range: []string{"Sheet2!A1:J1", "Sheet2!A2:J2", "Sheet2!A3:J3"},
// Markers: true,
// })
//
// The following shows the formatting options of sparkline supported by excelize:
//
// Parameter | Description
// -----------+--------------------------------------------
// Location | Required, must have the same number with 'Range' parameter
// Range | Required, must have the same number with 'Location' parameter
// Type | Enumeration value: line, column, win_loss
// Style | Value range: 0 - 35
// Hight | Toggle sparkline high points
// Low | Toggle sparkline low points
// First | Toggle sparkline first points
// Last | Toggle sparkline last points
// Negative | Toggle sparkline negative points
// Markers | Toggle sparkline markers
// ColorAxis | An RGB Color is specified as RRGGBB
// Axis | Show sparkline axis
//
func (f *File) AddSparkline(sheet string, opt *SparklineOption) (err error) {
var (
ws *xlsxWorksheet
sparkType string
sparkTypes map[string]string
specifiedSparkTypes string
ok bool
group *xlsxX14SparklineGroup
groups *xlsxX14SparklineGroups
sparklineGroupsBytes, extBytes []byte
)
// parameter validation
if ws, err = f.parseFormatAddSparklineSet(sheet, opt); err != nil {
return
}
// Handle the sparkline type
sparkType = "line"
sparkTypes = map[string]string{"line": "line", "column": "column", "win_loss": "stacked"}
if opt.Type != "" {
if specifiedSparkTypes, ok = sparkTypes[opt.Type]; !ok {
err = errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'")
return
}
sparkType = specifiedSparkTypes
}
group = f.addSparklineGroupByStyle(opt.Style)
group.Type = sparkType
group.ColorAxis = &xlsxColor{RGB: "FF000000"}
group.DisplayEmptyCellsAs = "gap"
group.High = opt.High
group.Low = opt.Low
group.First = opt.First
group.Last = opt.Last
group.Negative = opt.Negative
group.DisplayXAxis = opt.Axis
group.Markers = opt.Markers
if opt.SeriesColor != "" {
group.ColorSeries = &xlsxTabColor{
RGB: getPaletteColor(opt.SeriesColor),
}
}
if opt.Reverse {
group.RightToLeft = opt.Reverse
}
f.addSparkline(opt, group)
if ws.ExtLst.Ext != "" { // append mode ext
if err = f.appendSparkline(ws, group, groups); err != nil {
return
}
} else {
groups = &xlsxX14SparklineGroups{
XMLNSXM: NameSpaceSpreadSheetExcel2006Main,
SparklineGroups: []*xlsxX14SparklineGroup{group},
}
if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
return
}
if extBytes, err = xml.Marshal(&xlsxWorksheetExt{
URI: ExtURISparklineGroups,
Content: string(sparklineGroupsBytes),
}); err != nil {
return
}
ws.ExtLst.Ext = string(extBytes)
}
return
}
// parseFormatAddSparklineSet provides a function to validate sparkline
// properties.
func (f *File) parseFormatAddSparklineSet(sheet string, opt *SparklineOption) (*xlsxWorksheet, error) {
ws, err := f.workSheetReader(sheet)
if err != nil {
return ws, err
}
if opt == nil {
return ws, errors.New("parameter is required")
}
if len(opt.Location) < 1 {
return ws, errors.New("parameter 'Location' is required")
}
if len(opt.Range) < 1 {
return ws, errors.New("parameter 'Range' is required")
}
// The ranges and locations must match.\
if len(opt.Location) != len(opt.Range) {
return ws, errors.New(`must have the same number of 'Location' and 'Range' parameters`)
}
if opt.Style < 0 || opt.Style > 35 {
return ws, errors.New("parameter 'Style' must betweent 0-35")
}
if ws.ExtLst == nil {
ws.ExtLst = &xlsxExtLst{}
}
return ws, err
}
// addSparkline provides a function to create a sparkline in a sparkline group
// by given properties.
func (f *File) addSparkline(opt *SparklineOption, group *xlsxX14SparklineGroup) {
for idx, location := range opt.Location {
group.Sparklines.Sparkline = append(group.Sparklines.Sparkline, &xlsxX14Sparkline{
F: opt.Range[idx],
Sqref: location,
})
}
}
// appendSparkline provides a function to append sparkline to sparkline
// groups.
func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup, groups *xlsxX14SparklineGroups) (err error) {
var (
idx int
decodeExtLst *decodeWorksheetExt
decodeSparklineGroups *decodeX14SparklineGroups
ext *xlsxWorksheetExt
sparklineGroupsBytes, sparklineGroupBytes, extLstBytes []byte
)
decodeExtLst = new(decodeWorksheetExt)
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
Decode(decodeExtLst); err != nil && err != io.EOF {
return
}
for idx, ext = range decodeExtLst.Ext {
if ext.URI == ExtURISparklineGroups {
decodeSparklineGroups = new(decodeX14SparklineGroups)
if err = f.xmlNewDecoder(strings.NewReader(ext.Content)).
Decode(decodeSparklineGroups); err != nil && err != io.EOF {
return
}
if sparklineGroupBytes, err = xml.Marshal(group); err != nil {
return
}
groups = &xlsxX14SparklineGroups{
XMLNSXM: NameSpaceSpreadSheetExcel2006Main,
Content: decodeSparklineGroups.Content + string(sparklineGroupBytes),
}
if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
return
}
decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes)
}
}
if extLstBytes, err = xml.Marshal(decodeExtLst); err != nil {
return
}
ws.ExtLst = &xlsxExtLst{
Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>"),
}
return
}

View file

@ -0,0 +1,515 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
"time"
)
// StreamWriter defined the type of stream writer.
type StreamWriter struct {
File *File
Sheet string
SheetID int
worksheet *xlsxWorksheet
rawData bufferedWriter
tableParts string
}
// NewStreamWriter return stream writer struct by given worksheet name for
// generate new worksheet with large amounts of data. Note that after set
// rows, you must call the 'Flush' method to end the streaming writing
// process and ensure that the order of line numbers is ascending. For
// example, set data for worksheet of size 102400 rows x 50 columns with
// numbers and style:
//
// file := excelize.NewFile()
// streamWriter, err := file.NewStreamWriter("Sheet1")
// if err != nil {
// fmt.Println(err)
// }
// styleID, err := file.NewStyle(`{"font":{"color":"#777777"}}`)
// if err != nil {
// fmt.Println(err)
// }
// if err := streamWriter.SetRow("A1", []interface{}{excelize.Cell{StyleID: styleID, Value: "Data"}}); err != nil {
// fmt.Println(err)
// }
// for rowID := 2; rowID <= 102400; rowID++ {
// row := make([]interface{}, 50)
// for colID := 0; colID < 50; colID++ {
// row[colID] = rand.Intn(640000)
// }
// cell, _ := excelize.CoordinatesToCellName(1, rowID)
// if err := streamWriter.SetRow(cell, row); err != nil {
// fmt.Println(err)
// }
// }
// if err := streamWriter.Flush(); err != nil {
// fmt.Println(err)
// }
// if err := file.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
//
func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
sheetID := f.getSheetID(sheet)
if sheetID == 0 {
return nil, fmt.Errorf("sheet %s is not exist", sheet)
}
sw := &StreamWriter{
File: f,
Sheet: sheet,
SheetID: sheetID,
}
var err error
sw.worksheet, err = f.workSheetReader(sheet)
if err != nil {
return nil, err
}
sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
bulkAppendFields(&sw.rawData, sw.worksheet, 1, 5)
sw.rawData.WriteString(`<sheetData>`)
return sw, err
}
// AddTable creates an Excel table for the StreamWriter using the given
// coordinate area and format set. For example, create a table of A1:D5:
//
// err := sw.AddTable("A1", "D5", ``)
//
// Create a table of F2:H6 with format set:
//
// err := sw.AddTable("F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2","show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
//
// Note that the table must be at least two lines including the header. The
// header cells must contain strings and must be unique.
//
// Currently only one table is allowed for a StreamWriter. AddTable must be
// called after the rows are written but before Flush.
//
// See File.AddTable for details on the table format.
func (sw *StreamWriter) AddTable(hcell, vcell, format string) error {
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
}
coordinates, err := areaRangeToCoordinates(hcell, vcell)
if err != nil {
return err
}
_ = sortCoordinates(coordinates)
// Correct the minimum number of rows, the table at least two lines.
if coordinates[1] == coordinates[3] {
coordinates[3]++
}
// Correct table reference coordinate area, such correct C1:B3 to B1:C3.
ref, err := sw.File.coordinatesToAreaRef(coordinates)
if err != nil {
return err
}
// create table columns using the first row
tableHeaders, err := sw.getRowValues(coordinates[1], coordinates[0], coordinates[2])
if err != nil {
return err
}
tableColumn := make([]*xlsxTableColumn, len(tableHeaders))
for i, name := range tableHeaders {
tableColumn[i] = &xlsxTableColumn{
ID: i + 1,
Name: name,
}
}
tableID := sw.File.countTables() + 1
name := formatSet.TableName
if name == "" {
name = "Table" + strconv.Itoa(tableID)
}
table := xlsxTable{
XMLNS: NameSpaceSpreadSheet,
ID: tableID,
Name: name,
DisplayName: name,
Ref: ref,
AutoFilter: &xlsxAutoFilter{
Ref: ref,
},
TableColumns: &xlsxTableColumns{
Count: len(tableColumn),
TableColumn: tableColumn,
},
TableStyleInfo: &xlsxTableStyleInfo{
Name: formatSet.TableStyle,
ShowFirstColumn: formatSet.ShowFirstColumn,
ShowLastColumn: formatSet.ShowLastColumn,
ShowRowStripes: formatSet.ShowRowStripes,
ShowColumnStripes: formatSet.ShowColumnStripes,
},
}
sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1)
// Add first table for given sheet.
sheetPath, _ := sw.File.sheetMap[trimSheetName(sw.Sheet)]
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
rID := sw.File.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
sw.tableParts = fmt.Sprintf(`<tableParts count="1"><tablePart r:id="rId%d"></tablePart></tableParts>`, rID)
sw.File.addContentTypePart(tableID, "table")
b, _ := xml.Marshal(table)
sw.File.saveFileList(tableXML, b)
return nil
}
// Extract values from a row in the StreamWriter.
func (sw *StreamWriter) getRowValues(hrow, hcol, vcol int) (res []string, err error) {
res = make([]string, vcol-hcol+1)
r, err := sw.rawData.Reader()
if err != nil {
return nil, err
}
dec := sw.File.xmlNewDecoder(r)
for {
token, err := dec.Token()
if err == io.EOF {
return res, nil
}
if err != nil {
return nil, err
}
startElement, ok := getRowElement(token, hrow)
if !ok {
continue
}
// decode cells
var row xlsxRow
if err := dec.DecodeElement(&row, &startElement); err != nil {
return nil, err
}
for _, c := range row.C {
col, _, err := CellNameToCoordinates(c.R)
if err != nil {
return nil, err
}
if col < hcol || col > vcol {
continue
}
res[col-hcol] = c.V
}
return res, nil
}
}
// Check if the token is an XLSX row with the matching row number.
func getRowElement(token xml.Token, hrow int) (startElement xml.StartElement, ok bool) {
startElement, ok = token.(xml.StartElement)
if !ok {
return
}
ok = startElement.Name.Local == "row"
if !ok {
return
}
ok = false
for _, attr := range startElement.Attr {
if attr.Name.Local != "r" {
continue
}
row, _ := strconv.Atoi(attr.Value)
if row == hrow {
ok = true
return
}
}
return
}
// Cell can be used directly in StreamWriter.SetRow to specify a style and
// a value.
type Cell struct {
StyleID int
Value interface{}
}
// SetRow writes an array to stream rows by giving a worksheet name, starting
// coordinate and a pointer to an array of values. Note that you must call the
// 'Flush' method to end the streaming writing process.
//
// As a special case, if Cell is used as a value, then the Cell.StyleID will be
// applied to that cell.
func (sw *StreamWriter) SetRow(axis string, values []interface{}) error {
col, row, err := CellNameToCoordinates(axis)
if err != nil {
return err
}
fmt.Fprintf(&sw.rawData, `<row r="%d">`, row)
for i, val := range values {
axis, err := CoordinatesToCellName(col+i, row)
if err != nil {
return err
}
c := xlsxC{R: axis}
if v, ok := val.(Cell); ok {
c.S = v.StyleID
val = v.Value
} else if v, ok := val.(*Cell); ok && v != nil {
c.S = v.StyleID
val = v.Value
}
if err = setCellValFunc(&c, val); err != nil {
sw.rawData.WriteString(`</row>`)
return err
}
writeCell(&sw.rawData, c)
}
sw.rawData.WriteString(`</row>`)
return sw.rawData.Sync()
}
// setCellValFunc provides a function to set value of a cell.
func setCellValFunc(c *xlsxC, val interface{}) (err error) {
switch val := val.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
err = setCellIntFunc(c, val)
case float32:
c.T, c.V = setCellFloat(float64(val), -1, 32)
case float64:
c.T, c.V = setCellFloat(val, -1, 64)
case string:
c.T, c.V, c.XMLSpace = setCellStr(val)
case []byte:
c.T, c.V, c.XMLSpace = setCellStr(string(val))
case time.Duration:
c.T, c.V = setCellDuration(val)
case time.Time:
c.T, c.V, _, err = setCellTime(val)
case bool:
c.T, c.V = setCellBool(val)
case nil:
c.T, c.V, c.XMLSpace = setCellStr("")
default:
c.T, c.V, c.XMLSpace = setCellStr(fmt.Sprint(val))
}
return err
}
// setCellIntFunc is a wrapper of SetCellInt.
func setCellIntFunc(c *xlsxC, val interface{}) (err error) {
switch val := val.(type) {
case int:
c.T, c.V = setCellInt(val)
case int8:
c.T, c.V = setCellInt(int(val))
case int16:
c.T, c.V = setCellInt(int(val))
case int32:
c.T, c.V = setCellInt(int(val))
case int64:
c.T, c.V = setCellInt(int(val))
case uint:
c.T, c.V = setCellInt(int(val))
case uint8:
c.T, c.V = setCellInt(int(val))
case uint16:
c.T, c.V = setCellInt(int(val))
case uint32:
c.T, c.V = setCellInt(int(val))
case uint64:
c.T, c.V = setCellInt(int(val))
default:
}
return
}
func writeCell(buf *bufferedWriter, c xlsxC) {
buf.WriteString(`<c`)
if c.XMLSpace.Value != "" {
fmt.Fprintf(buf, ` xml:%s="%s"`, c.XMLSpace.Name.Local, c.XMLSpace.Value)
}
fmt.Fprintf(buf, ` r="%s"`, c.R)
if c.S != 0 {
fmt.Fprintf(buf, ` s="%d"`, c.S)
}
if c.T != "" {
fmt.Fprintf(buf, ` t="%s"`, c.T)
}
buf.WriteString(`>`)
if c.V != "" {
buf.WriteString(`<v>`)
xml.EscapeText(buf, stringToBytes(c.V))
buf.WriteString(`</v>`)
}
buf.WriteString(`</c>`)
}
// Flush ending the streaming writing process.
func (sw *StreamWriter) Flush() error {
sw.rawData.WriteString(`</sheetData>`)
bulkAppendFields(&sw.rawData, sw.worksheet, 7, 37)
sw.rawData.WriteString(sw.tableParts)
bulkAppendFields(&sw.rawData, sw.worksheet, 39, 39)
sw.rawData.WriteString(`</worksheet>`)
if err := sw.rawData.Flush(); err != nil {
return err
}
sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
delete(sw.File.Sheet, sheetXML)
delete(sw.File.checked, sheetXML)
defer sw.rawData.Close()
b, err := sw.rawData.Bytes()
if err != nil {
return err
}
sw.File.XLSX[sheetXML] = b
return nil
}
// bulkAppendFields bulk-appends fields in a worksheet by specified field
// names order range.
func bulkAppendFields(w io.Writer, ws *xlsxWorksheet, from, to int) {
s := reflect.ValueOf(ws).Elem()
enc := xml.NewEncoder(w)
for i := 0; i < s.NumField(); i++ {
if from <= i && i <= to {
enc.Encode(s.Field(i).Interface())
}
}
}
// bufferedWriter uses a temp file to store an extended buffer. Writes are
// always made to an in-memory buffer, which will always succeed. The buffer
// is written to the temp file with Sync, which may return an error.
// Therefore, Sync should be periodically called and the error checked.
type bufferedWriter struct {
tmp *os.File
buf bytes.Buffer
}
// Write to the in-memory buffer. The err is always nil.
func (bw *bufferedWriter) Write(p []byte) (n int, err error) {
return bw.buf.Write(p)
}
// WriteString wites to the in-memory buffer. The err is always nil.
func (bw *bufferedWriter) WriteString(p string) (n int, err error) {
return bw.buf.WriteString(p)
}
// Reader provides read-access to the underlying buffer/file.
func (bw *bufferedWriter) Reader() (io.Reader, error) {
if bw.tmp == nil {
return bytes.NewReader(bw.buf.Bytes()), nil
}
if err := bw.Flush(); err != nil {
return nil, err
}
fi, err := bw.tmp.Stat()
if err != nil {
return nil, err
}
// os.File.ReadAt does not affect the cursor position and is safe to use here
return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil
}
// Bytes returns the entire content of the bufferedWriter. If a temp file is
// used, Bytes will efficiently allocate a buffer to prevent re-allocations.
func (bw *bufferedWriter) Bytes() ([]byte, error) {
if bw.tmp == nil {
return bw.buf.Bytes(), nil
}
if err := bw.Flush(); err != nil {
return nil, err
}
var buf bytes.Buffer
if fi, err := bw.tmp.Stat(); err == nil {
if size := fi.Size() + bytes.MinRead; size > bytes.MinRead {
if int64(int(size)) == size {
buf.Grow(int(size))
} else {
return nil, bytes.ErrTooLarge
}
}
}
if _, err := bw.tmp.Seek(0, 0); err != nil {
return nil, err
}
_, err := buf.ReadFrom(bw.tmp)
return buf.Bytes(), err
}
// Sync will write the in-memory buffer to a temp file, if the in-memory
// buffer has grown large enough. Any error will be returned.
func (bw *bufferedWriter) Sync() (err error) {
// Try to use local storage
const chunk = 1 << 24
if bw.buf.Len() < chunk {
return nil
}
if bw.tmp == nil {
bw.tmp, err = ioutil.TempFile(os.TempDir(), "excelize-")
if err != nil {
// can not use local storage
return nil
}
}
return bw.Flush()
}
// Flush the entire in-memory buffer to the temp file, if a temp file is being
// used.
func (bw *bufferedWriter) Flush() error {
if bw.tmp == nil {
return nil
}
_, err := bw.buf.WriteTo(bw.tmp)
if err != nil {
return err
}
bw.buf.Reset()
return nil
}
// Close the underlying temp file and reset the in-memory buffer.
func (bw *bufferedWriter) Close() error {
bw.buf.Reset()
if bw.tmp == nil {
return nil
}
defer os.Remove(bw.tmp.Name())
return bw.tmp.Close()
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,490 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/json"
"encoding/xml"
"fmt"
"regexp"
"strconv"
"strings"
)
// parseFormatTableSet provides a function to parse the format settings of the
// table with default value.
func parseFormatTableSet(formatSet string) (*formatTable, error) {
format := formatTable{
TableStyle: "",
ShowRowStripes: true,
}
err := json.Unmarshal(parseFormatSet(formatSet), &format)
return &format, err
}
// AddTable provides the method to add table in a worksheet by given worksheet
// name, coordinate area and format set. For example, create a table of A1:D5
// on Sheet1:
//
// err := f.AddTable("Sheet1", "A1", "D5", ``)
//
// Create a table of F2:H6 on Sheet2 with format set:
//
// err := f.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
//
// Note that the table must be at least two lines including the header. The
// header cells must contain strings and must be unique, and must set the
// header row data of the table before calling the AddTable function. Multiple
// tables coordinate areas that can't have an intersection.
//
// table_name: The name of the table, in the same worksheet name of the table should be unique
//
// table_style: The built-in table style names
//
// TableStyleLight1 - TableStyleLight21
// TableStyleMedium1 - TableStyleMedium28
// TableStyleDark1 - TableStyleDark11
//
func (f *File) AddTable(sheet, hcell, vcell, format string) error {
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
}
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
return err
}
if vcol < hcol {
vcol, hcol = hcol, vcol
}
if vrow < hrow {
vrow, hrow = hrow, vrow
}
tableID := f.countTables() + 1
sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1)
// Add first table for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
rID := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
f.addSheetTable(sheet, rID)
err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet)
if err != nil {
return err
}
f.addContentTypePart(tableID, "table")
return err
}
// countTables provides a function to get table files count storage in the
// folder xl/tables.
func (f *File) countTables() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/tables/table") {
count++
}
}
return count
}
// addSheetTable provides a function to add tablePart element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetTable(sheet string, rID int) {
xlsx, _ := f.workSheetReader(sheet)
table := &xlsxTablePart{
RID: "rId" + strconv.Itoa(rID),
}
if xlsx.TableParts == nil {
xlsx.TableParts = &xlsxTableParts{}
}
xlsx.TableParts.Count++
xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
}
// addTable provides a function to add table by given worksheet name,
// coordinate area and format set.
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet *formatTable) error {
// Correct the minimum number of rows, the table at least two lines.
if y1 == y2 {
y2++
}
// Correct table reference coordinate area, such correct C1:B3 to B1:C3.
ref, err := f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
if err != nil {
return err
}
var tableColumn []*xlsxTableColumn
idx := 0
for i := x1; i <= x2; i++ {
idx++
cell, err := CoordinatesToCellName(i, y1)
if err != nil {
return err
}
name, _ := f.GetCellValue(sheet, cell)
if _, err := strconv.Atoi(name); err == nil {
_ = f.SetCellStr(sheet, cell, name)
}
if name == "" {
name = "Column" + strconv.Itoa(idx)
f.SetCellStr(sheet, cell, name)
}
tableColumn = append(tableColumn, &xlsxTableColumn{
ID: idx,
Name: name,
})
}
name := formatSet.TableName
if name == "" {
name = "Table" + strconv.Itoa(i)
}
t := xlsxTable{
XMLNS: NameSpaceSpreadSheet,
ID: i,
Name: name,
DisplayName: name,
Ref: ref,
AutoFilter: &xlsxAutoFilter{
Ref: ref,
},
TableColumns: &xlsxTableColumns{
Count: idx,
TableColumn: tableColumn,
},
TableStyleInfo: &xlsxTableStyleInfo{
Name: formatSet.TableStyle,
ShowFirstColumn: formatSet.ShowFirstColumn,
ShowLastColumn: formatSet.ShowLastColumn,
ShowRowStripes: formatSet.ShowRowStripes,
ShowColumnStripes: formatSet.ShowColumnStripes,
},
}
table, _ := xml.Marshal(t)
f.saveFileList(tableXML, table)
return nil
}
// parseAutoFilterSet provides a function to parse the settings of the auto
// filter.
func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
format := formatAutoFilter{}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
}
// AutoFilter provides the method to add auto filter in a worksheet by given
// worksheet name, coordinate area and settings. An autofilter in Excel is a
// way of filtering a 2D range of data based on some simple criteria. For
// example applying an autofilter to a cell range A1:D4 in the Sheet1:
//
// err := f.AutoFilter("Sheet1", "A1", "D4", "")
//
// Filter data in an autofilter:
//
// err := f.AutoFilter("Sheet1", "A1", "D4", `{"column":"B","expression":"x != blanks"}`)
//
// column defines the filter columns in a autofilter range based on simple
// criteria
//
// It isn't sufficient to just specify the filter condition. You must also
// hide any rows that don't match the filter condition. Rows are hidden using
// the SetRowVisible() method. Excelize can't filter rows automatically since
// this isn't part of the file format.
//
// Setting a filter criteria for a column:
//
// expression defines the conditions, the following operators are available
// for setting the filter criteria:
//
// ==
// !=
// >
// <
// >=
// <=
// and
// or
//
// An expression can comprise a single statement or two statements separated
// by the 'and' and 'or' operators. For example:
//
// x < 2000
// x > 2000
// x == 2000
// x > 2000 and x < 5000
// x == 2000 or x == 5000
//
// Filtering of blank or non-blank data can be achieved by using a value of
// Blanks or NonBlanks in the expression:
//
// x == Blanks
// x == NonBlanks
//
// Excel also allows some simple string matching operations:
//
// x == b* // begins with b
// x != b* // doesnt begin with b
// x == *b // ends with b
// x != *b // doesnt end with b
// x == *b* // contains b
// x != *b* // doesn't contains b
//
// You can also use '*' to match any character or number and '?' to match any
// single character or number. No other regular expression quantifier is
// supported by Excel's filters. Excel's regular expression characters can be
// escaped using '~'.
//
// The placeholder variable x in the above examples can be replaced by any
// simple string. The actual placeholder name is ignored internally so the
// following are all equivalent:
//
// x < 2000
// col < 2000
// Price < 2000
//
func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
return err
}
if vcol < hcol {
vcol, hcol = hcol, vcol
}
if vrow < hrow {
vrow, hrow = hrow, vrow
}
formatSet, _ := parseAutoFilterSet(format)
var cellStart, cellEnd string
cellStart, err = CoordinatesToCellName(hcol, hrow)
if err != nil {
return err
}
cellEnd, err = CoordinatesToCellName(vcol, vrow)
if err != nil {
return err
}
ref := cellStart + ":" + cellEnd
refRange := vcol - hcol
return f.autoFilter(sheet, ref, refRange, hcol, formatSet)
}
// autoFilter provides a function to extract the tokens from the filter
// expression. The tokens are mainly non-whitespace groups.
func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *formatAutoFilter) error {
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if xlsx.SheetPr != nil {
xlsx.SheetPr.FilterMode = true
}
xlsx.SheetPr = &xlsxSheetPr{FilterMode: true}
filter := &xlsxAutoFilter{
Ref: ref,
}
xlsx.AutoFilter = filter
if formatSet.Column == "" || formatSet.Expression == "" {
return nil
}
fsCol, err := ColumnNameToNumber(formatSet.Column)
if err != nil {
return err
}
offset := fsCol - col
if offset < 0 || offset > refRange {
return fmt.Errorf("incorrect index of column '%s'", formatSet.Column)
}
filter.FilterColumn = &xlsxFilterColumn{
ColID: offset,
}
re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`)
token := re.FindAllString(formatSet.Expression, -1)
if len(token) != 3 && len(token) != 7 {
return fmt.Errorf("incorrect number of tokens in criteria '%s'", formatSet.Expression)
}
expressions, tokens, err := f.parseFilterExpression(formatSet.Expression, token)
if err != nil {
return err
}
f.writeAutoFilter(filter, expressions, tokens)
xlsx.AutoFilter = filter
return nil
}
// writeAutoFilter provides a function to check for single or double custom
// filters as default filters and handle them accordingly.
func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []string) {
if len(exp) == 1 && exp[0] == 2 {
// Single equality.
var filters []*xlsxFilter
filters = append(filters, &xlsxFilter{Val: tokens[0]})
filter.FilterColumn.Filters = &xlsxFilters{Filter: filters}
} else if len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {
// Double equality with "or" operator.
filters := []*xlsxFilter{}
for _, v := range tokens {
filters = append(filters, &xlsxFilter{Val: v})
}
filter.FilterColumn.Filters = &xlsxFilters{Filter: filters}
} else {
// Non default custom filter.
expRel := map[int]int{0: 0, 1: 2}
andRel := map[int]bool{0: true, 1: false}
for k, v := range tokens {
f.writeCustomFilter(filter, exp[expRel[k]], v)
if k == 1 {
filter.FilterColumn.CustomFilters.And = andRel[exp[k]]
}
}
}
}
// writeCustomFilter provides a function to write the <customFilter> element.
func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val string) {
operators := map[int]string{
1: "lessThan",
2: "equal",
3: "lessThanOrEqual",
4: "greaterThan",
5: "notEqual",
6: "greaterThanOrEqual",
22: "equal",
}
customFilter := xlsxCustomFilter{
Operator: operators[operator],
Val: val,
}
if filter.FilterColumn.CustomFilters != nil {
filter.FilterColumn.CustomFilters.CustomFilter = append(filter.FilterColumn.CustomFilters.CustomFilter, &customFilter)
} else {
customFilters := []*xlsxCustomFilter{}
customFilters = append(customFilters, &customFilter)
filter.FilterColumn.CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}
}
}
// parseFilterExpression provides a function to converts the tokens of a
// possibly conditional expression into 1 or 2 sub expressions for further
// parsing.
//
// Examples:
//
// ('x', '==', 2000) -> exp1
// ('x', '>', 2000, 'and', 'x', '<', 5000) -> exp1 and exp2
//
func (f *File) parseFilterExpression(expression string, tokens []string) ([]int, []string, error) {
expressions := []int{}
t := []string{}
if len(tokens) == 7 {
// The number of tokens will be either 3 (for 1 expression) or 7 (for 2
// expressions).
conditional := 0
c := tokens[3]
re, _ := regexp.Match(`(or|\|\|)`, []byte(c))
if re {
conditional = 1
}
expression1, token1, err := f.parseFilterTokens(expression, tokens[0:3])
if err != nil {
return expressions, t, err
}
expression2, token2, err := f.parseFilterTokens(expression, tokens[4:7])
if err != nil {
return expressions, t, err
}
expressions = []int{expression1[0], conditional, expression2[0]}
t = []string{token1, token2}
} else {
exp, token, err := f.parseFilterTokens(expression, tokens)
if err != nil {
return expressions, t, err
}
expressions = exp
t = []string{token}
}
return expressions, t, nil
}
// parseFilterTokens provides a function to parse the 3 tokens of a filter
// expression and return the operator and token.
func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, string, error) {
operators := map[string]int{
"==": 2,
"=": 2,
"=~": 2,
"eq": 2,
"!=": 5,
"!~": 5,
"ne": 5,
"<>": 5,
"<": 1,
"<=": 3,
">": 4,
">=": 6,
}
operator, ok := operators[strings.ToLower(tokens[1])]
if !ok {
// Convert the operator from a number to a descriptive string.
return []int{}, "", fmt.Errorf("unknown operator: %s", tokens[1])
}
token := tokens[2]
// Special handling for Blanks/NonBlanks.
re, _ := regexp.Match("blanks|nonblanks", []byte(strings.ToLower(token)))
if re {
// Only allow Equals or NotEqual in this context.
if operator != 2 && operator != 5 {
return []int{operator}, token, fmt.Errorf("the operator '%s' in expression '%s' is not valid in relation to Blanks/NonBlanks'", tokens[1], expression)
}
token = strings.ToLower(token)
// The operator should always be 2 (=) to flag a "simple" equality in
// the binary record. Therefore we convert <> to =.
if token == "blanks" {
if operator == 5 {
token = " "
}
} else {
if operator == 5 {
operator = 2
token = "blanks"
} else {
operator = 5
token = " "
}
}
}
// if the string token contains an Excel match character then change the
// operator type to indicate a non "simple" equality.
re, _ = regexp.Match("[*?]", []byte(token))
if operator == 2 && re {
operator = 22
}
return []int{operator}, token, nil
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,144 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// vmlDrawing directly maps the root element in the file
// xl/drawings/vmlDrawing%d.vml.
type vmlDrawing struct {
XMLName xml.Name `xml:"xml"`
XMLNSv string `xml:"xmlns:v,attr"`
XMLNSo string `xml:"xmlns:o,attr"`
XMLNSx string `xml:"xmlns:x,attr"`
XMLNSmv string `xml:"xmlns:mv,attr"`
Shapelayout *xlsxShapelayout `xml:"o:shapelayout"`
Shapetype *xlsxShapetype `xml:"v:shapetype"`
Shape []xlsxShape `xml:"v:shape"`
}
// xlsxShapelayout directly maps the shapelayout element. This element contains
// child elements that store information used in the editing and layout of
// shapes.
type xlsxShapelayout struct {
Ext string `xml:"v:ext,attr"`
IDmap *xlsxIDmap `xml:"o:idmap"`
}
// xlsxIDmap directly maps the idmap element.
type xlsxIDmap struct {
Ext string `xml:"v:ext,attr"`
Data int `xml:"data,attr"`
}
// xlsxShape directly maps the shape element.
type xlsxShape struct {
XMLName xml.Name `xml:"v:shape"`
ID string `xml:"id,attr"`
Type string `xml:"type,attr"`
Style string `xml:"style,attr"`
Fillcolor string `xml:"fillcolor,attr"`
Insetmode string `xml:"urn:schemas-microsoft-com:office:office insetmode,attr,omitempty"`
Strokecolor string `xml:"strokecolor,attr,omitempty"`
Val string `xml:",innerxml"`
}
// xlsxShapetype directly maps the shapetype element.
type xlsxShapetype struct {
ID string `xml:"id,attr"`
Coordsize string `xml:"coordsize,attr"`
Spt int `xml:"o:spt,attr"`
Path string `xml:"path,attr"`
Stroke *xlsxStroke `xml:"v:stroke"`
VPath *vPath `xml:"v:path"`
}
// xlsxStroke directly maps the stroke element.
type xlsxStroke struct {
Joinstyle string `xml:"joinstyle,attr"`
}
// vPath directly maps the v:path element.
type vPath struct {
Gradientshapeok string `xml:"gradientshapeok,attr,omitempty"`
Connecttype string `xml:"o:connecttype,attr"`
}
// vFill directly maps the v:fill element. This element must be defined within a
// Shape element.
type vFill struct {
Angle int `xml:"angle,attr,omitempty"`
Color2 string `xml:"color2,attr"`
Type string `xml:"type,attr,omitempty"`
Fill *oFill `xml:"o:fill"`
}
// oFill directly maps the o:fill element.
type oFill struct {
Ext string `xml:"v:ext,attr"`
Type string `xml:"type,attr,omitempty"`
}
// vShadow directly maps the v:shadow element. This element must be defined
// within a Shape element. In addition, the On attribute must be set to True.
type vShadow struct {
On string `xml:"on,attr"`
Color string `xml:"color,attr,omitempty"`
Obscured string `xml:"obscured,attr"`
}
// vTextbox directly maps the v:textbox element. This element must be defined
// within a Shape element.
type vTextbox struct {
Style string `xml:"style,attr"`
Div *xlsxDiv `xml:"div"`
}
// xlsxDiv directly maps the div element.
type xlsxDiv struct {
Style string `xml:"style,attr"`
}
// xClientData (Attached Object Data) directly maps the x:ClientData element.
// This element specifies data associated with objects attached to a
// spreadsheet. While this element might contain any of the child elements
// below, only certain combinations are meaningful. The ObjectType attribute
// determines the kind of object the element represents and which subset of
// child elements is appropriate. Relevant groups are identified for each child
// element.
type xClientData struct {
ObjectType string `xml:"ObjectType,attr"`
MoveWithCells string `xml:"x:MoveWithCells,omitempty"`
SizeWithCells string `xml:"x:SizeWithCells,omitempty"`
Anchor string `xml:"x:Anchor"`
AutoFill string `xml:"x:AutoFill"`
Row int `xml:"x:Row"`
Column int `xml:"x:Column"`
}
// decodeVmlDrawing defines the structure used to parse the file
// xl/drawings/vmlDrawing%d.vml.
type decodeVmlDrawing struct {
Shape []decodeShape `xml:"urn:schemas-microsoft-com:vml shape"`
}
// decodeShape defines the structure used to parse the particular shape element.
type decodeShape struct {
Val string `xml:",innerxml"`
}
// encodeShape defines the structure used to re-serialization shape element.
type encodeShape struct {
Fill *vFill `xml:"v:fill"`
Shadow *vShadow `xml:"v:shadow"`
Path *vPath `xml:"v:path"`
Textbox *vTextbox `xml:"v:textbox"`
ClientData *xClientData `xml:"x:ClientData"`
}

View file

@ -0,0 +1,61 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxProperties specifies to an OOXML document properties such as the
// template used, the number of pages and words, and the application name and
// version.
type xlsxProperties struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties Properties"`
Template string
Manager string
Company string
Pages int
Words int
Characters int
PresentationFormat string
Lines int
Paragraphs int
Slides int
Notes int
TotalTime int
HiddenSlides int
MMClips int
ScaleCrop bool
HeadingPairs *xlsxVectorVariant
TitlesOfParts *xlsxVectorLpstr
LinksUpToDate bool
CharactersWithSpaces int
SharedDoc bool
HyperlinkBase string
HLinks *xlsxVectorVariant
HyperlinksChanged bool
DigSig *xlsxDigSig
Application string
AppVersion string
DocSecurity int
}
// xlsxVectorVariant specifies the set of hyperlinks that were in this
// document when last saved.
type xlsxVectorVariant struct {
Content string `xml:",innerxml"`
}
type xlsxVectorLpstr struct {
Content string `xml:",innerxml"`
}
// xlsxDigSig contains the signature of a digitally signed document.
type xlsxDigSig struct {
Content string `xml:",innerxml"`
}

View file

@ -0,0 +1,83 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxCalcChain directly maps the calcChain element. This element represents the root of the calculation chain.
type xlsxCalcChain struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main calcChain"`
C []xlsxCalcChainC `xml:"c"`
}
// xlsxCalcChainC directly maps the c element.
//
// Attributes | Attributes
// --------------------------+----------------------------------------------------------
// a (Array) | A Boolean flag indicating whether the cell's formula
// | is an array formula. True if this cell's formula is
// | an array formula, false otherwise. If there is a
// | conflict between this attribute and the t attribute
// | of the f element (§18.3.1.40), the t attribute takes
// | precedence. The possible values for this attribute
// | are defined by the W3C XML Schema boolean datatype.
// |
// i (Sheet Id) | A sheet Id of a sheet the cell belongs to. If this is
// | omitted, it is assumed to be the same as the i value
// | of the previous cell.The possible values for this
// | attribute are defined by the W3C XML Schema int datatype.
// |
// l (New Dependency Level) | A Boolean flag indicating that the cell's formula
// | starts a new dependency level. True if the formula
// | starts a new dependency level, false otherwise.
// | Starting a new dependency level means that all
// | concurrent calculations, and child calculations, shall
// | be completed - and the cells have new values - before
// | the calc chain can continue. In other words, this
// | dependency level might depend on levels that came before
// | it, and any later dependency levels might depend on
// | this level; but not later dependency levels can have
// | any calculations started until this dependency level
// | completes.The possible values for this attribute are
// | defined by the W3C XML Schema boolean datatype.
// |
// r (Cell Reference) | An A-1 style reference to a cell.The possible values
// | for this attribute are defined by the ST_CellRef
// | simple type (§18.18.7).
// |
// s (Child Chain) | A Boolean flag indicating whether the cell's formula
// | is on a child chain. True if this cell is part of a
// | child chain, false otherwise. If this is omitted, it
// | is assumed to be the same as the s value of the
// | previous cell .A child chain is a list of calculations
// | that occur which depend on the parent to the chain.
// | There shall not be cross dependencies between child
// | chains. Child chains are not the same as dependency
// | levels - a child chain and its parent are all on the
// | same dependency level. Child chains are series of
// | calculations that can be independently farmed out to
// | other threads or processors.The possible values for
// | this attribute are defined by the W3C XML Schema
// | boolean datatype.
// |
// t (New Thread) | A Boolean flag indicating whether the cell's formula
// | starts a new thread. True if the cell's formula starts
// | a new thread, false otherwise.The possible values for
// | this attribute are defined by the W3C XML Schema
// | boolean datatype.
//
type xlsxCalcChainC struct {
R string `xml:"r,attr"`
I int `xml:"i,attr"`
L bool `xml:"l,attr,omitempty"`
S bool `xml:"s,attr,omitempty"`
T bool `xml:"t,attr,omitempty"`
A bool `xml:"a,attr,omitempty"`
}

View file

@ -0,0 +1,653 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxChartSpace directly maps the chartSpace element. The chart namespace in
// DrawingML is for representing visualizations of numeric data with column
// charts, pie charts, scatter charts, or other types of charts.
type xlsxChartSpace struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/chart chartSpace"`
XMLNSc string `xml:"xmlns:c,attr"`
XMLNSa string `xml:"xmlns:a,attr"`
XMLNSr string `xml:"xmlns:r,attr"`
XMLNSc16r2 string `xml:"xmlns:c16r2,attr"`
Date1904 *attrValBool `xml:"date1904"`
Lang *attrValString `xml:"lang"`
RoundedCorners *attrValBool `xml:"roundedCorners"`
Chart cChart `xml:"chart"`
SpPr *cSpPr `xml:"spPr"`
TxPr *cTxPr `xml:"txPr"`
PrintSettings *cPrintSettings `xml:"printSettings"`
}
// cThicknessSpPr directly maps the element that specifies the thickness of
// the walls or floor as a percentage of the largest dimension of the plot
// volume and SpPr element.
type cThicknessSpPr struct {
Thickness *attrValInt `xml:"thickness"`
SpPr *cSpPr `xml:"spPr"`
}
// cChart (Chart) directly maps the chart element. This element specifies a
// title.
type cChart struct {
Title *cTitle `xml:"title"`
AutoTitleDeleted *cAutoTitleDeleted `xml:"autoTitleDeleted"`
View3D *cView3D `xml:"view3D"`
Floor *cThicknessSpPr `xml:"floor"`
SideWall *cThicknessSpPr `xml:"sideWall"`
BackWall *cThicknessSpPr `xml:"backWall"`
PlotArea *cPlotArea `xml:"plotArea"`
Legend *cLegend `xml:"legend"`
PlotVisOnly *attrValBool `xml:"plotVisOnly"`
DispBlanksAs *attrValString `xml:"dispBlanksAs"`
ShowDLblsOverMax *attrValBool `xml:"showDLblsOverMax"`
}
// cTitle (Title) directly maps the title element. This element specifies a
// title.
type cTitle struct {
Tx cTx `xml:"tx,omitempty"`
Layout string `xml:"layout,omitempty"`
Overlay *attrValBool `xml:"overlay"`
SpPr cSpPr `xml:"spPr,omitempty"`
TxPr cTxPr `xml:"txPr,omitempty"`
}
// cTx (Chart Text) directly maps the tx element. This element specifies text
// to use on a chart, including rich text formatting.
type cTx struct {
StrRef *cStrRef `xml:"strRef"`
Rich *cRich `xml:"rich,omitempty"`
}
// cRich (Rich Text) directly maps the rich element. This element contains a
// string with rich text formatting.
type cRich struct {
BodyPr aBodyPr `xml:"a:bodyPr,omitempty"`
LstStyle string `xml:"a:lstStyle,omitempty"`
P aP `xml:"a:p"`
}
// aBodyPr (Body Properties) directly maps the a:bodyPr element. This element
// defines the body properties for the text body within a shape.
type aBodyPr struct {
Anchor string `xml:"anchor,attr,omitempty"`
AnchorCtr bool `xml:"anchorCtr,attr"`
Rot int `xml:"rot,attr"`
BIns float64 `xml:"bIns,attr,omitempty"`
CompatLnSpc bool `xml:"compatLnSpc,attr,omitempty"`
ForceAA bool `xml:"forceAA,attr,omitempty"`
FromWordArt bool `xml:"fromWordArt,attr,omitempty"`
HorzOverflow string `xml:"horzOverflow,attr,omitempty"`
LIns float64 `xml:"lIns,attr,omitempty"`
NumCol int `xml:"numCol,attr,omitempty"`
RIns float64 `xml:"rIns,attr,omitempty"`
RtlCol bool `xml:"rtlCol,attr,omitempty"`
SpcCol int `xml:"spcCol,attr,omitempty"`
SpcFirstLastPara bool `xml:"spcFirstLastPara,attr"`
TIns float64 `xml:"tIns,attr,omitempty"`
Upright bool `xml:"upright,attr,omitempty"`
Vert string `xml:"vert,attr,omitempty"`
VertOverflow string `xml:"vertOverflow,attr,omitempty"`
Wrap string `xml:"wrap,attr,omitempty"`
}
// aP (Paragraph) directly maps the a:p element. This element specifies a
// paragraph of content in the document.
type aP struct {
PPr *aPPr `xml:"a:pPr"`
R *aR `xml:"a:r"`
EndParaRPr *aEndParaRPr `xml:"a:endParaRPr"`
}
// aPPr (Paragraph Properties) directly maps the a:pPr element. This element
// specifies a set of paragraph properties which shall be applied to the
// contents of the parent paragraph after all style/numbering/table properties
// have been applied to the text. These properties are defined as direct
// formatting, since they are directly applied to the paragraph and supersede
// any formatting from styles.
type aPPr struct {
DefRPr aRPr `xml:"a:defRPr"`
}
// aSolidFill (Solid Fill) directly maps the solidFill element. This element
// specifies a solid color fill. The shape is filled entirely with the specified
// color.
type aSolidFill struct {
SchemeClr *aSchemeClr `xml:"a:schemeClr"`
SrgbClr *attrValString `xml:"a:srgbClr"`
}
// aSchemeClr (Scheme Color) directly maps the a:schemeClr element. This
// element specifies a color bound to a user's theme. As with all elements which
// define a color, it is possible to apply a list of color transforms to the
// base color defined.
type aSchemeClr struct {
Val string `xml:"val,attr,omitempty"`
LumMod *attrValInt `xml:"a:lumMod"`
LumOff *attrValInt `xml:"a:lumOff"`
}
// attrValInt directly maps the val element with integer data type as an
// attribute。
type attrValInt struct {
Val *int `xml:"val,attr"`
}
// attrValFloat directly maps the val element with float64 data type as an
// attribute。
type attrValFloat struct {
Val *float64 `xml:"val,attr"`
}
// attrValBool directly maps the val element with boolean data type as an
// attribute。
type attrValBool struct {
Val *bool `xml:"val,attr"`
}
// attrValString directly maps the val element with string data type as an
// attribute。
type attrValString struct {
Val *string `xml:"val,attr"`
}
// aCs directly maps the a:cs element.
type aCs struct {
Typeface string `xml:"typeface,attr"`
}
// aEa directly maps the a:ea element.
type aEa struct {
Typeface string `xml:"typeface,attr"`
}
// aLatin (Latin Font) directly maps the a:latin element. This element
// specifies that a Latin font be used for a specific run of text. This font is
// specified with a typeface attribute much like the others but is specifically
// classified as a Latin font.
type aLatin struct {
Typeface string `xml:"typeface,attr"`
}
// aR directly maps the a:r element.
type aR struct {
RPr aRPr `xml:"a:rPr,omitempty"`
T string `xml:"a:t,omitempty"`
}
// aRPr (Run Properties) directly maps the rPr element. This element
// specifies a set of run properties which shall be applied to the contents of
// the parent run after all style formatting has been applied to the text. These
// properties are defined as direct formatting, since they are directly applied
// to the run and supersede any formatting from styles.
type aRPr struct {
AltLang string `xml:"altLang,attr,omitempty"`
B bool `xml:"b,attr"`
Baseline int `xml:"baseline,attr"`
Bmk string `xml:"bmk,attr,omitempty"`
Cap string `xml:"cap,attr,omitempty"`
Dirty bool `xml:"dirty,attr,omitempty"`
Err bool `xml:"err,attr,omitempty"`
I bool `xml:"i,attr"`
Kern int `xml:"kern,attr"`
Kumimoji bool `xml:"kumimoji,attr,omitempty"`
Lang string `xml:"lang,attr,omitempty"`
NoProof bool `xml:"noProof,attr,omitempty"`
NormalizeH bool `xml:"normalizeH,attr,omitempty"`
SmtClean bool `xml:"smtClean,attr,omitempty"`
SmtID uint64 `xml:"smtId,attr,omitempty"`
Spc int `xml:"spc,attr"`
Strike string `xml:"strike,attr,omitempty"`
Sz float64 `xml:"sz,attr,omitempty"`
U string `xml:"u,attr,omitempty"`
SolidFill *aSolidFill `xml:"a:solidFill"`
Latin *aLatin `xml:"a:latin"`
Ea *aEa `xml:"a:ea"`
Cs *aCs `xml:"a:cs"`
}
// cSpPr (Shape Properties) directly maps the spPr element. This element
// specifies the visual shape properties that can be applied to a shape. These
// properties include the shape fill, outline, geometry, effects, and 3D
// orientation.
type cSpPr struct {
NoFill *string `xml:"a:noFill"`
SolidFill *aSolidFill `xml:"a:solidFill"`
Ln *aLn `xml:"a:ln"`
Sp3D *aSp3D `xml:"a:sp3d"`
EffectLst *string `xml:"a:effectLst"`
}
// aSp3D (3-D Shape Properties) directly maps the a:sp3d element. This element
// defines the 3D properties associated with a particular shape in DrawingML.
// The 3D properties which can be applied to a shape are top and bottom bevels,
// a contour and an extrusion.
type aSp3D struct {
ContourW int `xml:"contourW,attr"`
ContourClr *aContourClr `xml:"a:contourClr"`
}
// aContourClr (Contour Color) directly maps the a:contourClr element. This
// element defines the color for the contour on a shape. The contour of a shape
// is a solid filled line which surrounds the outer edges of the shape.
type aContourClr struct {
SchemeClr *aSchemeClr `xml:"a:schemeClr"`
}
// aLn (Outline) directly maps the a:ln element. This element specifies an
// outline style that can be applied to a number of different objects such as
// shapes and text. The line allows for the specifying of many different types
// of outlines including even line dashes and bevels.
type aLn struct {
Algn string `xml:"algn,attr,omitempty"`
Cap string `xml:"cap,attr,omitempty"`
Cmpd string `xml:"cmpd,attr,omitempty"`
W int `xml:"w,attr,omitempty"`
NoFill string `xml:"a:noFill,omitempty"`
Round string `xml:"a:round,omitempty"`
SolidFill *aSolidFill `xml:"a:solidFill"`
}
// cTxPr (Text Properties) directly maps the txPr element. This element
// specifies text formatting. The lstStyle element is not supported.
type cTxPr struct {
BodyPr aBodyPr `xml:"a:bodyPr,omitempty"`
LstStyle string `xml:"a:lstStyle,omitempty"`
P aP `xml:"a:p,omitempty"`
}
// aEndParaRPr (End Paragraph Run Properties) directly maps the a:endParaRPr
// element. This element specifies the text run properties that are to be used
// if another run is inserted after the last run specified. This effectively
// saves the run property state so that it can be applied when the user enters
// additional text. If this element is omitted, then the application can
// determine which default properties to apply. It is recommended that this
// element be specified at the end of the list of text runs within the paragraph
// so that an orderly list is maintained.
type aEndParaRPr struct {
Lang string `xml:"lang,attr"`
AltLang string `xml:"altLang,attr,omitempty"`
Sz int `xml:"sz,attr,omitempty"`
}
// cAutoTitleDeleted (Auto Title Is Deleted) directly maps the
// autoTitleDeleted element. This element specifies the title shall not be
// shown for this chart.
type cAutoTitleDeleted struct {
Val bool `xml:"val,attr"`
}
// cView3D (View In 3D) directly maps the view3D element. This element
// specifies the 3-D view of the chart.
type cView3D struct {
RotX *attrValInt `xml:"rotX"`
RotY *attrValInt `xml:"rotY"`
RAngAx *attrValInt `xml:"rAngAx"`
DepthPercent *attrValInt `xml:"depthPercent"`
Perspective *attrValInt `xml:"perspective"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// cPlotArea directly maps the plotArea element. This element specifies the
// plot area of the chart.
type cPlotArea struct {
Layout *string `xml:"layout"`
AreaChart *cCharts `xml:"areaChart"`
Area3DChart *cCharts `xml:"area3DChart"`
BarChart *cCharts `xml:"barChart"`
Bar3DChart *cCharts `xml:"bar3DChart"`
BubbleChart *cCharts `xml:"bubbleChart"`
DoughnutChart *cCharts `xml:"doughnutChart"`
LineChart *cCharts `xml:"lineChart"`
PieChart *cCharts `xml:"pieChart"`
Pie3DChart *cCharts `xml:"pie3DChart"`
OfPieChart *cCharts `xml:"ofPieChart"`
RadarChart *cCharts `xml:"radarChart"`
ScatterChart *cCharts `xml:"scatterChart"`
Surface3DChart *cCharts `xml:"surface3DChart"`
SurfaceChart *cCharts `xml:"surfaceChart"`
CatAx []*cAxs `xml:"catAx"`
ValAx []*cAxs `xml:"valAx"`
SerAx []*cAxs `xml:"serAx"`
SpPr *cSpPr `xml:"spPr"`
}
// cCharts specifies the common element of the chart.
type cCharts struct {
BarDir *attrValString `xml:"barDir"`
BubbleScale *attrValFloat `xml:"bubbleScale"`
Grouping *attrValString `xml:"grouping"`
RadarStyle *attrValString `xml:"radarStyle"`
ScatterStyle *attrValString `xml:"scatterStyle"`
OfPieType *attrValString `xml:"ofPieType"`
VaryColors *attrValBool `xml:"varyColors"`
Wireframe *attrValBool `xml:"wireframe"`
Ser *[]cSer `xml:"ser"`
SerLines *attrValString `xml:"serLines"`
DLbls *cDLbls `xml:"dLbls"`
Shape *attrValString `xml:"shape"`
HoleSize *attrValInt `xml:"holeSize"`
Smooth *attrValBool `xml:"smooth"`
Overlap *attrValInt `xml:"overlap"`
AxID []*attrValInt `xml:"axId"`
}
// cAxs directly maps the catAx and valAx element.
type cAxs struct {
AxID *attrValInt `xml:"axId"`
Scaling *cScaling `xml:"scaling"`
Delete *attrValBool `xml:"delete"`
AxPos *attrValString `xml:"axPos"`
MajorGridlines *cChartLines `xml:"majorGridlines"`
MinorGridlines *cChartLines `xml:"minorGridlines"`
NumFmt *cNumFmt `xml:"numFmt"`
MajorTickMark *attrValString `xml:"majorTickMark"`
MinorTickMark *attrValString `xml:"minorTickMark"`
TickLblPos *attrValString `xml:"tickLblPos"`
SpPr *cSpPr `xml:"spPr"`
TxPr *cTxPr `xml:"txPr"`
CrossAx *attrValInt `xml:"crossAx"`
Crosses *attrValString `xml:"crosses"`
CrossBetween *attrValString `xml:"crossBetween"`
MajorUnit *attrValFloat `xml:"majorUnit"`
MinorUnit *attrValFloat `xml:"minorUnit"`
Auto *attrValBool `xml:"auto"`
LblAlgn *attrValString `xml:"lblAlgn"`
LblOffset *attrValInt `xml:"lblOffset"`
TickLblSkip *attrValInt `xml:"tickLblSkip"`
TickMarkSkip *attrValInt `xml:"tickMarkSkip"`
NoMultiLvlLbl *attrValBool `xml:"noMultiLvlLbl"`
}
// cChartLines directly maps the chart lines content model.
type cChartLines struct {
SpPr *cSpPr `xml:"spPr"`
}
// cScaling directly maps the scaling element. This element contains
// additional axis settings.
type cScaling struct {
Orientation *attrValString `xml:"orientation"`
Max *attrValFloat `xml:"max"`
Min *attrValFloat `xml:"min"`
}
// cNumFmt (Numbering Format) directly maps the numFmt element. This element
// specifies number formatting for the parent element.
type cNumFmt struct {
FormatCode string `xml:"formatCode,attr"`
SourceLinked bool `xml:"sourceLinked,attr"`
}
// cSer directly maps the ser element. This element specifies a series on a
// chart.
type cSer struct {
IDx *attrValInt `xml:"idx"`
Order *attrValInt `xml:"order"`
Tx *cTx `xml:"tx"`
SpPr *cSpPr `xml:"spPr"`
DPt []*cDPt `xml:"dPt"`
DLbls *cDLbls `xml:"dLbls"`
Marker *cMarker `xml:"marker"`
InvertIfNegative *attrValBool `xml:"invertIfNegative"`
Cat *cCat `xml:"cat"`
Val *cVal `xml:"val"`
XVal *cCat `xml:"xVal"`
YVal *cVal `xml:"yVal"`
Smooth *attrValBool `xml:"smooth"`
BubbleSize *cVal `xml:"bubbleSize"`
Bubble3D *attrValBool `xml:"bubble3D"`
}
// cMarker (Marker) directly maps the marker element. This element specifies a
// data marker.
type cMarker struct {
Symbol *attrValString `xml:"symbol"`
Size *attrValInt `xml:"size"`
SpPr *cSpPr `xml:"spPr"`
}
// cDPt (Data Point) directly maps the dPt element. This element specifies a
// single data point.
type cDPt struct {
IDx *attrValInt `xml:"idx"`
Bubble3D *attrValBool `xml:"bubble3D"`
SpPr *cSpPr `xml:"spPr"`
}
// cCat (Category Axis Data) directly maps the cat element. This element
// specifies the data used for the category axis.
type cCat struct {
StrRef *cStrRef `xml:"strRef"`
}
// cStrRef (String Reference) directly maps the strRef element. This element
// specifies a reference to data for a single data label or title with a cache
// of the last values used.
type cStrRef struct {
F string `xml:"f"`
StrCache *cStrCache `xml:"strCache"`
}
// cStrCache (String Cache) directly maps the strCache element. This element
// specifies the last string data used for a chart.
type cStrCache struct {
Pt []*cPt `xml:"pt"`
PtCount *attrValInt `xml:"ptCount"`
}
// cPt directly maps the pt element. This element specifies data for a
// particular data point.
type cPt struct {
IDx int `xml:"idx,attr"`
V *string `xml:"v"`
}
// cVal directly maps the val element. This element specifies the data values
// which shall be used to define the location of data markers on a chart.
type cVal struct {
NumRef *cNumRef `xml:"numRef"`
}
// cNumRef directly maps the numRef element. This element specifies a
// reference to numeric data with a cache of the last values used.
type cNumRef struct {
F string `xml:"f"`
NumCache *cNumCache `xml:"numCache"`
}
// cNumCache directly maps the numCache element. This element specifies the
// last data shown on the chart for a series.
type cNumCache struct {
FormatCode string `xml:"formatCode"`
Pt []*cPt `xml:"pt"`
PtCount *attrValInt `xml:"ptCount"`
}
// cDLbls (Data Lables) directly maps the dLbls element. This element serves
// as a root element that specifies the settings for the data labels for an
// entire series or the entire chart. It contains child elements that specify
// the specific formatting and positioning settings.
type cDLbls struct {
ShowLegendKey *attrValBool `xml:"showLegendKey"`
ShowVal *attrValBool `xml:"showVal"`
ShowCatName *attrValBool `xml:"showCatName"`
ShowSerName *attrValBool `xml:"showSerName"`
ShowPercent *attrValBool `xml:"showPercent"`
ShowBubbleSize *attrValBool `xml:"showBubbleSize"`
ShowLeaderLines *attrValBool `xml:"showLeaderLines"`
}
// cLegend (Legend) directly maps the legend element. This element specifies
// the legend.
type cLegend struct {
Layout *string `xml:"layout"`
LegendPos *attrValString `xml:"legendPos"`
Overlay *attrValBool `xml:"overlay"`
SpPr *cSpPr `xml:"spPr"`
TxPr *cTxPr `xml:"txPr"`
}
// cPrintSettings directly maps the printSettings element. This element
// specifies the print settings for the chart.
type cPrintSettings struct {
HeaderFooter *string `xml:"headerFooter"`
PageMargins *cPageMargins `xml:"pageMargins"`
PageSetup *string `xml:"pageSetup"`
}
// cPageMargins directly maps the pageMargins element. This element specifies
// the page margins for a chart.
type cPageMargins struct {
B float64 `xml:"b,attr"`
Footer float64 `xml:"footer,attr"`
Header float64 `xml:"header,attr"`
L float64 `xml:"l,attr"`
R float64 `xml:"r,attr"`
T float64 `xml:"t,attr"`
}
// formatChartAxis directly maps the format settings of the chart axis.
type formatChartAxis struct {
Crossing string `json:"crossing"`
MajorGridlines bool `json:"major_grid_lines"`
MinorGridlines bool `json:"minor_grid_lines"`
MajorTickMark string `json:"major_tick_mark"`
MinorTickMark string `json:"minor_tick_mark"`
MinorUnitType string `json:"minor_unit_type"`
MajorUnit float64 `json:"major_unit"`
MajorUnitType string `json:"major_unit_type"`
TickLabelSkip int `json:"tick_label_skip"`
DisplayUnits string `json:"display_units"`
DisplayUnitsVisible bool `json:"display_units_visible"`
DateAxis bool `json:"date_axis"`
ReverseOrder bool `json:"reverse_order"`
Maximum float64 `json:"maximum"`
Minimum float64 `json:"minimum"`
NumFormat string `json:"num_format"`
NumFont struct {
Color string `json:"color"`
Bold bool `json:"bold"`
Italic bool `json:"italic"`
Underline bool `json:"underline"`
} `json:"num_font"`
NameLayout formatLayout `json:"name_layout"`
}
type formatChartDimension struct {
Width int `json:"width"`
Height int `json:"height"`
}
// formatChart directly maps the format settings of the chart.
type formatChart struct {
Type string `json:"type"`
Series []formatChartSeries `json:"series"`
Format formatPicture `json:"format"`
Dimension formatChartDimension `json:"dimension"`
Legend formatChartLegend `json:"legend"`
Title formatChartTitle `json:"title"`
XAxis formatChartAxis `json:"x_axis"`
YAxis formatChartAxis `json:"y_axis"`
Chartarea struct {
Border struct {
None bool `json:"none"`
} `json:"border"`
Fill struct {
Color string `json:"color"`
} `json:"fill"`
Pattern struct {
Pattern string `json:"pattern"`
FgColor string `json:"fg_color"`
BgColor string `json:"bg_color"`
} `json:"pattern"`
} `json:"chartarea"`
Plotarea struct {
ShowBubbleSize bool `json:"show_bubble_size"`
ShowCatName bool `json:"show_cat_name"`
ShowLeaderLines bool `json:"show_leader_lines"`
ShowPercent bool `json:"show_percent"`
ShowSerName bool `json:"show_series_name"`
ShowVal bool `json:"show_val"`
Gradient struct {
Colors []string `json:"colors"`
} `json:"gradient"`
Border struct {
Color string `json:"color"`
Width int `json:"width"`
DashType string `json:"dash_type"`
} `json:"border"`
Fill struct {
Color string `json:"color"`
} `json:"fill"`
Layout formatLayout `json:"layout"`
} `json:"plotarea"`
ShowBlanksAs string `json:"show_blanks_as"`
ShowHiddenData bool `json:"show_hidden_data"`
SetRotation int `json:"set_rotation"`
SetHoleSize int `json:"set_hole_size"`
order int
}
// formatChartLegend directly maps the format settings of the chart legend.
type formatChartLegend struct {
None bool `json:"none"`
DeleteSeries []int `json:"delete_series"`
Font Font `json:"font"`
Layout formatLayout `json:"layout"`
Position string `json:"position"`
ShowLegendEntry bool `json:"show_legend_entry"`
ShowLegendKey bool `json:"show_legend_key"`
}
// formatChartSeries directly maps the format settings of the chart series.
type formatChartSeries struct {
Name string `json:"name"`
Categories string `json:"categories"`
Values string `json:"values"`
Line struct {
None bool `json:"none"`
Color string `json:"color"`
Width float64 `json:"width"`
} `json:"line"`
Marker struct {
Type string `json:"type"`
Size int `json:"size"`
Width float64 `json:"width"`
Border struct {
Color string `json:"color"`
None bool `json:"none"`
} `json:"border"`
Fill struct {
Color string `json:"color"`
None bool `json:"none"`
} `json:"fill"`
} `json:"marker"`
}
// formatChartTitle directly maps the format settings of the chart title.
type formatChartTitle struct {
None bool `json:"none"`
Name string `json:"name"`
Overlay bool `json:"overlay"`
Layout formatLayout `json:"layout"`
}
// formatLayout directly maps the format settings of the element layout.
type formatLayout struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Width float64 `json:"width"`
Height float64 `json:"height"`
}

View file

@ -0,0 +1,88 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// struct code generated by github.com/xuri/xgen
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxChartsheet directly maps the chartsheet element of Chartsheet Parts in
// a SpreadsheetML document.
type xlsxChartsheet struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main chartsheet"`
SheetPr []*xlsxChartsheetPr `xml:"sheetPr"`
SheetViews []*xlsxChartsheetViews `xml:"sheetViews"`
SheetProtection []*xlsxChartsheetProtection `xml:"sheetProtection"`
CustomSheetViews []*xlsxCustomChartsheetViews `xml:"customSheetViews"`
PageMargins *xlsxPageMargins `xml:"pageMargins"`
PageSetup []*xlsxPageSetUp `xml:"pageSetup"`
HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"`
Drawing *xlsxDrawing `xml:"drawing"`
DrawingHF []*xlsxDrawingHF `xml:"drawingHF"`
Picture []*xlsxPicture `xml:"picture"`
WebPublishItems []*xlsxInnerXML `xml:"webPublishItems"`
ExtLst []*xlsxExtLst `xml:"extLst"`
}
// xlsxChartsheetPr specifies chart sheet properties.
type xlsxChartsheetPr struct {
XMLName xml.Name `xml:"sheetPr"`
PublishedAttr bool `xml:"published,attr,omitempty"`
CodeNameAttr string `xml:"codeName,attr,omitempty"`
TabColor []*xlsxTabColor `xml:"tabColor"`
}
// xlsxChartsheetViews specifies chart sheet views.
type xlsxChartsheetViews struct {
XMLName xml.Name `xml:"sheetViews"`
SheetView []*xlsxChartsheetView `xml:"sheetView"`
ExtLst []*xlsxExtLst `xml:"extLst"`
}
// xlsxChartsheetView defines custom view properties for chart sheets.
type xlsxChartsheetView struct {
XMLName xml.Name `xml:"sheetView"`
TabSelectedAttr bool `xml:"tabSelected,attr,omitempty"`
ZoomScaleAttr uint32 `xml:"zoomScale,attr,omitempty"`
WorkbookViewIDAttr uint32 `xml:"workbookViewId,attr"`
ZoomToFitAttr bool `xml:"zoomToFit,attr,omitempty"`
ExtLst []*xlsxExtLst `xml:"extLst"`
}
// xlsxChartsheetProtection collection expresses the chart sheet protection
// options to enforce when the chart sheet is protected.
type xlsxChartsheetProtection struct {
XMLName xml.Name `xml:"sheetProtection"`
AlgorithmNameAttr string `xml:"algorithmName,attr,omitempty"`
HashValueAttr []byte `xml:"hashValue,attr,omitempty"`
SaltValueAttr []byte `xml:"saltValue,attr,omitempty"`
SpinCountAttr uint32 `xml:"spinCount,attr,omitempty"`
ContentAttr bool `xml:"content,attr,omitempty"`
ObjectsAttr bool `xml:"objects,attr,omitempty"`
}
// xlsxCustomChartsheetViews collection of custom Chart Sheet View
// information.
type xlsxCustomChartsheetViews struct {
XMLName xml.Name `xml:"customChartsheetViews"`
CustomSheetView []*xlsxCustomChartsheetView `xml:"customSheetView"`
}
// xlsxCustomChartsheetView defines custom view properties for chart sheets.
type xlsxCustomChartsheetView struct {
XMLName xml.Name `xml:"customChartsheetView"`
GUIDAttr string `xml:"guid,attr"`
ScaleAttr uint32 `xml:"scale,attr,omitempty"`
StateAttr string `xml:"state,attr,omitempty"`
ZoomToFitAttr bool `xml:"zoomToFit,attr,omitempty"`
PageMargins []*xlsxPageMargins `xml:"pageMargins"`
PageSetup []*xlsxPageSetUp `xml:"pageSetup"`
HeaderFooter []*xlsxHeaderFooter `xml:"headerFooter"`
}

View file

@ -0,0 +1,85 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxComments directly maps the comments element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main. A comment is a
// rich text note that is attached to and associated with a cell, separate from
// other cell content. Comment content is stored separate from the cell, and is
// displayed in a drawing object (like a text box) that is separate from, but
// associated with, a cell. Comments are used as reminders, such as noting how a
// complex formula works, or to provide feedback to other users. Comments can
// also be used to explain assumptions made in a formula or to call out
// something special about the cell.
type xlsxComments struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main comments"`
Authors []xlsxAuthor `xml:"authors"`
CommentList xlsxCommentList `xml:"commentList"`
}
// xlsxAuthor directly maps the author element. This element holds a string
// representing the name of a single author of comments. Every comment shall
// have an author. The maximum length of the author string is an implementation
// detail, but a good guideline is 255 chars.
type xlsxAuthor struct {
Author string `xml:"author"`
}
// xlsxCommentList (List of Comments) directly maps the xlsxCommentList element.
// This element is a container that holds a list of comments for the sheet.
type xlsxCommentList struct {
Comment []xlsxComment `xml:"comment"`
}
// xlsxComment directly maps the comment element. This element represents a
// single user entered comment. Each comment shall have an author and can
// optionally contain richly formatted text.
type xlsxComment struct {
Ref string `xml:"ref,attr"`
AuthorID int `xml:"authorId,attr"`
Text xlsxText `xml:"text"`
}
// xlsxText directly maps the text element. This element contains rich text
// which represents the text of a comment. The maximum length for this text is a
// spreadsheet application implementation detail. A recommended guideline is
// 32767 chars.
type xlsxText struct {
T *string `xml:"t"`
R []xlsxR `xml:"r"`
RPh *xlsxPhoneticRun `xml:"rPh"`
PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"`
}
// xlsxPhoneticRun element represents a run of text which displays a phonetic
// hint for this String Item (si). Phonetic hints are used to give information
// about the pronunciation of an East Asian language. The hints are displayed
// as text within the spreadsheet cells across the top portion of the cell.
type xlsxPhoneticRun struct {
Sb uint32 `xml:"sb,attr"`
Eb uint32 `xml:"eb,attr"`
T string `xml:"t,attr"`
}
// formatComment directly maps the format settings of the comment.
type formatComment struct {
Author string `json:"author"`
Text string `json:"text"`
}
// Comment directly maps the comment information.
type Comment struct {
Author string `json:"author"`
AuthorID int `json:"author_id"`
Ref string `json:"ref"`
Text string `json:"text"`
}

View file

@ -0,0 +1,35 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxTypes directly maps the types element of content types for relationship
// parts, it takes a Multipurpose Internet Mail Extension (MIME) media type as a
// value.
type xlsxTypes struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/content-types Types"`
Overrides []xlsxOverride `xml:"Override"`
Defaults []xlsxDefault `xml:"Default"`
}
// xlsxOverride directly maps the override element in the namespace
// http://schemas.openxmlformats.org/package/2006/content-types
type xlsxOverride struct {
PartName string `xml:",attr"`
ContentType string `xml:",attr"`
}
// xlsxDefault directly maps the default element in the namespace
// http://schemas.openxmlformats.org/package/2006/content-types
type xlsxDefault struct {
Extension string `xml:",attr"`
ContentType string `xml:",attr"`
}

View file

@ -0,0 +1,89 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// DocProperties directly maps the document core properties.
type DocProperties struct {
Category string
ContentStatus string
Created string
Creator string
Description string
Identifier string
Keywords string
LastModifiedBy string
Modified string
Revision string
Subject string
Title string
Language string
Version string
}
// decodeCoreProperties directly maps the root element for a part of this
// content type shall coreProperties. In order to solve the problem that the
// label structure is changed after serialization and deserialization, two
// different structures are defined. decodeCoreProperties just for
// deserialization.
type decodeCoreProperties struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/metadata/core-properties coreProperties"`
Title string `xml:"http://purl.org/dc/elements/1.1/ title,omitempty"`
Subject string `xml:"http://purl.org/dc/elements/1.1/ subject,omitempty"`
Creator string `xml:"http://purl.org/dc/elements/1.1/ creator"`
Keywords string `xml:"keywords,omitempty"`
Description string `xml:"http://purl.org/dc/elements/1.1/ description,omitempty"`
LastModifiedBy string `xml:"lastModifiedBy"`
Language string `xml:"http://purl.org/dc/elements/1.1/ language,omitempty"`
Identifier string `xml:"http://purl.org/dc/elements/1.1/ identifier,omitempty"`
Revision string `xml:"revision,omitempty"`
Created struct {
Text string `xml:",chardata"`
Type string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
} `xml:"http://purl.org/dc/terms/ created"`
Modified struct {
Text string `xml:",chardata"`
Type string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
} `xml:"http://purl.org/dc/terms/ modified"`
ContentStatus string `xml:"contentStatus,omitempty"`
Category string `xml:"category,omitempty"`
Version string `xml:"version,omitempty"`
}
// xlsxCoreProperties directly maps the root element for a part of this
// content type shall coreProperties.
type xlsxCoreProperties struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/metadata/core-properties coreProperties"`
Dc string `xml:"xmlns:dc,attr"`
Dcterms string `xml:"xmlns:dcterms,attr"`
Dcmitype string `xml:"xmlns:dcmitype,attr"`
XSI string `xml:"xmlns:xsi,attr"`
Title string `xml:"dc:title,omitempty"`
Subject string `xml:"dc:subject,omitempty"`
Creator string `xml:"dc:creator"`
Keywords string `xml:"keywords,omitempty"`
Description string `xml:"dc:description,omitempty"`
LastModifiedBy string `xml:"lastModifiedBy"`
Language string `xml:"dc:language,omitempty"`
Identifier string `xml:"dc:identifier,omitempty"`
Revision string `xml:"revision,omitempty"`
Created struct {
Text string `xml:",chardata"`
Type string `xml:"xsi:type,attr"`
} `xml:"dcterms:created"`
Modified struct {
Text string `xml:",chardata"`
Type string `xml:"xsi:type,attr"`
} `xml:"dcterms:modified"`
ContentStatus string `xml:"contentStatus,omitempty"`
Category string `xml:"category,omitempty"`
Version string `xml:"version,omitempty"`
}

View file

@ -0,0 +1,233 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// decodeCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape
// Size) and twoCellAnchor (Two Cell Anchor Shape Size). This element
// specifies a two cell anchor placeholder for a group, a shape, or a drawing
// element. It moves with cells and its extents are in EMU units.
type decodeCellAnchor struct {
EditAs string `xml:"editAs,attr,omitempty"`
From *decodeFrom `xml:"from"`
To *decodeTo `xml:"to"`
Sp *decodeSp `xml:"sp"`
ClientData *decodeClientData `xml:"clientData"`
Content string `xml:",innerxml"`
}
// xdrSp (Shape) directly maps the sp element. This element specifies the
// existence of a single shape. A shape can either be a preset or a custom
// geometry, defined using the SpreadsheetDrawingML framework. In addition to
// a geometry each shape can have both visual and non-visual properties
// attached. Text and corresponding styling information can also be attached
// to a shape. This shape is specified along with all other shapes within
// either the shape tree or group shape elements.
type decodeSp struct {
NvSpPr *decodeNvSpPr `xml:"nvSpPr"`
SpPr *decodeSpPr `xml:"spPr"`
}
// decodeSp (Non-Visual Properties for a Shape) directly maps the nvSpPr
// element. This element specifies all non-visual properties for a shape. This
// element is a container for the non-visual identification properties, shape
// properties and application properties that are to be associated with a
// shape. This allows for additional information that does not affect the
// appearance of the shape to be stored.
type decodeNvSpPr struct {
CNvPr *decodeCNvPr `xml:"cNvPr"`
ExtLst *decodeExt `xml:"extLst"`
CNvSpPr *decodeCNvSpPr `xml:"cNvSpPr"`
}
// decodeCNvSpPr (Connection Non-Visual Shape Properties) directly maps the
// cNvSpPr element. This element specifies the set of non-visual properties
// for a connection shape. These properties specify all data about the
// connection shape which do not affect its display within a spreadsheet.
type decodeCNvSpPr struct {
TxBox bool `xml:"txBox,attr"`
}
// decodeWsDr directly maps the root element for a part of this content type
// shall wsDr. In order to solve the problem that the label structure is
// changed after serialization and deserialization, two different structures
// are defined. decodeWsDr just for deserialization.
type decodeWsDr struct {
A string `xml:"xmlns a,attr"`
Xdr string `xml:"xmlns xdr,attr"`
R string `xml:"xmlns r,attr"`
OneCellAnchor []*decodeCellAnchor `xml:"oneCellAnchor,omitempty"`
TwoCellAnchor []*decodeCellAnchor `xml:"twoCellAnchor,omitempty"`
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr,omitempty"`
}
// decodeTwoCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape
// Size) and twoCellAnchor (Two Cell Anchor Shape Size). This element
// specifies a two cell anchor placeholder for a group, a shape, or a drawing
// element. It moves with cells and its extents are in EMU units.
type decodeTwoCellAnchor struct {
From *decodeFrom `xml:"from"`
To *decodeTo `xml:"to"`
Pic *decodePic `xml:"pic,omitempty"`
ClientData *decodeClientData `xml:"clientData"`
}
// decodeCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
// element specifies non-visual canvas properties. This allows for additional
// information that does not affect the appearance of the picture to be
// stored.
type decodeCNvPr struct {
ID int `xml:"id,attr"`
Name string `xml:"name,attr"`
Descr string `xml:"descr,attr"`
Title string `xml:"title,attr,omitempty"`
}
// decodePicLocks directly maps the picLocks (Picture Locks). This element
// specifies all locking properties for a graphic frame. These properties
// inform the generating application about specific properties that have been
// previously locked and thus should not be changed.
type decodePicLocks struct {
NoAdjustHandles bool `xml:"noAdjustHandles,attr,omitempty"`
NoChangeArrowheads bool `xml:"noChangeArrowheads,attr,omitempty"`
NoChangeAspect bool `xml:"noChangeAspect,attr"`
NoChangeShapeType bool `xml:"noChangeShapeType,attr,omitempty"`
NoCrop bool `xml:"noCrop,attr,omitempty"`
NoEditPoints bool `xml:"noEditPoints,attr,omitempty"`
NoGrp bool `xml:"noGrp,attr,omitempty"`
NoMove bool `xml:"noMove,attr,omitempty"`
NoResize bool `xml:"noResize,attr,omitempty"`
NoRot bool `xml:"noRot,attr,omitempty"`
NoSelect bool `xml:"noSelect,attr,omitempty"`
}
// decodeBlip directly maps the blip element in the namespace
// http://purl.oclc.org/ooxml/officeDoc ument/relationships - This element
// specifies the existence of an image (binary large image or picture) and
// contains a reference to the image data.
type decodeBlip struct {
Embed string `xml:"embed,attr"`
Cstate string `xml:"cstate,attr,omitempty"`
R string `xml:"r,attr"`
}
// decodeStretch directly maps the stretch element. This element specifies
// that a BLIP should be stretched to fill the target rectangle. The other
// option is a tile where a BLIP is tiled to fill the available area.
type decodeStretch struct {
FillRect string `xml:"fillRect"`
}
// decodeOff directly maps the colOff and rowOff element. This element is used
// to specify the column offset within a cell.
type decodeOff struct {
X int `xml:"x,attr"`
Y int `xml:"y,attr"`
}
// decodeExt directly maps the ext element.
type decodeExt struct {
Cx int `xml:"cx,attr"`
Cy int `xml:"cy,attr"`
}
// decodePrstGeom directly maps the prstGeom (Preset geometry). This element
// specifies when a preset geometric shape should be used instead of a custom
// geometric shape. The generating application should be able to render all
// preset geometries enumerated in the ST_ShapeType list.
type decodePrstGeom struct {
Prst string `xml:"prst,attr"`
}
// decodeXfrm directly maps the xfrm (2D Transform for Graphic Frame). This
// element specifies the transform to be applied to the corresponding graphic
// frame. This transformation is applied to the graphic frame just as it would
// be for a shape or group shape.
type decodeXfrm struct {
Off decodeOff `xml:"off"`
Ext decodeExt `xml:"ext"`
}
// decodeCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing
// Properties). This element specifies the non-visual properties for the picture
// canvas. These properties are to be used by the generating application to
// determine how certain properties are to be changed for the picture object in
// question.
type decodeCNvPicPr struct {
PicLocks decodePicLocks `xml:"picLocks"`
}
// directly maps the nvPicPr (Non-Visual Properties for a Picture). This
// element specifies all non-visual properties for a picture. This element is
// a container for the non-visual identification properties, shape properties
// and application properties that are to be associated with a picture. This
// allows for additional information that does not affect the appearance of
// the picture to be stored.
type decodeNvPicPr struct {
CNvPr decodeCNvPr `xml:"cNvPr"`
CNvPicPr decodeCNvPicPr `xml:"cNvPicPr"`
}
// decodeBlipFill directly maps the blipFill (Picture Fill). This element
// specifies the kind of picture fill that the picture object has. Because a
// picture has a picture fill already by default, it is possible to have two
// fills specified for a picture object.
type decodeBlipFill struct {
Blip decodeBlip `xml:"blip"`
Stretch decodeStretch `xml:"stretch"`
}
// decodeSpPr directly maps the spPr (Shape Properties). This element
// specifies the visual shape properties that can be applied to a picture.
// These are the same properties that are allowed to describe the visual
// properties of a shape but are used here to describe the visual appearance
// of a picture within a document.
type decodeSpPr struct {
Xfrm decodeXfrm `xml:"xfrm"`
PrstGeom decodePrstGeom `xml:"prstGeom"`
}
// decodePic elements encompass the definition of pictures within the
// DrawingML framework. While pictures are in many ways very similar to shapes
// they have specific properties that are unique in order to optimize for
// picture- specific scenarios.
type decodePic struct {
NvPicPr decodeNvPicPr `xml:"nvPicPr"`
BlipFill decodeBlipFill `xml:"blipFill"`
SpPr decodeSpPr `xml:"spPr"`
}
// decodeFrom specifies the starting anchor.
type decodeFrom struct {
Col int `xml:"col"`
ColOff int `xml:"colOff"`
Row int `xml:"row"`
RowOff int `xml:"rowOff"`
}
// decodeTo directly specifies the ending anchor.
type decodeTo struct {
Col int `xml:"col"`
ColOff int `xml:"colOff"`
Row int `xml:"row"`
RowOff int `xml:"rowOff"`
}
// decodeClientData directly maps the clientData element. An empty element
// which specifies (via attributes) certain properties related to printing and
// selection of the drawing object. The fLocksWithSheet attribute (either true
// or false) determines whether to disable selection when the sheet is
// protected, and fPrintsWithSheet attribute (either true or false) determines
// whether the object is printed when the sheet is printed.
type decodeClientData struct {
FLocksWithSheet bool `xml:"fLocksWithSheet,attr"`
FPrintsWithSheet bool `xml:"fPrintsWithSheet,attr"`
}

View file

@ -0,0 +1,453 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// Source relationship and namespace.
const (
SourceRelationship = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"
SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
SourceRelationshipChart201506 = "http://schemas.microsoft.com/office/drawing/2015/06/chart"
SourceRelationshipChart20070802 = "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"
SourceRelationshipChart2014 = "http://schemas.microsoft.com/office/drawing/2014/chart"
SourceRelationshipCompatibility = "http://schemas.openxmlformats.org/markup-compatibility/2006"
NameSpaceDrawingML = "http://schemas.openxmlformats.org/drawingml/2006/main"
NameSpaceDrawingMLChart = "http://schemas.openxmlformats.org/drawingml/2006/chart"
NameSpaceDrawingMLSpreadSheet = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
NameSpaceSpreadSheet = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
NameSpaceSpreadSheetX14 = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
NameSpaceSpreadSheetX15 = "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
NameSpaceSpreadSheetExcel2006Main = "http://schemas.microsoft.com/office/excel/2006/main"
NameSpaceMacExcel2008Main = "http://schemas.microsoft.com/office/mac/excel/2008/main"
NameSpaceXML = "http://www.w3.org/XML/1998/namespace"
NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"
StrictSourceRelationship = "http://purl.oclc.org/ooxml/officeDocument/relationships"
StrictSourceRelationshipChart = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
StrictSourceRelationshipComments = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments"
StrictSourceRelationshipImage = "http://purl.oclc.org/ooxml/officeDocument/relationships/image"
StrictNameSpaceSpreadSheet = "http://purl.oclc.org/ooxml/spreadsheetml/main"
NameSpaceDublinCore = "http://purl.org/dc/elements/1.1/"
NameSpaceDublinCoreTerms = "http://purl.org/dc/terms/"
NameSpaceDublinCoreMetadataIntiative = "http://purl.org/dc/dcmitype/"
ContentTypeDrawing = "application/vnd.openxmlformats-officedocument.drawing+xml"
ContentTypeDrawingML = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
ContentTypeMacro = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"
ContentTypeSpreadSheetMLChartsheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"
ContentTypeSpreadSheetMLComments = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
ContentTypeSpreadSheetMLPivotCacheDefinition = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
ContentTypeSpreadSheetMLPivotTable = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"
ContentTypeSpreadSheetMLSharedStrings = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
ContentTypeSpreadSheetMLTable = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
ContentTypeSpreadSheetMLWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
ContentTypeVBA = "application/vnd.ms-office.vbaProject"
ContentTypeVML = "application/vnd.openxmlformats-officedocument.vmlDrawing"
// ExtURIConditionalFormattings is the extLst child element
// ([ISO/IEC29500-1:2016] section 18.2.10) of the worksheet element
// ([ISO/IEC29500-1:2016] section 18.3.1.99) is extended by the addition of
// new child ext elements ([ISO/IEC29500-1:2016] section 18.2.7)
ExtURIConditionalFormattings = "{78C0D931-6437-407D-A8EE-F0AAD7539E65}"
ExtURIDataValidations = "{CCE6A557-97BC-4B89-ADB6-D9C93CAAB3DF}"
ExtURISparklineGroups = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"
ExtURISlicerListX14 = "{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}"
ExtURISlicerCachesListX14 = "{BBE1A952-AA13-448e-AADC-164F8A28A991}"
ExtURISlicerListX15 = "{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}"
ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}"
ExtURIIgnoredErrors = "{01252117-D84E-4E92-8308-4BE1C098FCBB}"
ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}"
ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}"
ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}"
ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}"
)
var supportImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png", ".tif": ".tiff", ".tiff": ".tiff"}
// xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
// element specifies non-visual canvas properties. This allows for additional
// information that does not affect the appearance of the picture to be stored.
type xlsxCNvPr struct {
ID int `xml:"id,attr"`
Name string `xml:"name,attr"`
Descr string `xml:"descr,attr"`
Title string `xml:"title,attr,omitempty"`
HlinkClick *xlsxHlinkClick `xml:"a:hlinkClick"`
}
// xlsxHlinkClick (Click Hyperlink) Specifies the on-click hyperlink
// information to be applied to a run of text. When the hyperlink text is
// clicked the link is fetched.
type xlsxHlinkClick struct {
R string `xml:"xmlns:r,attr,omitempty"`
RID string `xml:"r:id,attr,omitempty"`
InvalidURL string `xml:"invalidUrl,attr,omitempty"`
Action string `xml:"action,attr,omitempty"`
TgtFrame string `xml:"tgtFrame,attr,omitempty"`
Tooltip string `xml:"tooltip,attr,omitempty"`
History bool `xml:"history,attr,omitempty"`
HighlightClick bool `xml:"highlightClick,attr,omitempty"`
EndSnd bool `xml:"endSnd,attr,omitempty"`
}
// xlsxPicLocks directly maps the picLocks (Picture Locks). This element
// specifies all locking properties for a graphic frame. These properties inform
// the generating application about specific properties that have been
// previously locked and thus should not be changed.
type xlsxPicLocks struct {
NoAdjustHandles bool `xml:"noAdjustHandles,attr,omitempty"`
NoChangeArrowheads bool `xml:"noChangeArrowheads,attr,omitempty"`
NoChangeAspect bool `xml:"noChangeAspect,attr"`
NoChangeShapeType bool `xml:"noChangeShapeType,attr,omitempty"`
NoCrop bool `xml:"noCrop,attr,omitempty"`
NoEditPoints bool `xml:"noEditPoints,attr,omitempty"`
NoGrp bool `xml:"noGrp,attr,omitempty"`
NoMove bool `xml:"noMove,attr,omitempty"`
NoResize bool `xml:"noResize,attr,omitempty"`
NoRot bool `xml:"noRot,attr,omitempty"`
NoSelect bool `xml:"noSelect,attr,omitempty"`
}
// xlsxBlip directly maps the blip element in the namespace
// http://purl.oclc.org/ooxml/officeDoc ument/relationships - This element
// specifies the existence of an image (binary large image or picture) and
// contains a reference to the image data.
type xlsxBlip struct {
Embed string `xml:"r:embed,attr"`
Cstate string `xml:"cstate,attr,omitempty"`
R string `xml:"xmlns:r,attr"`
}
// xlsxStretch directly maps the stretch element. This element specifies that a
// BLIP should be stretched to fill the target rectangle. The other option is a
// tile where a BLIP is tiled to fill the available area.
type xlsxStretch struct {
FillRect string `xml:"a:fillRect"`
}
// xlsxOff directly maps the colOff and rowOff element. This element is used to
// specify the column offset within a cell.
type xlsxOff struct {
X int `xml:"x,attr"`
Y int `xml:"y,attr"`
}
// xlsxExt directly maps the ext element.
type xlsxExt struct {
Cx int `xml:"cx,attr"`
Cy int `xml:"cy,attr"`
}
// xlsxPrstGeom directly maps the prstGeom (Preset geometry). This element
// specifies when a preset geometric shape should be used instead of a custom
// geometric shape. The generating application should be able to render all
// preset geometries enumerated in the ST_ShapeType list.
type xlsxPrstGeom struct {
Prst string `xml:"prst,attr"`
}
// xlsxXfrm directly maps the xfrm (2D Transform for Graphic Frame). This
// element specifies the transform to be applied to the corresponding graphic
// frame. This transformation is applied to the graphic frame just as it would
// be for a shape or group shape.
type xlsxXfrm struct {
Off xlsxOff `xml:"a:off"`
Ext xlsxExt `xml:"a:ext"`
}
// xlsxCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing
// Properties). This element specifies the non-visual properties for the picture
// canvas. These properties are to be used by the generating application to
// determine how certain properties are to be changed for the picture object in
// question.
type xlsxCNvPicPr struct {
PicLocks xlsxPicLocks `xml:"a:picLocks"`
}
// directly maps the nvPicPr (Non-Visual Properties for a Picture). This element
// specifies all non-visual properties for a picture. This element is a
// container for the non-visual identification properties, shape properties and
// application properties that are to be associated with a picture. This allows
// for additional information that does not affect the appearance of the picture
// to be stored.
type xlsxNvPicPr struct {
CNvPr xlsxCNvPr `xml:"xdr:cNvPr"`
CNvPicPr xlsxCNvPicPr `xml:"xdr:cNvPicPr"`
}
// xlsxBlipFill directly maps the blipFill (Picture Fill). This element
// specifies the kind of picture fill that the picture object has. Because a
// picture has a picture fill already by default, it is possible to have two
// fills specified for a picture object.
type xlsxBlipFill struct {
Blip xlsxBlip `xml:"a:blip"`
Stretch xlsxStretch `xml:"a:stretch"`
}
// xlsxSpPr directly maps the spPr (Shape Properties). This element specifies
// the visual shape properties that can be applied to a picture. These are the
// same properties that are allowed to describe the visual properties of a shape
// but are used here to describe the visual appearance of a picture within a
// document.
type xlsxSpPr struct {
Xfrm xlsxXfrm `xml:"a:xfrm"`
PrstGeom xlsxPrstGeom `xml:"a:prstGeom"`
}
// xlsxPic elements encompass the definition of pictures within the DrawingML
// framework. While pictures are in many ways very similar to shapes they have
// specific properties that are unique in order to optimize for picture-
// specific scenarios.
type xlsxPic struct {
NvPicPr xlsxNvPicPr `xml:"xdr:nvPicPr"`
BlipFill xlsxBlipFill `xml:"xdr:blipFill"`
SpPr xlsxSpPr `xml:"xdr:spPr"`
}
// xlsxFrom specifies the starting anchor.
type xlsxFrom struct {
Col int `xml:"xdr:col"`
ColOff int `xml:"xdr:colOff"`
Row int `xml:"xdr:row"`
RowOff int `xml:"xdr:rowOff"`
}
// xlsxTo directly specifies the ending anchor.
type xlsxTo struct {
Col int `xml:"xdr:col"`
ColOff int `xml:"xdr:colOff"`
Row int `xml:"xdr:row"`
RowOff int `xml:"xdr:rowOff"`
}
// xdrClientData directly maps the clientData element. An empty element which
// specifies (via attributes) certain properties related to printing and
// selection of the drawing object. The fLocksWithSheet attribute (either true
// or false) determines whether to disable selection when the sheet is
// protected, and fPrintsWithSheet attribute (either true or false) determines
// whether the object is printed when the sheet is printed.
type xdrClientData struct {
FLocksWithSheet bool `xml:"fLocksWithSheet,attr"`
FPrintsWithSheet bool `xml:"fPrintsWithSheet,attr"`
}
// xdrCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape Size)
// and twoCellAnchor (Two Cell Anchor Shape Size). This element specifies a two
// cell anchor placeholder for a group, a shape, or a drawing element. It moves
// with cells and its extents are in EMU units.
type xdrCellAnchor struct {
EditAs string `xml:"editAs,attr,omitempty"`
Pos *xlsxPoint2D `xml:"xdr:pos"`
From *xlsxFrom `xml:"xdr:from"`
To *xlsxTo `xml:"xdr:to"`
Ext *xlsxExt `xml:"xdr:ext"`
Sp *xdrSp `xml:"xdr:sp"`
Pic *xlsxPic `xml:"xdr:pic,omitempty"`
GraphicFrame string `xml:",innerxml"`
ClientData *xdrClientData `xml:"xdr:clientData"`
}
// xlsxPoint2D describes the position of a drawing element within a spreadsheet.
type xlsxPoint2D struct {
XMLName xml.Name `xml:"xdr:pos"`
X int `xml:"x,attr"`
Y int `xml:"y,attr"`
}
// xlsxWsDr directly maps the root element for a part of this content type shall
// wsDr.
type xlsxWsDr struct {
XMLName xml.Name `xml:"xdr:wsDr"`
AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"`
OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
A string `xml:"xmlns:a,attr,omitempty"`
Xdr string `xml:"xmlns:xdr,attr,omitempty"`
R string `xml:"xmlns:r,attr,omitempty"`
}
// xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.
// This element specifies the existence of a graphics frame. This frame contains
// a graphic that was generated by an external source and needs a container in
// which to be displayed on the slide surface.
type xlsxGraphicFrame struct {
XMLName xml.Name `xml:"xdr:graphicFrame"`
Macro string `xml:"macro,attr"`
NvGraphicFramePr xlsxNvGraphicFramePr `xml:"xdr:nvGraphicFramePr"`
Xfrm xlsxXfrm `xml:"xdr:xfrm"`
Graphic *xlsxGraphic `xml:"a:graphic"`
}
// xlsxNvGraphicFramePr (Non-Visual Properties for a Graphic Frame) directly
// maps the xdr:nvGraphicFramePr element. This element specifies all non-visual
// properties for a graphic frame. This element is a container for the non-
// visual identification properties, shape properties and application properties
// that are to be associated with a graphic frame. This allows for additional
// information that does not affect the appearance of the graphic frame to be
// stored.
type xlsxNvGraphicFramePr struct {
CNvPr *xlsxCNvPr `xml:"xdr:cNvPr"`
ChicNvGraphicFramePr string `xml:"xdr:cNvGraphicFramePr"`
}
// xlsxGraphic (Graphic Object) directly maps the a:graphic element. This
// element specifies the existence of a single graphic object. Document authors
// should refer to this element when they wish to persist a graphical object of
// some kind. The specification for this graphical object is provided entirely
// by the document author and referenced within the graphicData child element.
type xlsxGraphic struct {
GraphicData *xlsxGraphicData `xml:"a:graphicData"`
}
// xlsxGraphicData (Graphic Object Data) directly maps the a:graphicData
// element. This element specifies the reference to a graphic object within the
// document. This graphic object is provided entirely by the document authors
// who choose to persist this data within the document.
type xlsxGraphicData struct {
URI string `xml:"uri,attr"`
Chart *xlsxChart `xml:"c:chart,omitempty"`
}
// xlsxChart (Chart) directly maps the c:chart element.
type xlsxChart struct {
C string `xml:"xmlns:c,attr"`
RID string `xml:"r:id,attr"`
R string `xml:"xmlns:r,attr"`
}
// xdrSp (Shape) directly maps the xdr:sp element. This element specifies the
// existence of a single shape. A shape can either be a preset or a custom
// geometry, defined using the SpreadsheetDrawingML framework. In addition to a
// geometry each shape can have both visual and non-visual properties attached.
// Text and corresponding styling information can also be attached to a shape.
// This shape is specified along with all other shapes within either the shape
// tree or group shape elements.
type xdrSp struct {
Macro string `xml:"macro,attr"`
Textlink string `xml:"textlink,attr"`
NvSpPr *xdrNvSpPr `xml:"xdr:nvSpPr"`
SpPr *xlsxSpPr `xml:"xdr:spPr"`
Style *xdrStyle `xml:"xdr:style"`
TxBody *xdrTxBody `xml:"xdr:txBody"`
}
// xdrNvSpPr (Non-Visual Properties for a Shape) directly maps the xdr:nvSpPr
// element. This element specifies all non-visual properties for a shape. This
// element is a container for the non-visual identification properties, shape
// properties and application properties that are to be associated with a shape.
// This allows for additional information that does not affect the appearance of
// the shape to be stored.
type xdrNvSpPr struct {
CNvPr *xlsxCNvPr `xml:"xdr:cNvPr"`
CNvSpPr *xdrCNvSpPr `xml:"xdr:cNvSpPr"`
}
// xdrCNvSpPr (Connection Non-Visual Shape Properties) directly maps the
// xdr:cNvSpPr element. This element specifies the set of non-visual properties
// for a connection shape. These properties specify all data about the
// connection shape which do not affect its display within a spreadsheet.
type xdrCNvSpPr struct {
TxBox bool `xml:"txBox,attr"`
}
// xdrStyle (Shape Style) directly maps the xdr:style element. The element
// specifies the style that is applied to a shape and the corresponding
// references for each of the style components such as lines and fills.
type xdrStyle struct {
LnRef *aRef `xml:"a:lnRef"`
FillRef *aRef `xml:"a:fillRef"`
EffectRef *aRef `xml:"a:effectRef"`
FontRef *aFontRef `xml:"a:fontRef"`
}
// aRef directly maps the a:lnRef, a:fillRef and a:effectRef element.
type aRef struct {
Idx int `xml:"idx,attr"`
ScrgbClr *aScrgbClr `xml:"a:scrgbClr"`
SchemeClr *attrValString `xml:"a:schemeClr"`
SrgbClr *attrValString `xml:"a:srgbClr"`
}
// aScrgbClr (RGB Color Model - Percentage Variant) directly maps the a:scrgbClr
// element. This element specifies a color using the red, green, blue RGB color
// model. Each component, red, green, and blue is expressed as a percentage from
// 0% to 100%. A linear gamma of 1.0 is assumed.
type aScrgbClr struct {
R float64 `xml:"r,attr"`
G float64 `xml:"g,attr"`
B float64 `xml:"b,attr"`
}
// aFontRef (Font Reference) directly maps the a:fontRef element. This element
// represents a reference to a themed font. When used it specifies which themed
// font to use along with a choice of color.
type aFontRef struct {
Idx string `xml:"idx,attr"`
SchemeClr *attrValString `xml:"a:schemeClr"`
}
// xdrTxBody (Shape Text Body) directly maps the xdr:txBody element. This
// element specifies the existence of text to be contained within the
// corresponding shape. All visible text and visible text related properties are
// contained within this element. There can be multiple paragraphs and within
// paragraphs multiple runs of text.
type xdrTxBody struct {
BodyPr *aBodyPr `xml:"a:bodyPr"`
P []*aP `xml:"a:p"`
}
// formatPicture directly maps the format settings of the picture.
type formatPicture struct {
FPrintsWithSheet bool `json:"print_obj"`
FLocksWithSheet bool `json:"locked"`
NoChangeAspect bool `json:"lock_aspect_ratio"`
OffsetX int `json:"x_offset"`
OffsetY int `json:"y_offset"`
XScale float64 `json:"x_scale"`
YScale float64 `json:"y_scale"`
Hyperlink string `json:"hyperlink"`
HyperlinkType string `json:"hyperlink_type"`
Positioning string `json:"positioning"`
}
// formatShape directly maps the format settings of the shape.
type formatShape struct {
Type string `json:"type"`
Width int `json:"width"`
Height int `json:"height"`
Format formatPicture `json:"format"`
Color formatShapeColor `json:"color"`
Paragraph []formatShapeParagraph `json:"paragraph"`
}
// formatShapeParagraph directly maps the format settings of the paragraph in
// the shape.
type formatShapeParagraph struct {
Font Font `json:"font"`
Text string `json:"text"`
}
// formatShapeColor directly maps the color settings of the shape.
type formatShapeColor struct {
Line string `json:"line"`
Fill string `json:"fill"`
Effect string `json:"effect"`
}

View file

@ -0,0 +1,218 @@
package excelize
import "encoding/xml"
// xlsxPivotCacheDefinition represents the pivotCacheDefinition part. This part
// defines each field in the source data, including the name, the string
// resources of the instance data (for shared items), and information about
// the type of data that appears in the field.
type xlsxPivotCacheDefinition struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotCacheDefinition"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
Invalid bool `xml:"invalid,attr,omitempty"`
SaveData bool `xml:"saveData,attr"`
RefreshOnLoad bool `xml:"refreshOnLoad,attr,omitempty"`
OptimizeMemory bool `xml:"optimizeMemory,attr,omitempty"`
EnableRefresh bool `xml:"enableRefresh,attr,omitempty"`
RefreshedBy string `xml:"refreshedBy,attr,omitempty"`
RefreshedDate float64 `xml:"refreshedDate,attr,omitempty"`
RefreshedDateIso float64 `xml:"refreshedDateIso,attr,omitempty"`
BackgroundQuery bool `xml:"backgroundQuery,attr"`
MissingItemsLimit int `xml:"missingItemsLimit,attr,omitempty"`
CreatedVersion int `xml:"createdVersion,attr,omitempty"`
RefreshedVersion int `xml:"refreshedVersion,attr,omitempty"`
MinRefreshableVersion int `xml:"minRefreshableVersion,attr,omitempty"`
RecordCount int `xml:"recordCount,attr,omitempty"`
UpgradeOnRefresh bool `xml:"upgradeOnRefresh,attr,omitempty"`
TupleCacheAttr bool `xml:"tupleCache,attr,omitempty"`
SupportSubquery bool `xml:"supportSubquery,attr,omitempty"`
SupportAdvancedDrill bool `xml:"supportAdvancedDrill,attr,omitempty"`
CacheSource *xlsxCacheSource `xml:"cacheSource"`
CacheFields *xlsxCacheFields `xml:"cacheFields"`
CacheHierarchies *xlsxCacheHierarchies `xml:"cacheHierarchies"`
Kpis *xlsxKpis `xml:"kpis"`
TupleCache *xlsxTupleCache `xml:"tupleCache"`
CalculatedItems *xlsxCalculatedItems `xml:"calculatedItems"`
CalculatedMembers *xlsxCalculatedMembers `xml:"calculatedMembers"`
Dimensions *xlsxDimensions `xml:"dimensions"`
MeasureGroups *xlsxMeasureGroups `xml:"measureGroups"`
Maps *xlsxMaps `xml:"maps"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxCacheSource represents the description of data source whose data is
// stored in the pivot cache. The data source refers to the underlying rows or
// database records that provide the data for a PivotTable. You can create a
// PivotTable report from a SpreadsheetML table, an external database
// (including OLAP cubes), multiple SpreadsheetML worksheets, or another
// PivotTable.
type xlsxCacheSource struct {
Type string `xml:"type,attr"`
ConnectionID int `xml:"connectionId,attr,omitempty"`
WorksheetSource *xlsxWorksheetSource `xml:"worksheetSource"`
Consolidation *xlsxConsolidation `xml:"consolidation"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxWorksheetSource represents the location of the source of the data that
// is stored in the cache.
type xlsxWorksheetSource struct {
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
Ref string `xml:"ref,attr,omitempty"`
Name string `xml:"name,attr,omitempty"`
Sheet string `xml:"sheet,attr,omitempty"`
}
// xlsxConsolidation represents the description of the PivotCache source using
// multiple consolidation ranges. This element is used when the source of the
// PivotTable is a collection of ranges in the workbook. The ranges are
// specified in the rangeSets collection. The logic for how the application
// consolidates the data in the ranges is application- defined.
type xlsxConsolidation struct {
}
// xlsxCacheFields represents the collection of field definitions in the
// source data.
type xlsxCacheFields struct {
Count int `xml:"count,attr"`
CacheField []*xlsxCacheField `xml:"cacheField"`
}
// xlsxCacheField represent a single field in the PivotCache. This definition
// contains information about the field, such as its source, data type, and
// location within a level or hierarchy. The sharedItems element stores
// additional information about the data in this field. If there are no shared
// items, then values are stored directly in the pivotCacheRecords part.
type xlsxCacheField struct {
Name string `xml:"name,attr"`
Caption string `xml:"caption,attr,omitempty"`
PropertyName string `xml:"propertyName,attr,omitempty"`
ServerField bool `xml:"serverField,attr,omitempty"`
UniqueList bool `xml:"uniqueList,attr,omitempty"`
NumFmtID int `xml:"numFmtId,attr"`
Formula string `xml:"formula,attr,omitempty"`
SQLType int `xml:"sqlType,attr,omitempty"`
Hierarchy int `xml:"hierarchy,attr,omitempty"`
Level int `xml:"level,attr,omitempty"`
DatabaseField bool `xml:"databaseField,attr,omitempty"`
MappingCount int `xml:"mappingCount,attr,omitempty"`
MemberPropertyField bool `xml:"memberPropertyField,attr,omitempty"`
SharedItems *xlsxSharedItems `xml:"sharedItems"`
FieldGroup *xlsxFieldGroup `xml:"fieldGroup"`
MpMap *xlsxX `xml:"mpMap"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxSharedItems represents the collection of unique items for a field in
// the PivotCacheDefinition. The sharedItems complex type stores data type and
// formatting information about the data in a field. Items in the
// PivotCacheDefinition can be shared in order to reduce the redundancy of
// those values that are referenced in multiple places across all the
// PivotTable parts.
type xlsxSharedItems struct {
ContainsSemiMixedTypes bool `xml:"containsSemiMixedTypes,attr,omitempty"`
ContainsNonDate bool `xml:"containsNonDate,attr,omitempty"`
ContainsDate bool `xml:"containsDate,attr,omitempty"`
ContainsString bool `xml:"containsString,attr,omitempty"`
ContainsBlank bool `xml:"containsBlank,attr,omitempty"`
ContainsMixedTypes bool `xml:"containsMixedTypes,attr,omitempty"`
ContainsNumber bool `xml:"containsNumber,attr,omitempty"`
ContainsInteger bool `xml:"containsInteger,attr,omitempty"`
MinValue float64 `xml:"minValue,attr,omitempty"`
MaxValue float64 `xml:"maxValue,attr,omitempty"`
MinDate string `xml:"minDate,attr,omitempty"`
MaxDate string `xml:"maxDate,attr,omitempty"`
Count int `xml:"count,attr"`
LongText bool `xml:"longText,attr,omitempty"`
M *xlsxMissing `xml:"m"`
N *xlsxNumber `xml:"n"`
B *xlsxBoolean `xml:"b"`
E *xlsxError `xml:"e"`
S *xlsxString `xml:"s"`
D *xlsxDateTime `xml:"d"`
}
// xlsxMissing represents a value that was not specified.
type xlsxMissing struct {
}
// xlsxNumber represents a numeric value in the PivotTable.
type xlsxNumber struct {
V float64 `xml:"v,attr"`
U bool `xml:"u,attr,omitempty"`
F bool `xml:"f,attr,omitempty"`
C string `xml:"c,attr,omitempty"`
Cp int `xml:"cp,attr,omitempty"`
In int `xml:"in,attr,omitempty"`
Bc string `xml:"bc,attr,omitempty"`
Fc string `xml:"fc,attr,omitempty"`
I bool `xml:"i,attr,omitempty"`
Un bool `xml:"un,attr,omitempty"`
St bool `xml:"st,attr,omitempty"`
B bool `xml:"b,attr,omitempty"`
Tpls *xlsxTuples `xml:"tpls"`
X *attrValInt `xml:"x"`
}
// xlsxTuples represents members for the OLAP sheet data entry, also known as
// a tuple.
type xlsxTuples struct {
}
// xlsxBoolean represents a boolean value for an item in the PivotTable.
type xlsxBoolean struct {
}
// xlsxError represents an error value. The use of this item indicates that an
// error value is present in the PivotTable source. The error is recorded in
// the value attribute.
type xlsxError struct {
}
// xlsxString represents a character value in a PivotTable.
type xlsxString struct {
}
// xlsxDateTime represents a date-time value in the PivotTable.
type xlsxDateTime struct {
}
// xlsxFieldGroup represents the collection of properties for a field group.
type xlsxFieldGroup struct {
}
// xlsxCacheHierarchies represents the collection of OLAP hierarchies in the
// PivotCache.
type xlsxCacheHierarchies struct {
}
// xlsxKpis represents the collection of Key Performance Indicators (KPIs)
// defined on the OLAP server and stored in the PivotCache.
type xlsxKpis struct {
}
// xlsxTupleCache represents the cache of OLAP sheet data members, or tuples.
type xlsxTupleCache struct {
}
// xlsxCalculatedItems represents the collection of calculated items.
type xlsxCalculatedItems struct {
}
// xlsxCalculatedMembers represents the collection of calculated members in an
// OLAP PivotTable.
type xlsxCalculatedMembers struct {
}
// xlsxDimensions represents the collection of PivotTable OLAP dimensions.
type xlsxDimensions struct {
}
// xlsxMeasureGroups represents the collection of PivotTable OLAP measure
// groups.
type xlsxMeasureGroups struct {
}
// xlsxMaps represents the PivotTable OLAP measure group - Dimension maps.
type xlsxMaps struct {
}

View file

@ -0,0 +1,294 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxPivotTableDefinition represents the PivotTable root element for
// non-null PivotTables. There exists one pivotTableDefinition for each
// PivotTableDefinition part
type xlsxPivotTableDefinition struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotTableDefinition"`
Name string `xml:"name,attr"`
CacheID int `xml:"cacheId,attr"`
ApplyNumberFormats bool `xml:"applyNumberFormats,attr,omitempty"`
ApplyBorderFormats bool `xml:"applyBorderFormats,attr,omitempty"`
ApplyFontFormats bool `xml:"applyFontFormats,attr,omitempty"`
ApplyPatternFormats bool `xml:"applyPatternFormats,attr,omitempty"`
ApplyAlignmentFormats bool `xml:"applyAlignmentFormats,attr,omitempty"`
ApplyWidthHeightFormats bool `xml:"applyWidthHeightFormats,attr,omitempty"`
DataOnRows bool `xml:"dataOnRows,attr,omitempty"`
DataPosition int `xml:"dataPosition,attr,omitempty"`
DataCaption string `xml:"dataCaption,attr"`
GrandTotalCaption string `xml:"grandTotalCaption,attr,omitempty"`
ErrorCaption string `xml:"errorCaption,attr,omitempty"`
ShowError bool `xml:"showError,attr,omitempty"`
MissingCaption string `xml:"missingCaption,attr,omitempty"`
ShowMissing bool `xml:"showMissing,attr,omitempty"`
PageStyle string `xml:"pageStyle,attr,omitempty"`
PivotTableStyle string `xml:"pivotTableStyle,attr,omitempty"`
VacatedStyle string `xml:"vacatedStyle,attr,omitempty"`
Tag string `xml:"tag,attr,omitempty"`
UpdatedVersion int `xml:"updatedVersion,attr,omitempty"`
MinRefreshableVersion int `xml:"minRefreshableVersion,attr,omitempty"`
AsteriskTotals bool `xml:"asteriskTotals,attr,omitempty"`
ShowItems bool `xml:"showItems,attr,omitempty"`
EditData bool `xml:"editData,attr,omitempty"`
DisableFieldList bool `xml:"disableFieldList,attr,omitempty"`
ShowCalcMbrs bool `xml:"showCalcMbrs,attr,omitempty"`
VisualTotals bool `xml:"visualTotals,attr,omitempty"`
ShowMultipleLabel bool `xml:"showMultipleLabel,attr,omitempty"`
ShowDataDropDown bool `xml:"showDataDropDown,attr,omitempty"`
ShowDrill bool `xml:"showDrill,attr,omitempty"`
PrintDrill bool `xml:"printDrill,attr,omitempty"`
ShowMemberPropertyTips bool `xml:"showMemberPropertyTips,attr,omitempty"`
ShowDataTips bool `xml:"showDataTips,attr,omitempty"`
EnableWizard bool `xml:"enableWizard,attr,omitempty"`
EnableDrill bool `xml:"enableDrill,attr,omitempty"`
EnableFieldProperties bool `xml:"enableFieldProperties,attr,omitempty"`
PreserveFormatting bool `xml:"preserveFormatting,attr,omitempty"`
UseAutoFormatting bool `xml:"useAutoFormatting,attr,omitempty"`
PageWrap int `xml:"pageWrap,attr,omitempty"`
PageOverThenDown bool `xml:"pageOverThenDown,attr,omitempty"`
SubtotalHiddenItems bool `xml:"subtotalHiddenItems,attr,omitempty"`
RowGrandTotals bool `xml:"rowGrandTotals,attr,omitempty"`
ColGrandTotals bool `xml:"colGrandTotals,attr,omitempty"`
FieldPrintTitles bool `xml:"fieldPrintTitles,attr,omitempty"`
ItemPrintTitles bool `xml:"itemPrintTitles,attr,omitempty"`
MergeItem bool `xml:"mergeItem,attr,omitempty"`
ShowDropZones bool `xml:"showDropZones,attr,omitempty"`
CreatedVersion int `xml:"createdVersion,attr,omitempty"`
Indent int `xml:"indent,attr,omitempty"`
ShowEmptyRow bool `xml:"showEmptyRow,attr,omitempty"`
ShowEmptyCol bool `xml:"showEmptyCol,attr,omitempty"`
ShowHeaders bool `xml:"showHeaders,attr,omitempty"`
Compact bool `xml:"compact,attr"`
Outline bool `xml:"outline,attr"`
OutlineData bool `xml:"outlineData,attr,omitempty"`
CompactData bool `xml:"compactData,attr,omitempty"`
Published bool `xml:"published,attr,omitempty"`
GridDropZones bool `xml:"gridDropZones,attr,omitempty"`
Immersive bool `xml:"immersive,attr,omitempty"`
MultipleFieldFilters bool `xml:"multipleFieldFilters,attr,omitempty"`
ChartFormat int `xml:"chartFormat,attr,omitempty"`
RowHeaderCaption string `xml:"rowHeaderCaption,attr,omitempty"`
ColHeaderCaption string `xml:"colHeaderCaption,attr,omitempty"`
FieldListSortAscending bool `xml:"fieldListSortAscending,attr,omitempty"`
MdxSubqueries bool `xml:"mdxSubqueries,attr,omitempty"`
CustomListSort bool `xml:"customListSort,attr,omitempty"`
Location *xlsxLocation `xml:"location"`
PivotFields *xlsxPivotFields `xml:"pivotFields"`
RowFields *xlsxRowFields `xml:"rowFields"`
RowItems *xlsxRowItems `xml:"rowItems"`
ColFields *xlsxColFields `xml:"colFields"`
ColItems *xlsxColItems `xml:"colItems"`
PageFields *xlsxPageFields `xml:"pageFields"`
DataFields *xlsxDataFields `xml:"dataFields"`
ConditionalFormats *xlsxConditionalFormats `xml:"conditionalFormats"`
PivotTableStyleInfo *xlsxPivotTableStyleInfo `xml:"pivotTableStyleInfo"`
}
// xlsxLocation represents location information for the PivotTable.
type xlsxLocation struct {
Ref string `xml:"ref,attr"`
FirstHeaderRow int `xml:"firstHeaderRow,attr"`
FirstDataRow int `xml:"firstDataRow,attr"`
FirstDataCol int `xml:"firstDataCol,attr"`
RowPageCount int `xml:"rowPageCount,attr,omitempty"`
ColPageCount int `xml:"colPageCount,attr,omitempty"`
}
// xlsxPivotFields represents the collection of fields that appear on the
// PivotTable.
type xlsxPivotFields struct {
Count int `xml:"count,attr"`
PivotField []*xlsxPivotField `xml:"pivotField"`
}
// xlsxPivotField represents a single field in the PivotTable. This element
// contains information about the field, including the collection of items in
// the field.
type xlsxPivotField struct {
Name string `xml:"name,attr,omitempty"`
Axis string `xml:"axis,attr,omitempty"`
DataField bool `xml:"dataField,attr,omitempty"`
SubtotalCaption string `xml:"subtotalCaption,attr,omitempty"`
ShowDropDowns bool `xml:"showDropDowns,attr,omitempty"`
HiddenLevel bool `xml:"hiddenLevel,attr,omitempty"`
UniqueMemberProperty string `xml:"uniqueMemberProperty,attr,omitempty"`
Compact bool `xml:"compact,attr"`
AllDrilled bool `xml:"allDrilled,attr,omitempty"`
NumFmtID string `xml:"numFmtId,attr,omitempty"`
Outline bool `xml:"outline,attr"`
SubtotalTop bool `xml:"subtotalTop,attr,omitempty"`
DragToRow bool `xml:"dragToRow,attr,omitempty"`
DragToCol bool `xml:"dragToCol,attr,omitempty"`
MultipleItemSelectionAllowed bool `xml:"multipleItemSelectionAllowed,attr,omitempty"`
DragToPage bool `xml:"dragToPage,attr,omitempty"`
DragToData bool `xml:"dragToData,attr,omitempty"`
DragOff bool `xml:"dragOff,attr,omitempty"`
ShowAll bool `xml:"showAll,attr"`
InsertBlankRow bool `xml:"insertBlankRow,attr,omitempty"`
ServerField bool `xml:"serverField,attr,omitempty"`
InsertPageBreak bool `xml:"insertPageBreak,attr,omitempty"`
AutoShow bool `xml:"autoShow,attr,omitempty"`
TopAutoShow bool `xml:"topAutoShow,attr,omitempty"`
HideNewItems bool `xml:"hideNewItems,attr,omitempty"`
MeasureFilter bool `xml:"measureFilter,attr,omitempty"`
IncludeNewItemsInFilter bool `xml:"includeNewItemsInFilter,attr,omitempty"`
ItemPageCount int `xml:"itemPageCount,attr,omitempty"`
SortType string `xml:"sortType,attr,omitempty"`
DataSourceSort bool `xml:"dataSourceSort,attr,omitempty"`
NonAutoSortDefault bool `xml:"nonAutoSortDefault,attr,omitempty"`
RankBy int `xml:"rankBy,attr,omitempty"`
DefaultSubtotal bool `xml:"defaultSubtotal,attr,omitempty"`
SumSubtotal bool `xml:"sumSubtotal,attr,omitempty"`
CountASubtotal bool `xml:"countASubtotal,attr,omitempty"`
AvgSubtotal bool `xml:"avgSubtotal,attr,omitempty"`
MaxSubtotal bool `xml:"maxSubtotal,attr,omitempty"`
MinSubtotal bool `xml:"minSubtotal,attr,omitempty"`
ProductSubtotal bool `xml:"productSubtotal,attr,omitempty"`
CountSubtotal bool `xml:"countSubtotal,attr,omitempty"`
StdDevSubtotal bool `xml:"stdDevSubtotal,attr,omitempty"`
StdDevPSubtotal bool `xml:"stdDevPSubtotal,attr,omitempty"`
VarSubtotal bool `xml:"varSubtotal,attr,omitempty"`
VarPSubtotal bool `xml:"varPSubtotal,attr,omitempty"`
ShowPropCell bool `xml:"showPropCell,attr,omitempty"`
ShowPropTip bool `xml:"showPropTip,attr,omitempty"`
ShowPropAsCaption bool `xml:"showPropAsCaption,attr,omitempty"`
DefaultAttributeDrillState bool `xml:"defaultAttributeDrillState,attr,omitempty"`
Items *xlsxItems `xml:"items"`
AutoSortScope *xlsxAutoSortScope `xml:"autoSortScope"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxItems represents the collection of items in a PivotTable field. The
// items in the collection are ordered by index. Items represent the unique
// entries from the field in the source data.
type xlsxItems struct {
Count int `xml:"count,attr"`
Item []*xlsxItem `xml:"item"`
}
// xlsxItem represents a single item in PivotTable field.
type xlsxItem struct {
N string `xml:"n,attr,omitempty"`
T string `xml:"t,attr,omitempty"`
H bool `xml:"h,attr,omitempty"`
S bool `xml:"s,attr,omitempty"`
SD bool `xml:"sd,attr,omitempty"`
F bool `xml:"f,attr,omitempty"`
M bool `xml:"m,attr,omitempty"`
C bool `xml:"c,attr,omitempty"`
X int `xml:"x,attr,omitempty"`
D bool `xml:"d,attr,omitempty"`
E bool `xml:"e,attr,omitempty"`
}
// xlsxAutoSortScope represents the sorting scope for the PivotTable.
type xlsxAutoSortScope struct {
}
// xlsxRowFields represents the collection of row fields for the PivotTable.
type xlsxRowFields struct {
Count int `xml:"count,attr"`
Field []*xlsxField `xml:"field"`
}
// xlsxField represents a generic field that can appear either on the column
// or the row region of the PivotTable. There areas many <x> elements as there
// are item values in any particular column or row.
type xlsxField struct {
X int `xml:"x,attr"`
}
// xlsxRowItems represents the collection of items in row axis of the
// PivotTable.
type xlsxRowItems struct {
Count int `xml:"count,attr"`
I []*xlsxI `xml:"i"`
}
// xlsxI represents the collection of items in the row region of the
// PivotTable.
type xlsxI struct {
X []*xlsxX `xml:"x"`
}
// xlsxX represents an array of indexes to cached shared item values.
type xlsxX struct {
}
// xlsxColFields represents the collection of fields that are on the column
// axis of the PivotTable.
type xlsxColFields struct {
Count int `xml:"count,attr"`
Field []*xlsxField `xml:"field"`
}
// xlsxColItems represents the collection of column items of the PivotTable.
type xlsxColItems struct {
Count int `xml:"count,attr"`
I []*xlsxI `xml:"i"`
}
// xlsxPageFields represents the collection of items in the page or report
// filter region of the PivotTable.
type xlsxPageFields struct {
Count int `xml:"count,attr"`
PageField []*xlsxPageField `xml:"pageField"`
}
// xlsxPageField represents a field on the page or report filter of the
// PivotTable.
type xlsxPageField struct {
Fld int `xml:"fld,attr"`
Item int `xml:"item,attr,omitempty"`
Hier int `xml:"hier,attr,omitempty"`
Name string `xml:"name,attr,omitempty"`
Cap string `xml:"cap,attr,omitempty"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxDataFields represents the collection of items in the data region of the
// PivotTable.
type xlsxDataFields struct {
Count int `xml:"count,attr"`
DataField []*xlsxDataField `xml:"dataField"`
}
// xlsxDataField represents a field from a source list, table, or database
// that contains data that is summarized in a PivotTable.
type xlsxDataField struct {
Name string `xml:"name,attr,omitempty"`
Fld int `xml:"fld,attr"`
Subtotal string `xml:"subtotal,attr,omitempty"`
ShowDataAs string `xml:"showDataAs,attr,omitempty"`
BaseField int `xml:"baseField,attr,omitempty"`
BaseItem int64 `xml:"baseItem,attr,omitempty"`
NumFmtID string `xml:"numFmtId,attr,omitempty"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxConditionalFormats represents the collection of conditional formats
// applied to a PivotTable.
type xlsxConditionalFormats struct {
}
// xlsxPivotTableStyleInfo represent information on style applied to the
// PivotTable.
type xlsxPivotTableStyleInfo struct {
Name string `xml:"name,attr"`
ShowRowHeaders bool `xml:"showRowHeaders,attr"`
ShowColHeaders bool `xml:"showColHeaders,attr"`
ShowRowStripes bool `xml:"showRowStripes,attr,omitempty"`
ShowColStripes bool `xml:"showColStripes,attr,omitempty"`
ShowLastColumn bool `xml:"showLastColumn,attr,omitempty"`
}

View file

@ -0,0 +1,100 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
"encoding/xml"
"strings"
)
// xlsxSST directly maps the sst element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main. String values may
// be stored directly inside spreadsheet cell elements; however, storing the
// same value inside multiple cell elements can result in very large worksheet
// Parts, possibly resulting in performance degradation. The Shared String Table
// is an indexed list of string values, shared across the workbook, which allows
// implementations to store values only once.
type xlsxSST struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main sst"`
Count int `xml:"count,attr"`
UniqueCount int `xml:"uniqueCount,attr"`
SI []xlsxSI `xml:"si"`
}
// xlsxSI (String Item) is the representation of an individual string in the
// Shared String table. If the string is just a simple string with formatting
// applied at the cell level, then the String Item (si) should contain a
// single text element used to express the string. However, if the string in
// the cell is more complex - i.e., has formatting applied at the character
// level - then the string item shall consist of multiple rich text runs which
// collectively are used to express the string.
type xlsxSI struct {
T string `xml:"t,omitempty"`
R []xlsxR `xml:"r"`
}
// String extracts characters from a string item.
func (x xlsxSI) String() string {
if len(x.R) > 0 {
var rows strings.Builder
for _, s := range x.R {
if s.T != nil {
rows.WriteString(s.T.Val)
}
}
return rows.String()
}
return x.T
}
// xlsxR represents a run of rich text. A rich text run is a region of text
// that share a common set of properties, such as formatting properties. The
// properties are defined in the rPr element, and the text displayed to the
// user is defined in the Text (t) element.
type xlsxR struct {
RPr *xlsxRPr `xml:"rPr"`
T *xlsxT `xml:"t"`
}
// xlsxT directly maps the t element in the run properties.
type xlsxT struct {
XMLName xml.Name `xml:"t"`
Space string `xml:"xml:space,attr,omitempty"`
Val string `xml:",innerxml"`
}
// xlsxRPr (Run Properties) specifies a set of run properties which shall be
// applied to the contents of the parent run after all style formatting has been
// applied to the text. These properties are defined as direct formatting, since
// they are directly applied to the run and supersede any formatting from
// styles.
type xlsxRPr struct {
RFont *attrValString `xml:"rFont"`
Charset *attrValInt `xml:"charset"`
Family *attrValInt `xml:"family"`
B string `xml:"b,omitempty"`
I string `xml:"i,omitempty"`
Strike string `xml:"strike,omitempty"`
Outline string `xml:"outline,omitempty"`
Shadow string `xml:"shadow,omitempty"`
Condense string `xml:"condense,omitempty"`
Extend string `xml:"extend,omitempty"`
Color *xlsxColor `xml:"color"`
Sz *attrValFloat `xml:"sz"`
U *attrValString `xml:"u"`
VertAlign *attrValString `xml:"vertAlign"`
Scheme *attrValString `xml:"scheme"`
}
// RichTextRun directly maps the settings of the rich text run.
type RichTextRun struct {
Font *Font
Text string
}

View file

@ -0,0 +1,373 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxStyleSheet directly maps the stylesheet element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxStyleSheet struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main styleSheet"`
NumFmts *xlsxNumFmts `xml:"numFmts,omitempty"`
Fonts *xlsxFonts `xml:"fonts,omitempty"`
Fills *xlsxFills `xml:"fills,omitempty"`
Borders *xlsxBorders `xml:"borders,omitempty"`
CellStyleXfs *xlsxCellStyleXfs `xml:"cellStyleXfs,omitempty"`
CellXfs *xlsxCellXfs `xml:"cellXfs,omitempty"`
CellStyles *xlsxCellStyles `xml:"cellStyles,omitempty"`
Dxfs *xlsxDxfs `xml:"dxfs,omitempty"`
TableStyles *xlsxTableStyles `xml:"tableStyles,omitempty"`
Colors *xlsxStyleColors `xml:"colors,omitempty"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxAlignment formatting information pertaining to text alignment in cells.
// There are a variety of choices for how text is aligned both horizontally and
// vertically, as well as indentation settings, and so on.
type xlsxAlignment struct {
Horizontal string `xml:"horizontal,attr,omitempty"`
Indent int `xml:"indent,attr,omitempty"`
JustifyLastLine bool `xml:"justifyLastLine,attr,omitempty"`
ReadingOrder uint64 `xml:"readingOrder,attr,omitempty"`
RelativeIndent int `xml:"relativeIndent,attr,omitempty"`
ShrinkToFit bool `xml:"shrinkToFit,attr,omitempty"`
TextRotation int `xml:"textRotation,attr,omitempty"`
Vertical string `xml:"vertical,attr,omitempty"`
WrapText bool `xml:"wrapText,attr,omitempty"`
}
// xlsxProtection (Protection Properties) contains protection properties
// associated with the cell. Each cell has protection properties that can be
// set. The cell protection properties do not take effect unless the sheet has
// been protected.
type xlsxProtection struct {
Hidden bool `xml:"hidden,attr"`
Locked bool `xml:"locked,attr"`
}
// xlsxLine directly maps the line style element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxLine struct {
Style string `xml:"style,attr,omitempty"`
Color *xlsxColor `xml:"color,omitempty"`
}
// xlsxColor is a common mapping used for both the fgColor and bgColor elements.
// Foreground color of the cell fill pattern. Cell fill patterns operate with
// two colors: a background color and a foreground color. These combine together
// to make a patterned cell fill. Background color of the cell fill pattern.
// Cell fill patterns operate with two colors: a background color and a
// foreground color. These combine together to make a patterned cell fill.
type xlsxColor struct {
Auto bool `xml:"auto,attr,omitempty"`
RGB string `xml:"rgb,attr,omitempty"`
Indexed int `xml:"indexed,attr,omitempty"`
Theme *int `xml:"theme,attr"`
Tint float64 `xml:"tint,attr,omitempty"`
}
// xlsxFonts directly maps the font element. This element contains all font
// definitions for this workbook.
type xlsxFonts struct {
Count int `xml:"count,attr"`
Font []*xlsxFont `xml:"font"`
}
// xlsxFont directly maps the font element. This element defines the
// properties for one of the fonts used in this workbook.
type xlsxFont struct {
B *bool `xml:"b,omitempty"`
I *bool `xml:"i,omitempty"`
Strike *bool `xml:"strike,omitempty"`
Outline *bool `xml:"outline,omitempty"`
Shadow *bool `xml:"shadow,omitempty"`
Condense *bool `xml:"condense,omitempty"`
Extend *bool `xml:"extend,omitempty"`
U *attrValString `xml:"u"`
Sz *attrValFloat `xml:"sz"`
Color *xlsxColor `xml:"color"`
Name *attrValString `xml:"name"`
Family *attrValInt `xml:"family"`
Charset *attrValInt `xml:"charset"`
Scheme *attrValString `xml:"scheme"`
}
// xlsxFills directly maps the fills element. This element defines the cell
// fills portion of the Styles part, consisting of a sequence of fill records. A
// cell fill consists of a background color, foreground color, and pattern to be
// applied across the cell.
type xlsxFills struct {
Count int `xml:"count,attr"`
Fill []*xlsxFill `xml:"fill,omitempty"`
}
// xlsxFill directly maps the fill element. This element specifies fill
// formatting.
type xlsxFill struct {
PatternFill *xlsxPatternFill `xml:"patternFill,omitempty"`
GradientFill *xlsxGradientFill `xml:"gradientFill,omitempty"`
}
// xlsxPatternFill directly maps the patternFill element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need. This element is
// used to specify cell fill information for pattern and solid color cell fills.
// For solid cell fills (no pattern), fgColor is used. For cell fills with
// patterns specified, then the cell fill color is specified by the bgColor
// element.
type xlsxPatternFill struct {
PatternType string `xml:"patternType,attr,omitempty"`
FgColor xlsxColor `xml:"fgColor,omitempty"`
BgColor xlsxColor `xml:"bgColor,omitempty"`
}
// xlsxGradientFill defines a gradient-style cell fill. Gradient cell fills can
// use one or two colors as the end points of color interpolation.
type xlsxGradientFill struct {
Bottom float64 `xml:"bottom,attr,omitempty"`
Degree float64 `xml:"degree,attr,omitempty"`
Left float64 `xml:"left,attr,omitempty"`
Right float64 `xml:"right,attr,omitempty"`
Top float64 `xml:"top,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Stop []*xlsxGradientFillStop `xml:"stop,omitempty"`
}
// xlsxGradientFillStop directly maps the stop element.
type xlsxGradientFillStop struct {
Position float64 `xml:"position,attr"`
Color xlsxColor `xml:"color,omitempty"`
}
// xlsxBorders directly maps the borders element. This element contains borders
// formatting information, specifying all border definitions for all cells in
// the workbook.
type xlsxBorders struct {
Count int `xml:"count,attr"`
Border []*xlsxBorder `xml:"border,omitempty"`
}
// xlsxBorder directly maps the border element. Expresses a single set of cell
// border formats (left, right, top, bottom, diagonal). Color is optional. When
// missing, 'automatic' is implied.
type xlsxBorder struct {
DiagonalDown bool `xml:"diagonalDown,attr,omitempty"`
DiagonalUp bool `xml:"diagonalUp,attr,omitempty"`
Outline bool `xml:"outline,attr,omitempty"`
Left xlsxLine `xml:"left,omitempty"`
Right xlsxLine `xml:"right,omitempty"`
Top xlsxLine `xml:"top,omitempty"`
Bottom xlsxLine `xml:"bottom,omitempty"`
Diagonal xlsxLine `xml:"diagonal,omitempty"`
}
// xlsxCellStyles directly maps the cellStyles element. This element contains
// the named cell styles, consisting of a sequence of named style records. A
// named cell style is a collection of direct or themed formatting (e.g., cell
// border, cell fill, and font type/size/style) grouped together into a single
// named style, and can be applied to a cell.
type xlsxCellStyles struct {
XMLName xml.Name `xml:"cellStyles"`
Count int `xml:"count,attr"`
CellStyle []*xlsxCellStyle `xml:"cellStyle,omitempty"`
}
// xlsxCellStyle directly maps the cellStyle element. This element represents
// the name and related formatting records for a named cell style in this
// workbook.
type xlsxCellStyle struct {
XMLName xml.Name `xml:"cellStyle"`
Name string `xml:"name,attr"`
XfID int `xml:"xfId,attr"`
BuiltInID *int `xml:"builtinId,attr,omitempty"`
ILevel *int `xml:"iLevel,attr,omitempty"`
Hidden *bool `xml:"hidden,attr,omitempty"`
CustomBuiltIn *bool `xml:"customBuiltin,attr,omitempty"`
}
// xlsxCellStyleXfs directly maps the cellStyleXfs element. This element
// contains the master formatting records (xf's) which define the formatting for
// all named cell styles in this workbook. Master formatting records reference
// individual elements of formatting (e.g., number format, font definitions,
// cell fills, etc) by specifying a zero-based index into those collections.
// Master formatting records also specify whether to apply or ignore particular
// aspects of formatting.
type xlsxCellStyleXfs struct {
Count int `xml:"count,attr"`
Xf []xlsxXf `xml:"xf,omitempty"`
}
// xlsxXf directly maps the xf element. A single xf element describes all of the
// formatting for a cell.
type xlsxXf struct {
NumFmtID *int `xml:"numFmtId,attr"`
FontID *int `xml:"fontId,attr"`
FillID *int `xml:"fillId,attr"`
BorderID *int `xml:"borderId,attr"`
XfID *int `xml:"xfId,attr"`
QuotePrefix *bool `xml:"quotePrefix,attr"`
PivotButton *bool `xml:"pivotButton,attr"`
ApplyNumberFormat *bool `xml:"applyNumberFormat,attr"`
ApplyFont *bool `xml:"applyFont,attr"`
ApplyFill *bool `xml:"applyFill,attr"`
ApplyBorder *bool `xml:"applyBorder,attr"`
ApplyAlignment *bool `xml:"applyAlignment,attr"`
ApplyProtection *bool `xml:"applyProtection,attr"`
Alignment *xlsxAlignment `xml:"alignment"`
Protection *xlsxProtection `xml:"protection"`
}
// xlsxCellXfs directly maps the cellXfs element. This element contains the
// master formatting records (xf) which define the formatting applied to cells
// in this workbook. These records are the starting point for determining the
// formatting for a cell. Cells in the Sheet Part reference the xf records by
// zero-based index.
type xlsxCellXfs struct {
Count int `xml:"count,attr"`
Xf []xlsxXf `xml:"xf,omitempty"`
}
// xlsxDxfs directly maps the dxfs element. This element contains the master
// differential formatting records (dxf's) which define formatting for all non-
// cell formatting in this workbook. Whereas xf records fully specify a
// particular aspect of formatting (e.g., cell borders) by referencing those
// formatting definitions elsewhere in the Styles part, dxf records specify
// incremental (or differential) aspects of formatting directly inline within
// the dxf element. The dxf formatting is to be applied on top of or in addition
// to any formatting already present on the object using the dxf record.
type xlsxDxfs struct {
Count int `xml:"count,attr"`
Dxfs []*xlsxDxf `xml:"dxf,omitempty"`
}
// xlsxDxf directly maps the dxf element. A single dxf record, expressing
// incremental formatting to be applied.
type xlsxDxf struct {
Dxf string `xml:",innerxml"`
}
// dxf directly maps the dxf element.
type dxf struct {
Font *xlsxFont `xml:"font"`
NumFmt *xlsxNumFmt `xml:"numFmt"`
Fill *xlsxFill `xml:"fill"`
Alignment *xlsxAlignment `xml:"alignment"`
Border *xlsxBorder `xml:"border"`
Protection *xlsxProtection `xml:"protection"`
ExtLst *xlsxExt `xml:"extLst"`
}
// xlsxTableStyles directly maps the tableStyles element. This element
// represents a collection of Table style definitions for Table styles and
// PivotTable styles used in this workbook. It consists of a sequence of
// tableStyle records, each defining a single Table style.
type xlsxTableStyles struct {
Count int `xml:"count,attr"`
DefaultPivotStyle string `xml:"defaultPivotStyle,attr"`
DefaultTableStyle string `xml:"defaultTableStyle,attr"`
TableStyles []*xlsxTableStyle `xml:"tableStyle,omitempty"`
}
// xlsxTableStyle directly maps the tableStyle element. This element represents
// a single table style definition that indicates how a spreadsheet application
// should format and display a table.
type xlsxTableStyle struct {
Name string `xml:"name,attr,omitempty"`
Pivot int `xml:"pivot,attr"`
Count int `xml:"count,attr,omitempty"`
Table bool `xml:"table,attr,omitempty"`
TableStyleElement string `xml:",innerxml"`
}
// xlsxNumFmts directly maps the numFmts element. This element defines the
// number formats in this workbook, consisting of a sequence of numFmt records,
// where each numFmt record defines a particular number format, indicating how
// to format and render the numeric value of a cell.
type xlsxNumFmts struct {
Count int `xml:"count,attr"`
NumFmt []*xlsxNumFmt `xml:"numFmt,omitempty"`
}
// xlsxNumFmt directly maps the numFmt element. This element specifies number
// format properties which indicate how to format and render the numeric value
// of a cell.
type xlsxNumFmt struct {
NumFmtID int `xml:"numFmtId,attr,omitempty"`
FormatCode string `xml:"formatCode,attr,omitempty"`
}
// xlsxStyleColors directly maps the colors element. Color information
// associated with this stylesheet. This collection is written whenever the
// legacy color palette has been modified (backwards compatibility settings) or
// a custom color has been selected while using this workbook.
type xlsxStyleColors struct {
Color string `xml:",innerxml"`
}
// Alignment directly maps the alignment settings of the cells.
type Alignment struct {
Horizontal string `json:"horizontal"`
Indent int `json:"indent"`
JustifyLastLine bool `json:"justify_last_line"`
ReadingOrder uint64 `json:"reading_order"`
RelativeIndent int `json:"relative_indent"`
ShrinkToFit bool `json:"shrink_to_fit"`
TextRotation int `json:"text_rotation"`
Vertical string `json:"vertical"`
WrapText bool `json:"wrap_text"`
}
// Border directly maps the border settings of the cells.
type Border struct {
Type string `json:"type"`
Color string `json:"color"`
Style int `json:"style"`
}
// Font directly maps the font settings of the fonts.
type Font struct {
Bold bool `json:"bold"`
Italic bool `json:"italic"`
Underline string `json:"underline"`
Family string `json:"family"`
Size float64 `json:"size"`
Strike bool `json:"strike"`
Color string `json:"color"`
}
// Fill directly maps the fill settings of the cells.
type Fill struct {
Type string `json:"type"`
Pattern int `json:"pattern"`
Color []string `json:"color"`
Shading int `json:"shading"`
}
// Protection directly maps the protection settings of the cells.
type Protection struct {
Hidden bool `json:"hidden"`
Locked bool `json:"locked"`
}
// Style directly maps the style settings of the cells.
type Style struct {
Border []Border `json:"border"`
Fill Fill `json:"fill"`
Font *Font `json:"font"`
Alignment *Alignment `json:"alignment"`
Protection *Protection `json:"protection"`
NumFmt int `json:"number_format"`
DecimalPlaces int `json:"decimal_places"`
CustomNumFmt *string `json:"custom_number_format"`
Lang string `json:"lang"`
NegRed bool `json:"negred"`
}

View file

@ -0,0 +1,215 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxTable directly maps the table element. A table helps organize and provide
// structure to lists of information in a worksheet. Tables have clearly labeled
// columns, rows, and data regions. Tables make it easier for users to sort,
// analyze, format, manage, add, and delete information. This element is the
// root element for a table that is not a single cell XML table.
type xlsxTable struct {
XMLName xml.Name `xml:"table"`
XMLNS string `xml:"xmlns,attr"`
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
DisplayName string `xml:"displayName,attr,omitempty"`
HeaderRowBorderDxfID int `xml:"headerRowBorderDxfId,attr,omitempty"`
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
HeaderRowCount int `xml:"headerRowCount,attr,omitempty"`
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
ID int `xml:"id,attr"`
InsertRow bool `xml:"insertRow,attr,omitempty"`
InsertRowShift bool `xml:"insertRowShift,attr,omitempty"`
Name string `xml:"name,attr"`
Published bool `xml:"published,attr,omitempty"`
Ref string `xml:"ref,attr"`
TotalsRowCount int `xml:"totalsRowCount,attr,omitempty"`
TotalsRowDxfID int `xml:"totalsRowDxfId,attr,omitempty"`
TotalsRowShown bool `xml:"totalsRowShown,attr"`
AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
TableColumns *xlsxTableColumns `xml:"tableColumns"`
TableStyleInfo *xlsxTableStyleInfo `xml:"tableStyleInfo"`
}
// xlsxAutoFilter temporarily hides rows based on a filter criteria, which is
// applied column by column to a table of data in the worksheet. This collection
// expresses AutoFilter settings.
type xlsxAutoFilter struct {
XMLName xml.Name `xml:"autoFilter"`
Ref string `xml:"ref,attr"`
FilterColumn *xlsxFilterColumn `xml:"filterColumn"`
}
// xlsxFilterColumn directly maps the filterColumn element. The filterColumn
// collection identifies a particular column in the AutoFilter range and
// specifies filter information that has been applied to this column. If a
// column in the AutoFilter range has no criteria specified, then there is no
// corresponding filterColumn collection expressed for that column.
type xlsxFilterColumn struct {
ColID int `xml:"colId,attr"`
HiddenButton bool `xml:"hiddenButton,attr,omitempty"`
ShowButton bool `xml:"showButton,attr,omitempty"`
CustomFilters *xlsxCustomFilters `xml:"customFilters"`
Filters *xlsxFilters `xml:"filters"`
ColorFilter *xlsxColorFilter `xml:"colorFilter"`
DynamicFilter *xlsxDynamicFilter `xml:"dynamicFilter"`
IconFilter *xlsxIconFilter `xml:"iconFilter"`
Top10 *xlsxTop10 `xml:"top10"`
}
// xlsxCustomFilters directly maps the customFilters element. When there is more
// than one custom filter criteria to apply (an 'and' or 'or' joining two
// criteria), then this element groups the customFilter elements together.
type xlsxCustomFilters struct {
And bool `xml:"and,attr,omitempty"`
CustomFilter []*xlsxCustomFilter `xml:"customFilter"`
}
// xlsxCustomFilter directly maps the customFilter element. A custom AutoFilter
// specifies an operator and a value. There can be at most two customFilters
// specified, and in that case the parent element specifies whether the two
// conditions are joined by 'and' or 'or'. For any cells whose values do not
// meet the specified criteria, the corresponding rows shall be hidden from view
// when the filter is applied.
type xlsxCustomFilter struct {
Operator string `xml:"operator,attr,omitempty"`
Val string `xml:"val,attr,omitempty"`
}
// xlsxFilters directly maps the filters (Filter Criteria) element. When
// multiple values are chosen to filter by, or when a group of date values are
// chosen to filter by, this element groups those criteria together.
type xlsxFilters struct {
Blank bool `xml:"blank,attr,omitempty"`
CalendarType string `xml:"calendarType,attr,omitempty"`
Filter []*xlsxFilter `xml:"filter"`
DateGroupItem []*xlsxDateGroupItem `xml:"dateGroupItem"`
}
// xlsxFilter directly maps the filter element. This element expresses a filter
// criteria value.
type xlsxFilter struct {
Val string `xml:"val,attr,omitempty"`
}
// xlsxColorFilter directly maps the colorFilter element. This element specifies
// the color to filter by and whether to use the cell's fill or font color in
// the filter criteria. If the cell's font or fill color does not match the
// color specified in the criteria, the rows corresponding to those cells are
// hidden from view.
type xlsxColorFilter struct {
CellColor bool `xml:"cellColor,attr"`
DxfID int `xml:"dxfId,attr"`
}
// xlsxDynamicFilter directly maps the dynamicFilter element. This collection
// specifies dynamic filter criteria. These criteria are considered dynamic
// because they can change, either with the data itself (e.g., "above average")
// or with the current system date (e.g., show values for "today"). For any
// cells whose values do not meet the specified criteria, the corresponding rows
// shall be hidden from view when the filter is applied.
type xlsxDynamicFilter struct {
MaxValISO string `xml:"maxValIso,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Val float64 `xml:"val,attr,omitempty"`
ValISO string `xml:"valIso,attr,omitempty"`
}
// xlsxIconFilter directly maps the iconFilter element. This element specifies
// the icon set and particular icon within that set to filter by. For any cells
// whose icon does not match the specified criteria, the corresponding rows
// shall be hidden from view when the filter is applied.
type xlsxIconFilter struct {
IconID int `xml:"iconId,attr"`
IconSet string `xml:"iconSet,attr,omitempty"`
}
// xlsxTop10 directly maps the top10 element. This element specifies the top N
// (percent or number of items) to filter by.
type xlsxTop10 struct {
FilterVal float64 `xml:"filterVal,attr,omitempty"`
Percent bool `xml:"percent,attr,omitempty"`
Top bool `xml:"top,attr"`
Val float64 `xml:"val,attr,omitempty"`
}
// xlsxDateGroupItem directly maps the dateGroupItem element. This collection is
// used to express a group of dates or times which are used in an AutoFilter
// criteria. [Note: See parent element for an example. end note] Values are
// always written in the calendar type of the first date encountered in the
// filter range, so that all subsequent dates, even when formatted or
// represented by other calendar types, can be correctly compared for the
// purposes of filtering.
type xlsxDateGroupItem struct {
DateTimeGrouping string `xml:"dateTimeGrouping,attr,omitempty"`
Day int `xml:"day,attr,omitempty"`
Hour int `xml:"hour,attr,omitempty"`
Minute int `xml:"minute,attr,omitempty"`
Month int `xml:"month,attr,omitempty"`
Second int `xml:"second,attr,omitempty"`
Year int `xml:"year,attr,omitempty"`
}
// xlsxTableColumns directly maps the element representing the collection of all
// table columns for this table.
type xlsxTableColumns struct {
Count int `xml:"count,attr"`
TableColumn []*xlsxTableColumn `xml:"tableColumn"`
}
// xlsxTableColumn directly maps the element representing a single column for
// this table.
type xlsxTableColumn struct {
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
ID int `xml:"id,attr"`
Name string `xml:"name,attr"`
QueryTableFieldID int `xml:"queryTableFieldId,attr,omitempty"`
TotalsRowCellStyle string `xml:"totalsRowCellStyle,attr,omitempty"`
TotalsRowDxfID int `xml:"totalsRowDxfId,attr,omitempty"`
TotalsRowFunction string `xml:"totalsRowFunction,attr,omitempty"`
TotalsRowLabel string `xml:"totalsRowLabel,attr,omitempty"`
UniqueName string `xml:"uniqueName,attr,omitempty"`
}
// xlsxTableStyleInfo directly maps the tableStyleInfo element. This element
// describes which style is used to display this table, and specifies which
// portions of the table have the style applied.
type xlsxTableStyleInfo struct {
Name string `xml:"name,attr,omitempty"`
ShowFirstColumn bool `xml:"showFirstColumn,attr"`
ShowLastColumn bool `xml:"showLastColumn,attr"`
ShowRowStripes bool `xml:"showRowStripes,attr"`
ShowColumnStripes bool `xml:"showColumnStripes,attr"`
}
// formatTable directly maps the format settings of the table.
type formatTable struct {
TableName string `json:"table_name"`
TableStyle string `json:"table_style"`
ShowFirstColumn bool `json:"show_first_column"`
ShowLastColumn bool `json:"show_last_column"`
ShowRowStripes bool `json:"show_row_stripes"`
ShowColumnStripes bool `json:"show_column_stripes"`
}
// formatAutoFilter directly maps the auto filter settings.
type formatAutoFilter struct {
Column string `json:"column"`
Expression string `json:"expression"`
FilterList []struct {
Column string `json:"column"`
Value []int `json:"value"`
} `json:"filter_list"`
}

View file

@ -0,0 +1,149 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxTheme directly maps the theme element in the namespace
// http://schemas.openxmlformats.org/drawingml/2006/main
type xlsxTheme struct {
ThemeElements xlsxThemeElements `xml:"themeElements"`
ObjectDefaults xlsxObjectDefaults `xml:"objectDefaults"`
ExtraClrSchemeLst xlsxExtraClrSchemeLst `xml:"extraClrSchemeLst"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// objectDefaults element allows for the definition of default shape, line,
// and textbox formatting properties. An application can use this information
// to format a shape (or text) initially on insertion into a document.
type xlsxObjectDefaults struct {
ObjectDefaults string `xml:",innerxml"`
}
// xlsxExtraClrSchemeLst element is a container for the list of extra color
// schemes present in a document.
type xlsxExtraClrSchemeLst struct {
ExtraClrSchemeLst string `xml:",innerxml"`
}
// xlsxThemeElements directly maps the element defines the theme formatting
// options for the theme and is the workhorse of the theme. This is where the
// bulk of the shared theme information is contained and used by a document.
// This element contains the color scheme, font scheme, and format scheme
// elements which define the different formatting aspects of what a theme
// defines.
type xlsxThemeElements struct {
ClrScheme xlsxClrScheme `xml:"clrScheme"`
FontScheme xlsxFontScheme `xml:"fontScheme"`
FmtScheme xlsxFmtScheme `xml:"fmtScheme"`
}
// xlsxClrScheme element specifies the theme color, stored in the document's
// Theme part to which the value of this theme color shall be mapped. This
// mapping enables multiple theme colors to be chained together.
type xlsxClrScheme struct {
Name string `xml:"name,attr"`
Children []xlsxClrSchemeEl `xml:",any"`
}
// xlsxFontScheme element defines the font scheme within the theme. The font
// scheme consists of a pair of major and minor fonts for which to use in a
// document. The major font corresponds well with the heading areas of a
// document, and the minor font corresponds well with the normal text or
// paragraph areas.
type xlsxFontScheme struct {
Name string `xml:"name,attr"`
MajorFont xlsxMajorFont `xml:"majorFont"`
MinorFont xlsxMinorFont `xml:"minorFont"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxMajorFont element defines the set of major fonts which are to be used
// under different languages or locals.
type xlsxMajorFont struct {
Children []xlsxFontSchemeEl `xml:",any"`
}
// xlsxMinorFont element defines the set of minor fonts that are to be used
// under different languages or locals.
type xlsxMinorFont struct {
Children []xlsxFontSchemeEl `xml:",any"`
}
// xlsxFmtScheme element contains the background fill styles, effect styles,
// fill styles, and line styles which define the style matrix for a theme. The
// style matrix consists of subtle, moderate, and intense fills, lines, and
// effects. The background fills are not generally thought of to directly be
// associated with the matrix, but do play a role in the style of the overall
// document. Usually, a given object chooses a single line style, a single
// fill style, and a single effect style in order to define the overall final
// look of the object.
type xlsxFmtScheme struct {
Name string `xml:"name,attr"`
FillStyleLst xlsxFillStyleLst `xml:"fillStyleLst"`
LnStyleLst xlsxLnStyleLst `xml:"lnStyleLst"`
EffectStyleLst xlsxEffectStyleLst `xml:"effectStyleLst"`
BgFillStyleLst xlsxBgFillStyleLst `xml:"bgFillStyleLst"`
}
// xlsxFillStyleLst element defines a set of three fill styles that are used
// within a theme. The three fill styles are arranged in order from subtle to
// moderate to intense.
type xlsxFillStyleLst struct {
FillStyleLst string `xml:",innerxml"`
}
// xlsxLnStyleLst element defines a list of three line styles for use within a
// theme. The three line styles are arranged in order from subtle to moderate
// to intense versions of lines. This list makes up part of the style matrix.
type xlsxLnStyleLst struct {
LnStyleLst string `xml:",innerxml"`
}
// xlsxEffectStyleLst element defines a set of three effect styles that create
// the effect style list for a theme. The effect styles are arranged in order
// of subtle to moderate to intense.
type xlsxEffectStyleLst struct {
EffectStyleLst string `xml:",innerxml"`
}
// xlsxBgFillStyleLst element defines a list of background fills that are
// used within a theme. The background fills consist of three fills, arranged
// in order from subtle to moderate to intense.
type xlsxBgFillStyleLst struct {
BgFillStyleLst string `xml:",innerxml"`
}
// xlsxClrScheme maps to children of the clrScheme element in the namespace
// http://schemas.openxmlformats.org/drawingml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxClrSchemeEl struct {
XMLName xml.Name
SysClr *xlsxSysClr `xml:"sysClr"`
SrgbClr *attrValString `xml:"srgbClr"`
}
// xlsxFontSchemeEl directly maps the major and minor font of the style's font
// scheme.
type xlsxFontSchemeEl struct {
XMLName xml.Name
Script string `xml:"script,attr,omitempty"`
Typeface string `xml:"typeface,attr"`
Panose string `xml:"panose,attr,omitempty"`
PitchFamily string `xml:"pitchFamily,attr,omitempty"`
Charset string `xml:"charset,attr,omitempty"`
}
// xlsxSysClr element specifies a color bound to predefined operating system
// elements.
type xlsxSysClr struct {
Val string `xml:"val,attr"`
LastClr string `xml:"lastClr,attr"`
}

View file

@ -0,0 +1,299 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxRelationships describe references from parts to other internal resources in the package or to external resources.
type xlsxRelationships struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"`
Relationships []xlsxRelationship `xml:"Relationship"`
}
// xlsxRelationship contains relations which maps id and XML.
type xlsxRelationship struct {
ID string `xml:"Id,attr"`
Target string `xml:",attr"`
Type string `xml:",attr"`
TargetMode string `xml:",attr,omitempty"`
}
// xlsxWorkbook directly maps the workbook element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxWorkbook struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
FileVersion *xlsxFileVersion `xml:"fileVersion"`
WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"`
WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
BookViews *xlsxBookViews `xml:"bookViews"`
Sheets xlsxSheets `xml:"sheets"`
ExternalReferences *xlsxExternalReferences `xml:"externalReferences"`
DefinedNames *xlsxDefinedNames `xml:"definedNames"`
CalcPr *xlsxCalcPr `xml:"calcPr"`
CustomWorkbookViews *xlsxCustomWorkbookViews `xml:"customWorkbookViews"`
PivotCaches *xlsxPivotCaches `xml:"pivotCaches"`
ExtLst *xlsxExtLst `xml:"extLst"`
FileRecoveryPr *xlsxFileRecoveryPr `xml:"fileRecoveryPr"`
}
// xlsxFileRecoveryPr maps sheet recovery information. This element defines
// properties that track the state of the workbook file, such as whether the
// file was saved during a crash, or whether it should be opened in auto-recover
// mode.
type xlsxFileRecoveryPr struct {
AutoRecover bool `xml:"autoRecover,attr,omitempty"`
CrashSave bool `xml:"crashSave,attr,omitempty"`
DataExtractLoad bool `xml:"dataExtractLoad,attr,omitempty"`
RepairLoad bool `xml:"repairLoad,attr,omitempty"`
}
// xlsxWorkbookProtection directly maps the workbookProtection element. This
// element specifies options for protecting data in the workbook. Applications
// might use workbook protection to prevent anyone from accidentally changing,
// moving, or deleting important data. This protection can be ignored by
// applications which choose not to support this optional protection mechanism.
// When a password is to be hashed and stored in this element, it shall be
// hashed as defined below, starting from a UTF-16LE encoded string value. If
// there is a leading BOM character (U+FEFF) in the encoded password it is
// removed before hash calculation.
type xlsxWorkbookProtection struct {
LockRevision bool `xml:"lockRevision,attr,omitempty"`
LockStructure bool `xml:"lockStructure,attr,omitempty"`
LockWindows bool `xml:"lockWindows,attr,omitempty"`
RevisionsAlgorithmName string `xml:"revisionsAlgorithmName,attr,omitempty"`
RevisionsHashValue string `xml:"revisionsHashValue,attr,omitempty"`
RevisionsSaltValue string `xml:"revisionsSaltValue,attr,omitempty"`
RevisionsSpinCount int `xml:"revisionsSpinCount,attr,omitempty"`
WorkbookAlgorithmName string `xml:"workbookAlgorithmName,attr,omitempty"`
WorkbookHashValue string `xml:"workbookHashValue,attr,omitempty"`
WorkbookSaltValue string `xml:"workbookSaltValue,attr,omitempty"`
WorkbookSpinCount int `xml:"workbookSpinCount,attr,omitempty"`
}
// xlsxFileVersion directly maps the fileVersion element. This element defines
// properties that track which version of the application accessed the data and
// source code contained in the file.
type xlsxFileVersion struct {
AppName string `xml:"appName,attr,omitempty"`
CodeName string `xml:"codeName,attr,omitempty"`
LastEdited string `xml:"lastEdited,attr,omitempty"`
LowestEdited string `xml:"lowestEdited,attr,omitempty"`
RupBuild string `xml:"rupBuild,attr,omitempty"`
}
// xlsxWorkbookPr directly maps the workbookPr element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a collection of workbook properties.
type xlsxWorkbookPr struct {
AllowRefreshQuery bool `xml:"allowRefreshQuery,attr,omitempty"`
AutoCompressPictures bool `xml:"autoCompressPictures,attr,omitempty"`
BackupFile bool `xml:"backupFile,attr,omitempty"`
CheckCompatibility bool `xml:"checkCompatibility,attr,omitempty"`
CodeName string `xml:"codeName,attr,omitempty"`
Date1904 bool `xml:"date1904,attr,omitempty"`
DefaultThemeVersion string `xml:"defaultThemeVersion,attr,omitempty"`
FilterPrivacy bool `xml:"filterPrivacy,attr,omitempty"`
HidePivotFieldList bool `xml:"hidePivotFieldList,attr,omitempty"`
PromptedSolutions bool `xml:"promptedSolutions,attr,omitempty"`
PublishItems bool `xml:"publishItems,attr,omitempty"`
RefreshAllConnections bool `xml:"refreshAllConnections,attr,omitempty"`
SaveExternalLinkValues bool `xml:"saveExternalLinkValues,attr,omitempty"`
ShowBorderUnselectedTables bool `xml:"showBorderUnselectedTables,attr,omitempty"`
ShowInkAnnotation bool `xml:"showInkAnnotation,attr,omitempty"`
ShowObjects string `xml:"showObjects,attr,omitempty"`
ShowPivotChartFilter bool `xml:"showPivotChartFilter,attr,omitempty"`
UpdateLinks string `xml:"updateLinks,attr,omitempty"`
}
// xlsxBookViews directly maps the bookViews element. This element specifies the
// collection of workbook views of the enclosing workbook. Each view can specify
// a window position, filter options, and other configurations. There is no
// limit on the number of workbook views that can be defined for a workbook.
type xlsxBookViews struct {
WorkBookView []xlsxWorkBookView `xml:"workbookView"`
}
// xlsxWorkBookView directly maps the workbookView element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// specifies a single Workbook view.
type xlsxWorkBookView struct {
ActiveTab int `xml:"activeTab,attr,omitempty"`
AutoFilterDateGrouping bool `xml:"autoFilterDateGrouping,attr,omitempty"`
FirstSheet int `xml:"firstSheet,attr,omitempty"`
Minimized bool `xml:"minimized,attr,omitempty"`
ShowHorizontalScroll bool `xml:"showHorizontalScroll,attr,omitempty"`
ShowSheetTabs bool `xml:"showSheetTabs,attr,omitempty"`
ShowVerticalScroll bool `xml:"showVerticalScroll,attr,omitempty"`
TabRatio int `xml:"tabRatio,attr,omitempty"`
Visibility string `xml:"visibility,attr,omitempty"`
WindowHeight int `xml:"windowHeight,attr,omitempty"`
WindowWidth int `xml:"windowWidth,attr,omitempty"`
XWindow string `xml:"xWindow,attr,omitempty"`
YWindow string `xml:"yWindow,attr,omitempty"`
}
// xlsxSheets directly maps the sheets element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
type xlsxSheets struct {
Sheet []xlsxSheet `xml:"sheet"`
}
// xlsxSheet defines a sheet in this workbook. Sheet data is stored in a
// separate part.
type xlsxSheet struct {
Name string `xml:"name,attr,omitempty"`
SheetID int `xml:"sheetId,attr,omitempty"`
ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
State string `xml:"state,attr,omitempty"`
}
// xlsxExternalReferences directly maps the externalReferences element of the
// external workbook references part.
type xlsxExternalReferences struct {
ExternalReference []xlsxExternalReference `xml:"externalReference"`
}
// xlsxExternalReference directly maps the externalReference element of the
// external workbook references part.
type xlsxExternalReference struct {
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxPivotCaches element enumerates pivot cache definition parts used by pivot
// tables and formulas in this workbook.
type xlsxPivotCaches struct {
PivotCache []xlsxPivotCache `xml:"pivotCache"`
}
// xlsxPivotCache directly maps the pivotCache element.
type xlsxPivotCache struct {
CacheID int `xml:"cacheId,attr"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// extLst element provides a convention for extending spreadsheetML in
// predefined locations. The locations shall be denoted with the extLst element,
// and are called extension lists. Extension list locations within the markup
// document are specified in the markup specification and can be used to store
// extensions to the markup specification, whether those are future version
// extensions of the markup specification or are private extensions implemented
// independently from the markup specification. Markup within an extension might
// not be understood by a consumer.
type xlsxExtLst struct {
Ext string `xml:",innerxml"`
}
// xlsxDefinedNames directly maps the definedNames element. This element defines
// the collection of defined names for this workbook. Defined names are
// descriptive names to represent cells, ranges of cells, formulas, or constant
// values. Defined names can be used to represent a range on any worksheet.
type xlsxDefinedNames struct {
DefinedName []xlsxDefinedName `xml:"definedName"`
}
// xlsxDefinedName directly maps the definedName element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a defined name within this workbook. A defined name is descriptive
// text that is used to represents a cell, range of cells, formula, or constant
// value. For a descriptions of the attributes see https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.definedname
type xlsxDefinedName struct {
Comment string `xml:"comment,attr,omitempty"`
CustomMenu string `xml:"customMenu,attr,omitempty"`
Description string `xml:"description,attr,omitempty"`
Function bool `xml:"function,attr,omitempty"`
FunctionGroupID int `xml:"functionGroupId,attr,omitempty"`
Help string `xml:"help,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
LocalSheetID *int `xml:"localSheetId,attr"`
Name string `xml:"name,attr,omitempty"`
PublishToServer bool `xml:"publishToServer,attr,omitempty"`
ShortcutKey string `xml:"shortcutKey,attr,omitempty"`
StatusBar string `xml:"statusBar,attr,omitempty"`
VbProcedure bool `xml:"vbProcedure,attr,omitempty"`
WorkbookParameter bool `xml:"workbookParameter,attr,omitempty"`
Xlm bool `xml:"xml,attr,omitempty"`
Data string `xml:",chardata"`
}
// xlsxCalcPr directly maps the calcPr element. This element defines the
// collection of properties the application uses to record calculation status
// and details. Calculation is the process of computing formulas and then
// displaying the results as values in the cells that contain the formulas.
type xlsxCalcPr struct {
CalcCompleted bool `xml:"calcCompleted,attr,omitempty"`
CalcID string `xml:"calcId,attr,omitempty"`
CalcMode string `xml:"calcMode,attr,omitempty"`
CalcOnSave bool `xml:"calcOnSave,attr,omitempty"`
ConcurrentCalc *bool `xml:"concurrentCalc,attr"`
ConcurrentManualCount int `xml:"concurrentManualCount,attr,omitempty"`
ForceFullCalc bool `xml:"forceFullCalc,attr,omitempty"`
FullCalcOnLoad bool `xml:"fullCalcOnLoad,attr,omitempty"`
FullPrecision bool `xml:"fullPrecision,attr,omitempty"`
Iterate bool `xml:"iterate,attr,omitempty"`
IterateCount int `xml:"iterateCount,attr,omitempty"`
IterateDelta float64 `xml:"iterateDelta,attr,omitempty"`
RefMode string `xml:"refMode,attr,omitempty"`
}
// xlsxCustomWorkbookViews defines the collection of custom workbook views that
// are defined for this workbook. A customWorkbookView is similar in concept to
// a workbookView in that its attributes contain settings related to the way
// that the workbook should be displayed on a screen by a spreadsheet
// application.
type xlsxCustomWorkbookViews struct {
CustomWorkbookView []xlsxCustomWorkbookView `xml:"customWorkbookView"`
}
// xlsxCustomWorkbookView directly maps the customWorkbookView element. This
// element specifies a single custom workbook view. A custom workbook view
// consists of a set of display and print settings that you can name and apply
// to a workbook. You can create more than one custom workbook view of the same
// workbook. Custom Workbook Views are not required in order to construct a
// valid SpreadsheetML document, and are not necessary if the document is never
// displayed by a spreadsheet application, or if the spreadsheet application has
// a fixed display for workbooks. However, if a spreadsheet application chooses
// to implement configurable display modes, the customWorkbookView element
// should be used to persist the settings for those display modes.
type xlsxCustomWorkbookView struct {
ActiveSheetID *int `xml:"activeSheetId,attr"`
AutoUpdate *bool `xml:"autoUpdate,attr"`
ChangesSavedWin *bool `xml:"changesSavedWin,attr"`
GUID *string `xml:"guid,attr"`
IncludeHiddenRowCol *bool `xml:"includeHiddenRowCol,attr"`
IncludePrintSettings *bool `xml:"includePrintSettings,attr"`
Maximized *bool `xml:"maximized,attr"`
MergeInterval int `xml:"mergeInterval,attr"`
Minimized *bool `xml:"minimized,attr"`
Name *string `xml:"name,attr"`
OnlySync *bool `xml:"onlySync,attr"`
PersonalView *bool `xml:"personalView,attr"`
ShowComments *string `xml:"showComments,attr"`
ShowFormulaBar *bool `xml:"showFormulaBar,attr"`
ShowHorizontalScroll *bool `xml:"showHorizontalScroll,attr"`
ShowObjects *string `xml:"showObjects,attr"`
ShowSheetTabs *bool `xml:"showSheetTabs,attr"`
ShowStatusbar *bool `xml:"showStatusbar,attr"`
ShowVerticalScroll *bool `xml:"showVerticalScroll,attr"`
TabRatio *int `xml:"tabRatio,attr"`
WindowHeight *int `xml:"windowHeight,attr"`
WindowWidth *int `xml:"windowWidth,attr"`
XWindow *int `xml:"xWindow,attr"`
YWindow *int `xml:"yWindow,attr"`
}
// DefinedName directly maps the name for a cell or cell range on a
// worksheet.
type DefinedName struct {
Name string
Comment string
RefersTo string
Scope string
}

View file

@ -0,0 +1,872 @@
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import "encoding/xml"
// xlsxWorksheet directly maps the worksheet element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
type xlsxWorksheet struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
SheetPr *xlsxSheetPr `xml:"sheetPr"`
Dimension *xlsxDimension `xml:"dimension"`
SheetViews *xlsxSheetViews `xml:"sheetViews"`
SheetFormatPr *xlsxSheetFormatPr `xml:"sheetFormatPr"`
Cols *xlsxCols `xml:"cols"`
SheetData xlsxSheetData `xml:"sheetData"`
SheetCalcPr *xlsxInnerXML `xml:"sheetCalcPr"`
SheetProtection *xlsxSheetProtection `xml:"sheetProtection"`
ProtectedRanges *xlsxInnerXML `xml:"protectedRanges"`
Scenarios *xlsxInnerXML `xml:"scenarios"`
AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
SortState *xlsxSortState `xml:"sortState"`
DataConsolidate *xlsxInnerXML `xml:"dataConsolidate"`
CustomSheetViews *xlsxCustomSheetViews `xml:"customSheetViews"`
MergeCells *xlsxMergeCells `xml:"mergeCells"`
PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"`
ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"`
DataValidations *xlsxDataValidations `xml:"dataValidations"`
Hyperlinks *xlsxHyperlinks `xml:"hyperlinks"`
PrintOptions *xlsxPrintOptions `xml:"printOptions"`
PageMargins *xlsxPageMargins `xml:"pageMargins"`
PageSetUp *xlsxPageSetUp `xml:"pageSetup"`
HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"`
RowBreaks *xlsxBreaks `xml:"rowBreaks"`
ColBreaks *xlsxBreaks `xml:"colBreaks"`
CustomProperties *xlsxInnerXML `xml:"customProperties"`
CellWatches *xlsxInnerXML `xml:"cellWatches"`
IgnoredErrors *xlsxInnerXML `xml:"ignoredErrors"`
SmartTags *xlsxInnerXML `xml:"smartTags"`
Drawing *xlsxDrawing `xml:"drawing"`
LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"`
LegacyDrawingHF *xlsxLegacyDrawingHF `xml:"legacyDrawingHF"`
DrawingHF *xlsxDrawingHF `xml:"drawingHF"`
Picture *xlsxPicture `xml:"picture"`
OleObjects *xlsxInnerXML `xml:"oleObjects"`
Controls *xlsxInnerXML `xml:"controls"`
WebPublishItems *xlsxInnerXML `xml:"webPublishItems"`
TableParts *xlsxTableParts `xml:"tableParts"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxDrawing change r:id to rid in the namespace.
type xlsxDrawing struct {
XMLName xml.Name `xml:"drawing"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxHeaderFooter directly maps the headerFooter element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - When printed or
// viewed in page layout view (§18.18.69), each page of a worksheet can have a
// page header, a page footer, or both. The headers and footers on odd-numbered
// pages can differ from those on even-numbered pages, and the headers and
// footers on the first page can differ from those on odd- and even-numbered
// pages. In the latter case, the first page is not considered an odd page.
type xlsxHeaderFooter struct {
XMLName xml.Name `xml:"headerFooter"`
AlignWithMargins bool `xml:"alignWithMargins,attr,omitempty"`
DifferentFirst bool `xml:"differentFirst,attr,omitempty"`
DifferentOddEven bool `xml:"differentOddEven,attr,omitempty"`
ScaleWithDoc bool `xml:"scaleWithDoc,attr,omitempty"`
OddHeader string `xml:"oddHeader,omitempty"`
OddFooter string `xml:"oddFooter,omitempty"`
EvenHeader string `xml:"evenHeader,omitempty"`
EvenFooter string `xml:"evenFooter,omitempty"`
FirstFooter string `xml:"firstFooter,omitempty"`
FirstHeader string `xml:"firstHeader,omitempty"`
DrawingHF *xlsxDrawingHF `xml:"drawingHF"`
}
// xlsxDrawingHF (Drawing Reference in Header Footer) specifies the usage of
// drawing objects to be rendered in the headers and footers of the sheet. It
// specifies an explicit relationship to the part containing the DrawingML
// shapes used in the headers and footers. It also indicates where in the
// headers and footers each shape belongs. One drawing object can appear in
// each of the left section, center section and right section of a header and
// a footer.
type xlsxDrawingHF struct {
Content string `xml:",innerxml"`
}
// xlsxPageSetUp directly maps the pageSetup element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page setup
// settings for the worksheet.
type xlsxPageSetUp struct {
XMLName xml.Name `xml:"pageSetup"`
BlackAndWhite bool `xml:"blackAndWhite,attr,omitempty"`
CellComments string `xml:"cellComments,attr,omitempty"`
Copies int `xml:"copies,attr,omitempty"`
Draft bool `xml:"draft,attr,omitempty"`
Errors string `xml:"errors,attr,omitempty"`
FirstPageNumber int `xml:"firstPageNumber,attr,omitempty"`
FitToHeight int `xml:"fitToHeight,attr,omitempty"`
FitToWidth int `xml:"fitToWidth,attr,omitempty"`
HorizontalDPI int `xml:"horizontalDpi,attr,omitempty"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
Orientation string `xml:"orientation,attr,omitempty"`
PageOrder string `xml:"pageOrder,attr,omitempty"`
PaperHeight string `xml:"paperHeight,attr,omitempty"`
PaperSize int `xml:"paperSize,attr,omitempty"`
PaperWidth string `xml:"paperWidth,attr,omitempty"`
Scale int `xml:"scale,attr,omitempty"`
UseFirstPageNumber bool `xml:"useFirstPageNumber,attr,omitempty"`
UsePrinterDefaults bool `xml:"usePrinterDefaults,attr,omitempty"`
VerticalDPI int `xml:"verticalDpi,attr,omitempty"`
}
// xlsxPrintOptions directly maps the printOptions element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Print options for
// the sheet. Printer-specific settings are stored separately in the Printer
// Settings part.
type xlsxPrintOptions struct {
XMLName xml.Name `xml:"printOptions"`
GridLines bool `xml:"gridLines,attr,omitempty"`
GridLinesSet bool `xml:"gridLinesSet,attr,omitempty"`
Headings bool `xml:"headings,attr,omitempty"`
HorizontalCentered bool `xml:"horizontalCentered,attr,omitempty"`
VerticalCentered bool `xml:"verticalCentered,attr,omitempty"`
}
// xlsxPageMargins directly maps the pageMargins element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page margins for
// a sheet or a custom sheet view.
type xlsxPageMargins struct {
XMLName xml.Name `xml:"pageMargins"`
Bottom float64 `xml:"bottom,attr"`
Footer float64 `xml:"footer,attr"`
Header float64 `xml:"header,attr"`
Left float64 `xml:"left,attr"`
Right float64 `xml:"right,attr"`
Top float64 `xml:"top,attr"`
}
// xlsxSheetFormatPr directly maps the sheetFormatPr element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main. This element
// specifies the sheet formatting properties.
type xlsxSheetFormatPr struct {
XMLName xml.Name `xml:"sheetFormatPr"`
BaseColWidth uint8 `xml:"baseColWidth,attr,omitempty"`
DefaultColWidth float64 `xml:"defaultColWidth,attr,omitempty"`
DefaultRowHeight float64 `xml:"defaultRowHeight,attr"`
CustomHeight bool `xml:"customHeight,attr,omitempty"`
ZeroHeight bool `xml:"zeroHeight,attr,omitempty"`
ThickTop bool `xml:"thickTop,attr,omitempty"`
ThickBottom bool `xml:"thickBottom,attr,omitempty"`
OutlineLevelRow uint8 `xml:"outlineLevelRow,attr,omitempty"`
OutlineLevelCol uint8 `xml:"outlineLevelCol,attr,omitempty"`
}
// xlsxSheetViews directly maps the sheetViews element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Worksheet views
// collection.
type xlsxSheetViews struct {
XMLName xml.Name `xml:"sheetViews"`
SheetView []xlsxSheetView `xml:"sheetView"`
}
// xlsxSheetView directly maps the sheetView element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need. A single sheet
// view definition. When more than one sheet view is defined in the file, it
// means that when opening the workbook, each sheet view corresponds to a
// separate window within the spreadsheet application, where each window is
// showing the particular sheet containing the same workbookViewId value, the
// last sheetView definition is loaded, and the others are discarded. When
// multiple windows are viewing the same sheet, multiple sheetView elements
// (with corresponding workbookView entries) are saved.
// See https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.sheetview
type xlsxSheetView struct {
WindowProtection bool `xml:"windowProtection,attr,omitempty"`
ShowFormulas bool `xml:"showFormulas,attr,omitempty"`
ShowGridLines *bool `xml:"showGridLines,attr"`
ShowRowColHeaders *bool `xml:"showRowColHeaders,attr"`
ShowZeros *bool `xml:"showZeros,attr,omitempty"`
RightToLeft bool `xml:"rightToLeft,attr,omitempty"`
TabSelected bool `xml:"tabSelected,attr,omitempty"`
ShowWhiteSpace *bool `xml:"showWhiteSpace,attr"`
ShowOutlineSymbols bool `xml:"showOutlineSymbols,attr,omitempty"`
DefaultGridColor *bool `xml:"defaultGridColor,attr"`
View string `xml:"view,attr,omitempty"`
TopLeftCell string `xml:"topLeftCell,attr,omitempty"`
ColorID int `xml:"colorId,attr,omitempty"`
ZoomScale float64 `xml:"zoomScale,attr,omitempty"`
ZoomScaleNormal float64 `xml:"zoomScaleNormal,attr,omitempty"`
ZoomScalePageLayoutView float64 `xml:"zoomScalePageLayoutView,attr,omitempty"`
ZoomScaleSheetLayoutView float64 `xml:"zoomScaleSheetLayoutView,attr,omitempty"`
WorkbookViewID int `xml:"workbookViewId,attr"`
Pane *xlsxPane `xml:"pane,omitempty"`
Selection []*xlsxSelection `xml:"selection"`
}
// xlsxSelection directly maps the selection element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Worksheet view
// selection.
type xlsxSelection struct {
ActiveCell string `xml:"activeCell,attr,omitempty"`
ActiveCellID *int `xml:"activeCellId,attr"`
Pane string `xml:"pane,attr,omitempty"`
SQRef string `xml:"sqref,attr,omitempty"`
}
// xlsxSelection directly maps the selection element. Worksheet view pane.
type xlsxPane struct {
ActivePane string `xml:"activePane,attr,omitempty"`
State string `xml:"state,attr,omitempty"` // Either "split" or "frozen"
TopLeftCell string `xml:"topLeftCell,attr,omitempty"`
XSplit float64 `xml:"xSplit,attr,omitempty"`
YSplit float64 `xml:"ySplit,attr,omitempty"`
}
// xlsxSheetPr directly maps the sheetPr element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Sheet-level
// properties.
type xlsxSheetPr struct {
XMLName xml.Name `xml:"sheetPr"`
SyncHorizontal bool `xml:"syncHorizontal,attr,omitempty"`
SyncVertical bool `xml:"syncVertical,attr,omitempty"`
SyncRef string `xml:"syncRef,attr,omitempty"`
TransitionEvaluation bool `xml:"transitionEvaluation,attr,omitempty"`
Published *bool `xml:"published,attr"`
CodeName string `xml:"codeName,attr,omitempty"`
FilterMode bool `xml:"filterMode,attr,omitempty"`
EnableFormatConditionsCalculation *bool `xml:"enableFormatConditionsCalculation,attr"`
TransitionEntry bool `xml:"transitionEntry,attr,omitempty"`
TabColor *xlsxTabColor `xml:"tabColor,omitempty"`
OutlinePr *xlsxOutlinePr `xml:"outlinePr,omitempty"`
PageSetUpPr *xlsxPageSetUpPr `xml:"pageSetUpPr,omitempty"`
}
// xlsxOutlinePr maps to the outlinePr element
// SummaryBelow allows you to adjust the direction of grouper controls
type xlsxOutlinePr struct {
SummaryBelow bool `xml:"summaryBelow,attr"`
}
// xlsxPageSetUpPr directly maps the pageSetupPr element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page setup
// properties of the worksheet.
type xlsxPageSetUpPr struct {
AutoPageBreaks bool `xml:"autoPageBreaks,attr,omitempty"`
FitToPage bool `xml:"fitToPage,attr,omitempty"` // Flag indicating whether the Fit to Page print option is enabled.
}
// xlsxTabColor directly maps the tabColor element in the namespace currently I
// have not checked it for completeness - it does as much as I need.
type xlsxTabColor struct {
RGB string `xml:"rgb,attr,omitempty"`
Theme int `xml:"theme,attr,omitempty"`
Tint float64 `xml:"tint,attr,omitempty"`
}
// xlsxCols directly maps the cols element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxCols struct {
XMLName xml.Name `xml:"cols"`
Col []xlsxCol `xml:"col"`
}
// xlsxCol directly maps the col (Column Width & Formatting). Defines column
// width and column formatting for one or more columns of the worksheet.
type xlsxCol struct {
BestFit bool `xml:"bestFit,attr,omitempty"`
Collapsed bool `xml:"collapsed,attr,omitempty"`
CustomWidth bool `xml:"customWidth,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
Max int `xml:"max,attr"`
Min int `xml:"min,attr"`
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
Phonetic bool `xml:"phonetic,attr,omitempty"`
Style int `xml:"style,attr,omitempty"`
Width float64 `xml:"width,attr,omitempty"`
}
// xlsxDimension directly maps the dimension element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - This element
// specifies the used range of the worksheet. It specifies the row and column
// bounds of used cells in the worksheet. This is optional and is not required.
// Used cells include cells with formulas, text content, and cell formatting.
// When an entire column is formatted, only the first cell in that column is
// considered used.
type xlsxDimension struct {
XMLName xml.Name `xml:"dimension"`
Ref string `xml:"ref,attr"`
}
// xlsxSheetData directly maps the sheetData element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxSheetData struct {
XMLName xml.Name `xml:"sheetData"`
Row []xlsxRow `xml:"row"`
}
// xlsxRow directly maps the row element. The element expresses information
// about an entire row of a worksheet, and contains all cell definitions for a
// particular row in the worksheet.
type xlsxRow struct {
Collapsed bool `xml:"collapsed,attr,omitempty"`
CustomFormat bool `xml:"customFormat,attr,omitempty"`
CustomHeight bool `xml:"customHeight,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
Ht float64 `xml:"ht,attr,omitempty"`
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
Ph bool `xml:"ph,attr,omitempty"`
R int `xml:"r,attr,omitempty"`
S int `xml:"s,attr,omitempty"`
Spans string `xml:"spans,attr,omitempty"`
ThickBot bool `xml:"thickBot,attr,omitempty"`
ThickTop bool `xml:"thickTop,attr,omitempty"`
C []xlsxC `xml:"c"`
}
// xlsxSortState directly maps the sortState element. This collection
// preserves the AutoFilter sort state.
type xlsxSortState struct {
ColumnSort bool `xml:"columnSort,attr,omitempty"`
CaseSensitive bool `xml:"caseSensitive,attr,omitempty"`
SortMethod string `xml:"sortMethod,attr,omitempty"`
Ref string `xml:"ref,attr"`
Content string `xml:",innerxml"`
}
// xlsxCustomSheetViews directly maps the customSheetViews element. This is a
// collection of custom sheet views.
type xlsxCustomSheetViews struct {
XMLName xml.Name `xml:"customSheetViews"`
CustomSheetView []*xlsxCustomSheetView `xml:"customSheetView"`
}
// xlsxBrk directly maps the row or column break to use when paginating a
// worksheet.
type xlsxBrk struct {
ID int `xml:"id,attr,omitempty"`
Min int `xml:"min,attr,omitempty"`
Max int `xml:"max,attr,omitempty"`
Man bool `xml:"man,attr,omitempty"`
Pt bool `xml:"pt,attr,omitempty"`
}
// xlsxBreaks directly maps a collection of the row or column breaks.
type xlsxBreaks struct {
Brk []*xlsxBrk `xml:"brk"`
Count int `xml:"count,attr,omitempty"`
ManualBreakCount int `xml:"manualBreakCount,attr,omitempty"`
}
// xlsxCustomSheetView directly maps the customSheetView element.
type xlsxCustomSheetView struct {
Pane *xlsxPane `xml:"pane"`
Selection *xlsxSelection `xml:"selection"`
RowBreaks *xlsxBreaks `xml:"rowBreaks"`
ColBreaks *xlsxBreaks `xml:"colBreaks"`
PageMargins *xlsxPageMargins `xml:"pageMargins"`
PrintOptions *xlsxPrintOptions `xml:"printOptions"`
PageSetup *xlsxPageSetUp `xml:"pageSetup"`
HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"`
AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
ExtLst *xlsxExtLst `xml:"extLst"`
GUID string `xml:"guid,attr"`
Scale int `xml:"scale,attr,omitempty"`
ColorID int `xml:"colorId,attr,omitempty"`
ShowPageBreaks bool `xml:"showPageBreaks,attr,omitempty"`
ShowFormulas bool `xml:"showFormulas,attr,omitempty"`
ShowGridLines bool `xml:"showGridLines,attr,omitempty"`
ShowRowCol bool `xml:"showRowCol,attr,omitempty"`
OutlineSymbols bool `xml:"outlineSymbols,attr,omitempty"`
ZeroValues bool `xml:"zeroValues,attr,omitempty"`
FitToPage bool `xml:"fitToPage,attr,omitempty"`
PrintArea bool `xml:"printArea,attr,omitempty"`
Filter bool `xml:"filter,attr,omitempty"`
ShowAutoFilter bool `xml:"showAutoFilter,attr,omitempty"`
HiddenRows bool `xml:"hiddenRows,attr,omitempty"`
HiddenColumns bool `xml:"hiddenColumns,attr,omitempty"`
State string `xml:"state,attr,omitempty"`
FilterUnique bool `xml:"filterUnique,attr,omitempty"`
View string `xml:"view,attr,omitempty"`
ShowRuler bool `xml:"showRuler,attr,omitempty"`
TopLeftCell string `xml:"topLeftCell,attr,omitempty"`
}
// xlsxMergeCell directly maps the mergeCell element. A single merged cell.
type xlsxMergeCell struct {
Ref string `xml:"ref,attr,omitempty"`
}
// xlsxMergeCells directly maps the mergeCells element. This collection
// expresses all the merged cells in the sheet.
type xlsxMergeCells struct {
XMLName xml.Name `xml:"mergeCells"`
Count int `xml:"count,attr,omitempty"`
Cells []*xlsxMergeCell `xml:"mergeCell,omitempty"`
}
// xlsxDataValidations expresses all data validation information for cells in a
// sheet which have data validation features applied.
type xlsxDataValidations struct {
XMLName xml.Name `xml:"dataValidations"`
Count int `xml:"count,attr,omitempty"`
DisablePrompts bool `xml:"disablePrompts,attr,omitempty"`
XWindow int `xml:"xWindow,attr,omitempty"`
YWindow int `xml:"yWindow,attr,omitempty"`
DataValidation []*DataValidation `xml:"dataValidation"`
}
// DataValidation directly maps the a single item of data validation defined
// on a range of the worksheet.
type DataValidation struct {
AllowBlank bool `xml:"allowBlank,attr"`
Error *string `xml:"error,attr"`
ErrorStyle *string `xml:"errorStyle,attr"`
ErrorTitle *string `xml:"errorTitle,attr"`
Operator string `xml:"operator,attr,omitempty"`
Prompt *string `xml:"prompt,attr"`
PromptTitle *string `xml:"promptTitle,attr"`
ShowDropDown bool `xml:"showDropDown,attr,omitempty"`
ShowErrorMessage bool `xml:"showErrorMessage,attr,omitempty"`
ShowInputMessage bool `xml:"showInputMessage,attr,omitempty"`
Sqref string `xml:"sqref,attr"`
Type string `xml:"type,attr,omitempty"`
Formula1 string `xml:",innerxml"`
Formula2 string `xml:",innerxml"`
}
// xlsxC directly maps the c element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
//
// This simple type is restricted to the values listed in the following table:
//
// Enumeration Value | Description
// ---------------------------+---------------------------------
// b (Boolean) | Cell containing a boolean.
// d (Date) | Cell contains a date in the ISO 8601 format.
// e (Error) | Cell containing an error.
// inlineStr (Inline String) | Cell containing an (inline) rich string, i.e., one not in the shared string table. If this cell type is used, then the cell value is in the is element rather than the v element in the cell (c element).
// n (Number) | Cell containing a number.
// s (Shared String) | Cell containing a shared string.
// str (String) | Cell containing a formula string.
//
type xlsxC struct {
XMLName xml.Name `xml:"c"`
XMLSpace xml.Attr `xml:"space,attr,omitempty"`
R string `xml:"r,attr,omitempty"` // Cell ID, e.g. A1
S int `xml:"s,attr,omitempty"` // Style reference.
// Str string `xml:"str,attr,omitempty"` // Style reference.
T string `xml:"t,attr,omitempty"` // Type.
F *xlsxF `xml:"f,omitempty"` // Formula
V string `xml:"v,omitempty"` // Value
IS *xlsxSI `xml:"is"`
}
func (c *xlsxC) hasValue() bool {
return c.S != 0 || c.V != "" || c.F != nil || c.T != ""
}
// xlsxF directly maps the f element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxF struct {
Content string `xml:",chardata"`
T string `xml:"t,attr,omitempty"` // Formula type
Ref string `xml:"ref,attr,omitempty"` // Shared formula ref
Si string `xml:"si,attr,omitempty"` // Shared formula index
}
// xlsxSheetProtection collection expresses the sheet protection options to
// enforce when the sheet is protected.
type xlsxSheetProtection struct {
XMLName xml.Name `xml:"sheetProtection"`
AlgorithmName string `xml:"algorithmName,attr,omitempty"`
Password string `xml:"password,attr,omitempty"`
HashValue string `xml:"hashValue,attr,omitempty"`
SaltValue string `xml:"saltValue,attr,omitempty"`
SpinCount int `xml:"spinCount,attr,omitempty"`
Sheet bool `xml:"sheet,attr"`
Objects bool `xml:"objects,attr"`
Scenarios bool `xml:"scenarios,attr"`
FormatCells bool `xml:"formatCells,attr"`
FormatColumns bool `xml:"formatColumns,attr"`
FormatRows bool `xml:"formatRows,attr"`
InsertColumns bool `xml:"insertColumns,attr"`
InsertRows bool `xml:"insertRows,attr"`
InsertHyperlinks bool `xml:"insertHyperlinks,attr"`
DeleteColumns bool `xml:"deleteColumns,attr"`
DeleteRows bool `xml:"deleteRows,attr"`
SelectLockedCells bool `xml:"selectLockedCells,attr"`
Sort bool `xml:"sort,attr"`
AutoFilter bool `xml:"autoFilter,attr"`
PivotTables bool `xml:"pivotTables,attr"`
SelectUnlockedCells bool `xml:"selectUnlockedCells,attr"`
}
// xlsxPhoneticPr (Phonetic Properties) represents a collection of phonetic
// properties that affect the display of phonetic text for this String Item
// (si). Phonetic text is used to give hints as to the pronunciation of an East
// Asian language, and the hints are displayed as text within the spreadsheet
// cells across the top portion of the cell. Since the phonetic hints are text,
// every phonetic hint is expressed as a phonetic run (rPh), and these
// properties specify how to display that phonetic run.
type xlsxPhoneticPr struct {
XMLName xml.Name `xml:"phoneticPr"`
Alignment string `xml:"alignment,attr,omitempty"`
FontID *int `xml:"fontId,attr"`
Type string `xml:"type,attr,omitempty"`
}
// A Conditional Format is a format, such as cell shading or font color, that a
// spreadsheet application can automatically apply to cells if a specified
// condition is true. This collection expresses conditional formatting rules
// applied to a particular cell or range.
type xlsxConditionalFormatting struct {
XMLName xml.Name `xml:"conditionalFormatting"`
SQRef string `xml:"sqref,attr,omitempty"`
CfRule []*xlsxCfRule `xml:"cfRule"`
}
// xlsxCfRule (Conditional Formatting Rule) represents a description of a
// conditional formatting rule.
type xlsxCfRule struct {
AboveAverage *bool `xml:"aboveAverage,attr"`
Bottom bool `xml:"bottom,attr,omitempty"`
DxfID *int `xml:"dxfId,attr"`
EqualAverage bool `xml:"equalAverage,attr,omitempty"`
Operator string `xml:"operator,attr,omitempty"`
Percent bool `xml:"percent,attr,omitempty"`
Priority int `xml:"priority,attr,omitempty"`
Rank int `xml:"rank,attr,omitempty"`
StdDev int `xml:"stdDev,attr,omitempty"`
StopIfTrue bool `xml:"stopIfTrue,attr,omitempty"`
Text string `xml:"text,attr,omitempty"`
TimePeriod string `xml:"timePeriod,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Formula []string `xml:"formula,omitempty"`
ColorScale *xlsxColorScale `xml:"colorScale"`
DataBar *xlsxDataBar `xml:"dataBar"`
IconSet *xlsxIconSet `xml:"iconSet"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxColorScale (Color Scale) describes a gradated color scale in this
// conditional formatting rule.
type xlsxColorScale struct {
Cfvo []*xlsxCfvo `xml:"cfvo"`
Color []*xlsxColor `xml:"color"`
}
// dataBar (Data Bar) describes a data bar conditional formatting rule.
type xlsxDataBar struct {
MaxLength int `xml:"maxLength,attr,omitempty"`
MinLength int `xml:"minLength,attr,omitempty"`
ShowValue bool `xml:"showValue,attr,omitempty"`
Cfvo []*xlsxCfvo `xml:"cfvo"`
Color []*xlsxColor `xml:"color"`
}
// xlsxIconSet (Icon Set) describes an icon set conditional formatting rule.
type xlsxIconSet struct {
Cfvo []*xlsxCfvo `xml:"cfvo"`
IconSet string `xml:"iconSet,attr,omitempty"`
ShowValue bool `xml:"showValue,attr,omitempty"`
Percent bool `xml:"percent,attr,omitempty"`
Reverse bool `xml:"reverse,attr,omitempty"`
}
// cfvo (Conditional Format Value Object) describes the values of the
// interpolation points in a gradient scale.
type xlsxCfvo struct {
Gte bool `xml:"gte,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Val string `xml:"val,attr,omitempty"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxHyperlinks directly maps the hyperlinks element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - A hyperlink can
// be stored in a package as a relationship. Hyperlinks shall be identified by
// containing a target which specifies the destination of the given hyperlink.
type xlsxHyperlinks struct {
XMLName xml.Name `xml:"hyperlinks"`
Hyperlink []xlsxHyperlink `xml:"hyperlink"`
}
// xlsxHyperlink directly maps the hyperlink element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main
type xlsxHyperlink struct {
Ref string `xml:"ref,attr"`
Location string `xml:"location,attr,omitempty"`
Display string `xml:"display,attr,omitempty"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxTableParts directly maps the tableParts element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - The table element
// has several attributes applied to identify the table and the data range it
// covers. The table id attribute needs to be unique across all table parts, the
// same goes for the name and displayName. The displayName has the further
// restriction that it must be unique across all defined names in the workbook.
// Later on we will see that you can define names for many elements, such as
// cells or formulas. The name value is used for the object model in Microsoft
// Office Excel. The displayName is used for references in formulas. The ref
// attribute is used to identify the cell range that the table covers. This
// includes not only the table data, but also the table header containing column
// names.
// To add columns to your table you add new tableColumn elements to the
// tableColumns container. Similar to the shared string table the collection
// keeps a count attribute identifying the number of columns. Besides the table
// definition in the table part there is also the need to identify which tables
// are displayed in the worksheet. The worksheet part has a separate element
// tableParts to store this information. Each table part is referenced through
// the relationship ID and again a count of the number of table parts is
// maintained. The following markup sample is taken from the documents
// accompanying this book. The sheet data element has been removed to reduce the
// size of the sample. To reference the table, just add the tableParts element,
// of course after having created and stored the table part. For example:
//
// <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
// ...
// <tableParts count="1">
// <tablePart r:id="rId1" />
// </tableParts>
// </worksheet>
//
type xlsxTableParts struct {
XMLName xml.Name `xml:"tableParts"`
Count int `xml:"count,attr,omitempty"`
TableParts []*xlsxTablePart `xml:"tablePart"`
}
// xlsxTablePart directly maps the tablePart element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main
type xlsxTablePart struct {
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxPicture directly maps the picture element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Background sheet
// image. For example:
//
// <picture r:id="rId1"/>
//
type xlsxPicture struct {
XMLName xml.Name `xml:"picture"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxLegacyDrawing directly maps the legacyDrawing element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - A comment is a
// rich text note that is attached to, and associated with, a cell, separate
// from other cell content. Comment content is stored separate from the cell,
// and is displayed in a drawing object (like a text box) that is separate from,
// but associated with, a cell. Comments are used as reminders, such as noting
// how a complex formula works, or to provide feedback to other users. Comments
// can also be used to explain assumptions made in a formula or to call out
// something special about the cell.
type xlsxLegacyDrawing struct {
XMLName xml.Name `xml:"legacyDrawing"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
// xlsxLegacyDrawingHF specifies the explicit relationship to the part
// containing the VML defining pictures rendered in the header / footer of the
// sheet.
type xlsxLegacyDrawingHF struct {
XMLName xml.Name `xml:"legacyDrawingHF"`
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
type xlsxInnerXML struct {
Content string `xml:",innerxml"`
}
// xlsxWorksheetExt directly maps the ext element in the worksheet.
type xlsxWorksheetExt struct {
XMLName xml.Name `xml:"ext"`
URI string `xml:"uri,attr"`
Content string `xml:",innerxml"`
}
// decodeWorksheetExt directly maps the ext element.
type decodeWorksheetExt struct {
XMLName xml.Name `xml:"extLst"`
Ext []*xlsxWorksheetExt `xml:"ext"`
}
// decodeX14SparklineGroups directly maps the sparklineGroups element.
type decodeX14SparklineGroups struct {
XMLName xml.Name `xml:"sparklineGroups"`
XMLNSXM string `xml:"xmlns:xm,attr"`
Content string `xml:",innerxml"`
}
// xlsxX14SparklineGroups directly maps the sparklineGroups element.
type xlsxX14SparklineGroups struct {
XMLName xml.Name `xml:"x14:sparklineGroups"`
XMLNSXM string `xml:"xmlns:xm,attr"`
SparklineGroups []*xlsxX14SparklineGroup `xml:"x14:sparklineGroup"`
Content string `xml:",innerxml"`
}
// xlsxX14SparklineGroup directly maps the sparklineGroup element.
type xlsxX14SparklineGroup struct {
XMLName xml.Name `xml:"x14:sparklineGroup"`
ManualMax int `xml:"manualMax,attr,omitempty"`
ManualMin int `xml:"manualMin,attr,omitempty"`
LineWeight float64 `xml:"lineWeight,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
DateAxis bool `xml:"dateAxis,attr,omitempty"`
DisplayEmptyCellsAs string `xml:"displayEmptyCellsAs,attr,omitempty"`
Markers bool `xml:"markers,attr,omitempty"`
High bool `xml:"high,attr,omitempty"`
Low bool `xml:"low,attr,omitempty"`
First bool `xml:"first,attr,omitempty"`
Last bool `xml:"last,attr,omitempty"`
Negative bool `xml:"negative,attr,omitempty"`
DisplayXAxis bool `xml:"displayXAxis,attr,omitempty"`
DisplayHidden bool `xml:"displayHidden,attr,omitempty"`
MinAxisType string `xml:"minAxisType,attr,omitempty"`
MaxAxisType string `xml:"maxAxisType,attr,omitempty"`
RightToLeft bool `xml:"rightToLeft,attr,omitempty"`
ColorSeries *xlsxTabColor `xml:"x14:colorSeries"`
ColorNegative *xlsxTabColor `xml:"x14:colorNegative"`
ColorAxis *xlsxColor `xml:"x14:colorAxis"`
ColorMarkers *xlsxTabColor `xml:"x14:colorMarkers"`
ColorFirst *xlsxTabColor `xml:"x14:colorFirst"`
ColorLast *xlsxTabColor `xml:"x14:colorLast"`
ColorHigh *xlsxTabColor `xml:"x14:colorHigh"`
ColorLow *xlsxTabColor `xml:"x14:colorLow"`
Sparklines xlsxX14Sparklines `xml:"x14:sparklines"`
}
// xlsxX14Sparklines directly maps the sparklines element.
type xlsxX14Sparklines struct {
Sparkline []*xlsxX14Sparkline `xml:"x14:sparkline"`
}
// xlsxX14Sparkline directly maps the sparkline element.
type xlsxX14Sparkline struct {
F string `xml:"xm:f"`
Sqref string `xml:"xm:sqref"`
}
// SparklineOption directly maps the settings of the sparkline.
type SparklineOption struct {
Location []string
Range []string
Max int
CustMax int
Min int
CustMin int
Type string
Weight float64
DateAxis bool
Markers bool
High bool
Low bool
First bool
Last bool
Negative bool
Axis bool
Hidden bool
Reverse bool
Style int
SeriesColor string
NegativeColor string
MarkersColor string
FirstColor string
LastColor string
HightColor string
LowColor string
EmptyCells string
}
// formatPanes directly maps the settings of the panes.
type formatPanes struct {
Freeze bool `json:"freeze"`
Split bool `json:"split"`
XSplit int `json:"x_split"`
YSplit int `json:"y_split"`
TopLeftCell string `json:"top_left_cell"`
ActivePane string `json:"active_pane"`
Panes []struct {
SQRef string `json:"sqref"`
ActiveCell string `json:"active_cell"`
Pane string `json:"pane"`
} `json:"panes"`
}
// formatConditional directly maps the conditional format settings of the cells.
type formatConditional struct {
Type string `json:"type"`
AboveAverage bool `json:"above_average"`
Percent bool `json:"percent"`
Format int `json:"format"`
Criteria string `json:"criteria"`
Value string `json:"value,omitempty"`
Minimum string `json:"minimum,omitempty"`
Maximum string `json:"maximum,omitempty"`
MinType string `json:"min_type,omitempty"`
MidType string `json:"mid_type,omitempty"`
MaxType string `json:"max_type,omitempty"`
MinValue string `json:"min_value,omitempty"`
MidValue string `json:"mid_value,omitempty"`
MaxValue string `json:"max_value,omitempty"`
MinColor string `json:"min_color,omitempty"`
MidColor string `json:"mid_color,omitempty"`
MaxColor string `json:"max_color,omitempty"`
MinLength string `json:"min_length,omitempty"`
MaxLength string `json:"max_length,omitempty"`
MultiRange string `json:"multi_range,omitempty"`
BarColor string `json:"bar_color,omitempty"`
}
// FormatSheetProtection directly maps the settings of worksheet protection.
type FormatSheetProtection struct {
AutoFilter bool
DeleteColumns bool
DeleteRows bool
EditObjects bool
EditScenarios bool
FormatCells bool
FormatColumns bool
FormatRows bool
InsertColumns bool
InsertHyperlinks bool
InsertRows bool
Password string
PivotTables bool
SelectLockedCells bool
SelectUnlockedCells bool
Sort bool
}
// FormatHeaderFooter directly maps the settings of header and footer.
type FormatHeaderFooter struct {
AlignWithMargins bool
DifferentFirst bool
DifferentOddEven bool
ScaleWithDoc bool
OddHeader string
OddFooter string
EvenHeader string
EvenFooter string
FirstFooter string
FirstHeader string
}
// FormatPageMargins directly maps the settings of page margins
type FormatPageMargins struct {
Bottom string
Footer string
Header string
Left string
Right string
Top string
}

26
vendor/github.com/mohae/deepcopy/.gitignore generated vendored Normal file
View file

@ -0,0 +1,26 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*~
*.out
*.log

11
vendor/github.com/mohae/deepcopy/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,11 @@
language: go
go:
- tip
matrix:
allow_failures:
- go: tip
script:
- go test ./...

21
vendor/github.com/mohae/deepcopy/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Joel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

8
vendor/github.com/mohae/deepcopy/README.md generated vendored Normal file
View file

@ -0,0 +1,8 @@
deepCopy
========
[![GoDoc](https://godoc.org/github.com/mohae/deepcopy?status.svg)](https://godoc.org/github.com/mohae/deepcopy)[![Build Status](https://travis-ci.org/mohae/deepcopy.png)](https://travis-ci.org/mohae/deepcopy)
DeepCopy makes deep copies of things: unexported field values are not copied.
## Usage
cpy := deepcopy.Copy(orig)

125
vendor/github.com/mohae/deepcopy/deepcopy.go generated vendored Normal file
View file

@ -0,0 +1,125 @@
// deepcopy makes deep copies of things. A standard copy will copy the
// pointers: deep copy copies the values pointed to. Unexported field
// values are not copied.
//
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
// License: MIT, for more details check the included LICENSE file.
package deepcopy
import (
"reflect"
"time"
)
// Interface for delegating copy process to type
type Interface interface {
DeepCopy() interface{}
}
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
func Iface(iface interface{}) interface{} {
return Copy(iface)
}
// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}. The returned value will need to be asserted to the
// correct type.
func Copy(src interface{}) interface{} {
if src == nil {
return nil
}
// Make the interface a reflect.Value
original := reflect.ValueOf(src)
// Make a copy of the same type as the original.
cpy := reflect.New(original.Type()).Elem()
// Recursively copy the original.
copyRecursive(original, cpy)
// Return the copy as an interface.
return cpy.Interface()
}
// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
// check for implement deepcopy.Interface
if original.CanInterface() {
if copier, ok := original.Interface().(Interface); ok {
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
return
}
}
// handle according to original's Kind
switch original.Kind() {
case reflect.Ptr:
// Get the actual value being pointed to.
originalValue := original.Elem()
// if it isn't valid, return.
if !originalValue.IsValid() {
return
}
cpy.Set(reflect.New(originalValue.Type()))
copyRecursive(originalValue, cpy.Elem())
case reflect.Interface:
// If this is a nil, don't do anything
if original.IsNil() {
return
}
// Get the value for the interface, not the pointer.
originalValue := original.Elem()
// Get the value by calling Elem().
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
cpy.Set(copyValue)
case reflect.Struct:
t, ok := original.Interface().(time.Time)
if ok {
cpy.Set(reflect.ValueOf(t))
return
}
// Go through each field of the struct and copy it.
for i := 0; i < original.NumField(); i++ {
// The Type's StructField for a given field is checked to see if StructField.PkgPath
// is set to determine if the field is exported or not because CanSet() returns false
// for settable fields. I'm not sure why. -mohae
if original.Type().Field(i).PkgPath != "" {
continue
}
copyRecursive(original.Field(i), cpy.Field(i))
}
case reflect.Slice:
if original.IsNil() {
return
}
// Make a new slice and copy each element.
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
for i := 0; i < original.Len(); i++ {
copyRecursive(original.Index(i), cpy.Index(i))
}
case reflect.Map:
if original.IsNil() {
return
}
cpy.Set(reflect.MakeMap(original.Type()))
for _, key := range original.MapKeys() {
originalValue := original.MapIndex(key)
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
copyKey := Copy(key.Interface())
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
}
default:
cpy.Set(original)
}
}

46
vendor/github.com/xuri/efp/CODE_OF_CONDUCT.md generated vendored Normal file
View file

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xuri.me](https://xuri.me). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

28
vendor/github.com/xuri/efp/LICENSE generated vendored Normal file
View file

@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2017 Ri Xu All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of efp nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

45
vendor/github.com/xuri/efp/PULL_REQUEST_TEMPLATE.md generated vendored Normal file
View file

@ -0,0 +1,45 @@
# PR Details
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
## How Has This Been Tested
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Docs change / refactoring / dependency upgrade
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

58
vendor/github.com/xuri/efp/README.md generated vendored Normal file
View file

@ -0,0 +1,58 @@
# EFP (Excel Formula Parser)
[![Build Status](https://github.com/xuri/efp/workflows/Go/badge.svg)](https://github.com/xuri/efp/actions?workflow=Go)
[![Code Coverage](https://codecov.io/gh/xuri/efp/branch/master/graph/badge.svg)](https://codecov.io/gh/xuri/efp)
[![Go Report Card](https://goreportcard.com/badge/github.com/xuri/efp)](https://goreportcard.com/report/github.com/xuri/efp)
[![GoDoc](https://godoc.org/github.com/xuri/efp?status.svg)](https://godoc.org/github.com/xuri/efp)
[![Licenses](https://img.shields.io/badge/license-bsd-orange.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fxuri%2Fefp.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fxuri%2Fefp?ref=badge_shield)
Using EFP (Excel Formula Parser) you can get an Abstract Syntax Tree (AST) from Excel formula.
## Installation
```go
go get github.com/xuri/efp
```
## Example
```go
package main
import "github.com/xuri/efp"
func main() {
ps := efp.ExcelParser()
ps.Parse("=SUM(A3+B9*2)/2")
println(ps.PrettyPrint())
}
```
Get AST
```
SUM <Function> <Start>
A3 <Operand> <Range>
+ <OperatorInfix> <Math>
B9 <Operand> <Range>
* <OperatorInfix> <Math>
2 <Operand> <Number>
<Function> <Stop>
/ <OperatorInfix> <Math>
2 <Operand> <Number>
```
## Contributing
Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change.
## Credits
EFP (Excel Formula Parser) is a Golang port of [E. W. Bachtal's](http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html) Excel formula parser.
## Licenses
This program is under the terms of the BSD 3-Clause License. See [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause).
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fxuri%2Fefp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fxuri%2Fefp?ref=badge_large)

9
vendor/github.com/xuri/efp/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
We will dive into any security-related issue as long as your efp version is still supported by us. When reporting an issue, include as much information as possible, but no need to fill fancy forms or answer tedious questions. Just tell us what you found, how to reproduce it, and any concerns you have about it. We will respond as soon as possible and follow up with any missing information.
## Reporting a Vulnerability
Please e-mail us directly at `xuri.me@gmail.com` or use the security issue template on GitHub. In general, public disclosure is made after the issue has been fully identified and a patch is ready to be released. A security issue gets the highest priority assigned and a reply regarding the vulnerability is given within a typical 24 hours. Thank you!

3
vendor/github.com/xuri/efp/codelingo.yaml generated vendored Normal file
View file

@ -0,0 +1,3 @@
tenets:
- import: codelingo/effective-go
- import: codelingo/code-review-comments

643
vendor/github.com/xuri/efp/efp.go generated vendored Normal file
View file

@ -0,0 +1,643 @@
// Package efp (Excel Formula Parser) tokenise an Excel formula using an
// implementation of E. W. Bachtal's algorithm, found here:
// http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
//
// Golang version by Ri Xu: https://xuri.me
package efp
import (
"strconv"
"strings"
)
// QuoteDouble, QuoteSingle and other's constants are token definitions.
const (
// Character constants
QuoteDouble = "\""
QuoteSingle = "'"
BracketClose = "]"
BracketOpen = "["
BraceOpen = "{"
BraceClose = "}"
ParenOpen = "("
ParenClose = ")"
Semicolon = ";"
Whitespace = " "
Comma = ","
ErrorStart = "#"
OperatorsSN = "+-"
OperatorsInfix = "+-*/^&=><"
OperatorsPostfix = "%"
// Token type
TokenTypeNoop = "Noop"
TokenTypeOperand = "Operand"
TokenTypeFunction = "Function"
TokenTypeSubexpression = "Subexpression"
TokenTypeArgument = "Argument"
TokenTypeOperatorPrefix = "OperatorPrefix"
TokenTypeOperatorInfix = "OperatorInfix"
TokenTypeOperatorPostfix = "OperatorPostfix"
TokenTypeWhitespace = "Whitespace"
TokenTypeUnknown = "Unknown"
// Token subtypes
TokenSubTypeNothing = "Nothing"
TokenSubTypeStart = "Start"
TokenSubTypeStop = "Stop"
TokenSubTypeText = "Text"
TokenSubTypeNumber = "Number"
TokenSubTypeLogical = "Logical"
TokenSubTypeError = "Error"
TokenSubTypeRange = "Range"
TokenSubTypeMath = "Math"
TokenSubTypeConcatenation = "Concatenation"
TokenSubTypeIntersection = "Intersection"
TokenSubTypeUnion = "Union"
)
// Token encapsulate a formula token.
type Token struct {
TValue string
TType string
TSubType string
}
// Tokens directly maps the ordered list of tokens.
// Attributes:
//
// items - Ordered list
// index - Current position in the list
//
type Tokens struct {
Index int
Items []Token
}
// Parser inheritable container. TokenStack directly maps a LIFO stack of
// tokens.
type Parser struct {
Formula string
Tokens Tokens
TokenStack Tokens
Offset int
Token string
InString bool
InPath bool
InRange bool
InError bool
}
// fToken provides function to encapsulate a formula token.
func fToken(value, tokenType, subType string) Token {
return Token{
TValue: value,
TType: tokenType,
TSubType: subType,
}
}
// fTokens provides function to handle an ordered list of tokens.
func fTokens() Tokens {
return Tokens{
Index: -1,
}
}
// add provides function to add a token to the end of the list.
func (tk *Tokens) add(value, tokenType, subType string) Token {
token := fToken(value, tokenType, subType)
tk.addRef(token)
return token
}
// addRef provides function to add a token to the end of the list.
func (tk *Tokens) addRef(token Token) {
tk.Items = append(tk.Items, token)
}
// reset provides function to reset the index to -1.
func (tk *Tokens) reset() {
tk.Index = -1
}
// BOF provides function to check whether or not beginning of list.
func (tk *Tokens) BOF() bool {
return tk.Index <= 0
}
// EOF provides function to check whether or not end of list.
func (tk *Tokens) EOF() bool {
return tk.Index >= (len(tk.Items) - 1)
}
// moveNext provides function to move the index along one.
func (tk *Tokens) moveNext() bool {
if tk.EOF() {
return false
}
tk.Index++
return true
}
// current return the current token.
func (tk *Tokens) current() *Token {
if tk.Index == -1 {
return nil
}
return &tk.Items[tk.Index]
}
// next return the next token (leave the index unchanged).
func (tk *Tokens) next() *Token {
if tk.EOF() {
return nil
}
return &tk.Items[tk.Index+1]
}
// previous return the previous token (leave the index unchanged).
func (tk *Tokens) previous() *Token {
if tk.Index < 1 {
return nil
}
return &tk.Items[tk.Index-1]
}
// push provides function to push a token onto the stack.
func (tk *Tokens) push(token Token) {
tk.Items = append(tk.Items, token)
}
// pop provides function to pop a token off the stack.
func (tk *Tokens) pop() Token {
t := tk.Items[len(tk.Items)-1]
tk.Items = tk.Items[:len(tk.Items)-1]
return fToken("", t.TType, TokenSubTypeStop)
}
// token provides function to non-destructively return the top item on the
// stack.
func (tk *Tokens) token() *Token {
if len(tk.Items) > 0 {
return &tk.Items[len(tk.Items)-1]
}
return nil
}
// value return the top token's value.
func (tk *Tokens) value() string {
return tk.token().TValue
}
// tp return the top token's type.
func (tk *Tokens) tp() string {
return tk.token().TType
}
// subtype return the top token's subtype.
func (tk *Tokens) subtype() string {
return tk.token().TSubType
}
// ExcelParser provides function to parse an Excel formula into a stream of
// tokens.
func ExcelParser() Parser {
return Parser{}
}
// getTokens return a token stream (list).
func (ps *Parser) getTokens(formula string) Tokens {
ps.Formula = strings.TrimSpace(ps.Formula)
f := []rune(ps.Formula)
if len(f) > 0 {
if string(f[0]) != "=" {
ps.Formula = "=" + ps.Formula
}
}
// state-dependent character evaluation (order is important)
for !ps.EOF() {
// double-quoted strings
// embeds are doubled
// end marks token
if ps.InString {
if ps.currentChar() == "\"" {
if ps.nextChar() == "\"" {
ps.Token += "\""
ps.Offset++
} else {
ps.InString = false
ps.Tokens.add(ps.Token, TokenTypeOperand, TokenSubTypeText)
ps.Token = ""
}
} else {
ps.Token += ps.currentChar()
}
ps.Offset++
continue
}
// single-quoted strings (links)
// embeds are double
// end does not mark a token
if ps.InPath {
if ps.currentChar() == "'" {
if ps.nextChar() == "'" {
ps.Token += "'"
ps.Offset++
} else {
ps.InPath = false
}
} else {
ps.Token += ps.currentChar()
}
ps.Offset++
continue
}
// bracketed strings (range offset or linked workbook name)
// no embeds (changed to "()" by Excel)
// end does not mark a token
if ps.InError {
if ps.currentChar() == "]" {
ps.InRange = false
}
ps.Token += ps.currentChar()
ps.Offset++
continue
}
// error values
// end marks a token, determined from absolute list of values
if ps.InError {
ps.Token += ps.currentChar()
ps.Offset++
errors := map[string]string{",#NULL!,": "", ",#DIV/0!,": "", ",#VALUE!,": "", ",#REF!,": "", ",#NAME?,": "", ",#NUM!,": "", ",#N/A,": ""}
_, ok := errors[","+ps.Token+","]
if ok {
ps.InError = false
ps.Tokens.add(ps.Token, TokenTypeOperand, TokenSubTypeError)
ps.Token = ""
}
continue
}
// independent character evaluation (order not important)
// establish state-dependent character evaluations
if ps.currentChar() == "\"" {
if len(ps.Token) > 0 {
// not expected
ps.Tokens.add(ps.Token, TokenTypeUnknown, "")
ps.Token = ""
}
ps.InString = true
ps.Offset++
continue
}
if ps.currentChar() == "'" {
if len(ps.Token) > 0 {
// not expected
ps.Tokens.add(ps.Token, TokenTypeUnknown, "")
ps.Token = ""
}
ps.InPath = true
ps.Offset++
continue
}
if ps.currentChar() == "[" {
ps.InRange = true
ps.Token += ps.currentChar()
ps.Offset++
continue
}
if ps.currentChar() == "#" {
if len(ps.Token) > 0 {
// not expected
ps.Tokens.add(ps.Token, TokenTypeUnknown, "")
ps.Token = ""
}
ps.InError = true
ps.Token += ps.currentChar()
ps.Offset++
continue
}
// mark start and end of arrays and array rows
if ps.currentChar() == "{" {
if len(ps.Token) > 0 {
// not expected
ps.Tokens.add(ps.Token, TokenTypeUnknown, "")
ps.Token = ""
}
ps.TokenStack.push(ps.Tokens.add("ARRAY", TokenTypeFunction, TokenSubTypeStart))
ps.TokenStack.push(ps.Tokens.add("ARRAYROW", TokenTypeFunction, TokenSubTypeStart))
ps.Offset++
continue
}
if ps.currentChar() == ";" {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.addRef(ps.TokenStack.pop())
ps.Tokens.add(",", TokenTypeArgument, "")
ps.TokenStack.push(ps.Tokens.add("ARRAYROW", TokenTypeFunction, TokenSubTypeStart))
ps.Offset++
continue
}
if ps.currentChar() == "}" {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.addRef(ps.TokenStack.pop())
ps.Tokens.addRef(ps.TokenStack.pop())
ps.Offset++
continue
}
// trim white-space
if ps.currentChar() == " " {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.add("", TokenTypeWhitespace, "")
ps.Offset++
for (ps.currentChar() == " ") && (!ps.EOF()) {
ps.Offset++
}
continue
}
// multi-character comparators
comparators := map[string]string{",>=,": "", ",<=,": "", ",<>,": ""}
_, ok := comparators[","+ps.doubleChar()+","]
if ok {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.add(ps.doubleChar(), TokenTypeOperatorInfix, TokenSubTypeLogical)
ps.Offset += 2
continue
}
// standard infix operators
operators := map[string]string{"+": "", "-": "", "*": "", "/": "", "^": "", "&": "", "=": "", ">": "", "<": ""}
_, ok = operators[ps.currentChar()]
if ok {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.add(ps.currentChar(), TokenTypeOperatorInfix, "")
ps.Offset++
continue
}
// standard postfix operators
if ps.currentChar() == "%" {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.add(ps.currentChar(), TokenTypeOperatorPostfix, "")
ps.Offset++
continue
}
// start subexpression or function
if ps.currentChar() == "(" {
if len(ps.Token) > 0 {
ps.TokenStack.push(ps.Tokens.add(ps.Token, TokenTypeFunction, TokenSubTypeStart))
ps.Token = ""
} else {
ps.TokenStack.push(ps.Tokens.add("", TokenTypeSubexpression, TokenSubTypeStart))
}
ps.Offset++
continue
}
// function, subexpression, array parameters
if ps.currentChar() == "," {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
if ps.TokenStack.tp() != TokenTypeFunction {
ps.Tokens.add(ps.currentChar(), TokenTypeOperatorInfix, TokenSubTypeUnion)
} else {
ps.Tokens.add(ps.currentChar(), TokenTypeArgument, "")
}
ps.Offset++
continue
}
// stop subexpression
if ps.currentChar() == ")" {
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
ps.Token = ""
}
ps.Tokens.addRef(ps.TokenStack.pop())
ps.Offset++
continue
}
// token accumulation
ps.Token += ps.currentChar()
ps.Offset++
}
// dump remaining accumulation
if len(ps.Token) > 0 {
ps.Tokens.add(ps.Token, TokenTypeOperand, "")
}
// move all tokens to a new collection, excluding all unnecessary white-space tokens
tokens2 := fTokens()
for ps.Tokens.moveNext() {
token := ps.Tokens.current()
if token.TType == TokenTypeWhitespace {
if ps.Tokens.BOF() || ps.Tokens.EOF() {
} else if !(((ps.Tokens.previous().TType == TokenTypeFunction) && (ps.Tokens.previous().TSubType == TokenSubTypeStop)) || ((ps.Tokens.previous().TType == TokenTypeSubexpression) && (ps.Tokens.previous().TSubType == TokenSubTypeStop)) || (ps.Tokens.previous().TType == TokenTypeOperand)) {
} else if !(((ps.Tokens.next().TType == TokenTypeFunction) && (ps.Tokens.next().TSubType == TokenSubTypeStart)) || ((ps.Tokens.next().TType == TokenTypeSubexpression) && (ps.Tokens.next().TSubType == TokenSubTypeStart)) || (ps.Tokens.next().TType == TokenTypeOperand)) {
} else {
tokens2.add(token.TValue, TokenTypeOperatorInfix, TokenSubTypeIntersection)
}
continue
}
tokens2.addRef(Token{
TValue: token.TValue,
TType: token.TType,
TSubType: token.TSubType,
})
}
// switch infix "-" operator to prefix when appropriate, switch infix "+"
// operator to noop when appropriate, identify operand and infix-operator
// subtypes, pull "@" from in front of function names
for tokens2.moveNext() {
token := tokens2.current()
if (token.TType == TokenTypeOperatorInfix) && (token.TValue == "-") {
if tokens2.BOF() {
token.TType = TokenTypeOperatorPrefix
} else if ((tokens2.previous().TType == TokenTypeFunction) && (tokens2.previous().TSubType == TokenSubTypeStop)) || ((tokens2.previous().TType == TokenTypeSubexpression) && (tokens2.previous().TSubType == TokenSubTypeStop)) || (tokens2.previous().TType == TokenTypeOperatorPostfix) || (tokens2.previous().TType == TokenTypeOperand) {
token.TSubType = TokenSubTypeMath
} else {
token.TType = TokenTypeOperatorPrefix
}
continue
}
if (token.TType == TokenTypeOperatorInfix) && (token.TValue == "+") {
if tokens2.BOF() {
token.TType = TokenTypeNoop
} else if (tokens2.previous().TType == TokenTypeFunction) && (tokens2.previous().TSubType == TokenSubTypeStop) || ((tokens2.previous().TType == TokenTypeSubexpression) && (tokens2.previous().TSubType == TokenSubTypeStop) || (tokens2.previous().TType == TokenTypeOperatorPostfix) || (tokens2.previous().TType == TokenTypeOperand)) {
token.TSubType = TokenSubTypeMath
} else {
token.TType = TokenTypeNoop
}
continue
}
if (token.TType == TokenTypeOperatorInfix) && (len(token.TSubType) == 0) {
op := map[string]string{"<": "", ">": "", "=": ""}
_, ok := op[token.TValue[0:1]]
if ok {
token.TSubType = TokenSubTypeLogical
} else if token.TValue == "&" {
token.TSubType = TokenSubTypeConcatenation
} else {
token.TSubType = TokenSubTypeMath
}
continue
}
if (token.TType == TokenTypeOperand) && (len(token.TSubType) == 0) {
if _, err := strconv.ParseFloat(token.TValue, 64); err != nil {
if (token.TValue == "TRUE") || (token.TValue == "FALSE") {
token.TSubType = TokenSubTypeLogical
} else {
token.TSubType = TokenSubTypeRange
}
} else {
token.TSubType = TokenSubTypeNumber
}
continue
}
if token.TType == TokenTypeFunction {
if (len(token.TValue) > 0) && token.TValue[0:1] == "@" {
token.TValue = token.TValue[1:]
}
continue
}
}
tokens2.reset()
// move all tokens to a new collection, excluding all noops
tokens := fTokens()
for tokens2.moveNext() {
if tokens2.current().TType != TokenTypeNoop {
tokens.addRef(Token{
TValue: tokens2.current().TValue,
TType: tokens2.current().TType,
TSubType: tokens2.current().TSubType,
})
}
}
tokens.reset()
return tokens
}
// doubleChar provides function to get two characters after the current
// position.
func (ps *Parser) doubleChar() string {
if len([]rune(ps.Formula)) >= ps.Offset+2 {
return string([]rune(ps.Formula)[ps.Offset : ps.Offset+2])
}
return ""
}
// currentChar provides function to get the character of the current position.
func (ps *Parser) currentChar() string {
return string([]rune(ps.Formula)[ps.Offset])
}
// nextChar provides function to get the next character of the current position.
func (ps *Parser) nextChar() string {
if len([]rune(ps.Formula)) >= ps.Offset+1 {
return ""
}
return string([]rune(ps.Formula)[ps.Offset+1])
}
// EOF provides function to check whether or not end of tokens stack.
func (ps *Parser) EOF() bool {
return ps.Offset >= len([]rune(ps.Formula))
}
// Parse provides function to parse formula as a token stream (list).
func (ps *Parser) Parse(formula string) []Token {
ps.Formula = formula
ps.Tokens = ps.getTokens(formula)
return ps.Tokens.Items
}
// PrettyPrint provides function to pretty the parsed result with the indented
// format.
func (ps *Parser) PrettyPrint() string {
indent := 0
output := ""
for _, t := range ps.Tokens.Items {
if t.TSubType == TokenSubTypeStop {
indent--
}
for i := 0; i < indent; i++ {
output += "\t"
}
output += t.TValue + " <" + t.TType + "> <" + t.TSubType + ">" + "\n"
if t.TSubType == TokenSubTypeStart {
indent++
}
}
return output
}
// Render provides function to get formatted formula after parsed.
func (ps *Parser) Render() string {
output := ""
for _, t := range ps.Tokens.Items {
if t.TType == TokenTypeFunction && t.TSubType == TokenSubTypeStart {
output += t.TValue + "("
} else if t.TType == TokenTypeFunction && t.TSubType == TokenSubTypeStop {
output += ")"
} else if t.TType == TokenTypeSubexpression && t.TSubType == TokenSubTypeStart {
output += "("
} else if t.TType == TokenTypeSubexpression && t.TSubType == TokenSubTypeStop {
output += ")"
} else if t.TType == TokenTypeOperand && t.TSubType == TokenSubTypeText {
output += "\"" + t.TValue + "\""
} else if t.TType == TokenTypeOperatorInfix && t.TSubType == TokenSubTypeIntersection {
output += " "
} else {
output += t.TValue
}
}
return output
}

3
vendor/golang.org/x/net/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/golang.org/x/net/CONTRIBUTORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/net/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/net/PATENTS generated vendored Normal file
View file

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

78
vendor/golang.org/x/net/html/atom/atom.go generated vendored Normal file
View file

@ -0,0 +1,78 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package atom provides integer codes (also known as atoms) for a fixed set of
// frequently occurring HTML strings: tag names and attribute keys such as "p"
// and "id".
//
// Sharing an atom's name between all elements with the same tag can result in
// fewer string allocations when tokenizing and parsing HTML. Integer
// comparisons are also generally faster than string comparisons.
//
// The value of an atom's particular code is not guaranteed to stay the same
// between versions of this package. Neither is any ordering guaranteed:
// whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to
// be dense. The only guarantees are that e.g. looking up "div" will yield
// atom.Div, calling atom.Div.String will return "div", and atom.Div != 0.
package atom // import "golang.org/x/net/html/atom"
// Atom is an integer code for a string. The zero value maps to "".
type Atom uint32
// String returns the atom's name.
func (a Atom) String() string {
start := uint32(a >> 8)
n := uint32(a & 0xff)
if start+n > uint32(len(atomText)) {
return ""
}
return atomText[start : start+n]
}
func (a Atom) string() string {
return atomText[a>>8 : a>>8+a&0xff]
}
// fnv computes the FNV hash with an arbitrary starting value h.
func fnv(h uint32, s []byte) uint32 {
for i := range s {
h ^= uint32(s[i])
h *= 16777619
}
return h
}
func match(s string, t []byte) bool {
for i, c := range t {
if s[i] != c {
return false
}
}
return true
}
// Lookup returns the atom whose name is s. It returns zero if there is no
// such atom. The lookup is case sensitive.
func Lookup(s []byte) Atom {
if len(s) == 0 || len(s) > maxAtomLen {
return 0
}
h := fnv(hash0, s)
if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
return a
}
if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
return a
}
return 0
}
// String returns a string whose contents are equal to s. In that sense, it is
// equivalent to string(s) but may be more efficient.
func String(s []byte) string {
if a := Lookup(s); a != 0 {
return a.String()
}
return string(s)
}

783
vendor/golang.org/x/net/html/atom/table.go generated vendored Normal file
View file

@ -0,0 +1,783 @@
// Code generated by go generate gen.go; DO NOT EDIT.
//go:generate go run gen.go
package atom
const (
A Atom = 0x1
Abbr Atom = 0x4
Accept Atom = 0x1a06
AcceptCharset Atom = 0x1a0e
Accesskey Atom = 0x2c09
Acronym Atom = 0xaa07
Action Atom = 0x27206
Address Atom = 0x6f307
Align Atom = 0xb105
Allowfullscreen Atom = 0x2080f
Allowpaymentrequest Atom = 0xc113
Allowusermedia Atom = 0xdd0e
Alt Atom = 0xf303
Annotation Atom = 0x1c90a
AnnotationXml Atom = 0x1c90e
Applet Atom = 0x31906
Area Atom = 0x35604
Article Atom = 0x3fc07
As Atom = 0x3c02
Aside Atom = 0x10705
Async Atom = 0xff05
Audio Atom = 0x11505
Autocomplete Atom = 0x2780c
Autofocus Atom = 0x12109
Autoplay Atom = 0x13c08
B Atom = 0x101
Base Atom = 0x3b04
Basefont Atom = 0x3b08
Bdi Atom = 0xba03
Bdo Atom = 0x14b03
Bgsound Atom = 0x15e07
Big Atom = 0x17003
Blink Atom = 0x17305
Blockquote Atom = 0x1870a
Body Atom = 0x2804
Br Atom = 0x202
Button Atom = 0x19106
Canvas Atom = 0x10306
Caption Atom = 0x23107
Center Atom = 0x22006
Challenge Atom = 0x29b09
Charset Atom = 0x2107
Checked Atom = 0x47907
Cite Atom = 0x19c04
Class Atom = 0x56405
Code Atom = 0x5c504
Col Atom = 0x1ab03
Colgroup Atom = 0x1ab08
Color Atom = 0x1bf05
Cols Atom = 0x1c404
Colspan Atom = 0x1c407
Command Atom = 0x1d707
Content Atom = 0x58b07
Contenteditable Atom = 0x58b0f
Contextmenu Atom = 0x3800b
Controls Atom = 0x1de08
Coords Atom = 0x1ea06
Crossorigin Atom = 0x1fb0b
Data Atom = 0x4a504
Datalist Atom = 0x4a508
Datetime Atom = 0x2b808
Dd Atom = 0x2d702
Default Atom = 0x10a07
Defer Atom = 0x5c705
Del Atom = 0x45203
Desc Atom = 0x56104
Details Atom = 0x7207
Dfn Atom = 0x8703
Dialog Atom = 0xbb06
Dir Atom = 0x9303
Dirname Atom = 0x9307
Disabled Atom = 0x16408
Div Atom = 0x16b03
Dl Atom = 0x5e602
Download Atom = 0x46308
Draggable Atom = 0x17a09
Dropzone Atom = 0x40508
Dt Atom = 0x64b02
Em Atom = 0x6e02
Embed Atom = 0x6e05
Enctype Atom = 0x28d07
Face Atom = 0x21e04
Fieldset Atom = 0x22608
Figcaption Atom = 0x22e0a
Figure Atom = 0x24806
Font Atom = 0x3f04
Footer Atom = 0xf606
For Atom = 0x25403
ForeignObject Atom = 0x2540d
Foreignobject Atom = 0x2610d
Form Atom = 0x26e04
Formaction Atom = 0x26e0a
Formenctype Atom = 0x2890b
Formmethod Atom = 0x2a40a
Formnovalidate Atom = 0x2ae0e
Formtarget Atom = 0x2c00a
Frame Atom = 0x8b05
Frameset Atom = 0x8b08
H1 Atom = 0x15c02
H2 Atom = 0x2de02
H3 Atom = 0x30d02
H4 Atom = 0x34502
H5 Atom = 0x34f02
H6 Atom = 0x64d02
Head Atom = 0x33104
Header Atom = 0x33106
Headers Atom = 0x33107
Height Atom = 0x5206
Hgroup Atom = 0x2ca06
Hidden Atom = 0x2d506
High Atom = 0x2db04
Hr Atom = 0x15702
Href Atom = 0x2e004
Hreflang Atom = 0x2e008
Html Atom = 0x5604
HttpEquiv Atom = 0x2e80a
I Atom = 0x601
Icon Atom = 0x58a04
Id Atom = 0x10902
Iframe Atom = 0x2fc06
Image Atom = 0x30205
Img Atom = 0x30703
Input Atom = 0x44b05
Inputmode Atom = 0x44b09
Ins Atom = 0x20403
Integrity Atom = 0x23f09
Is Atom = 0x16502
Isindex Atom = 0x30f07
Ismap Atom = 0x31605
Itemid Atom = 0x38b06
Itemprop Atom = 0x19d08
Itemref Atom = 0x3cd07
Itemscope Atom = 0x67109
Itemtype Atom = 0x31f08
Kbd Atom = 0xb903
Keygen Atom = 0x3206
Keytype Atom = 0xd607
Kind Atom = 0x17704
Label Atom = 0x5905
Lang Atom = 0x2e404
Legend Atom = 0x18106
Li Atom = 0xb202
Link Atom = 0x17404
List Atom = 0x4a904
Listing Atom = 0x4a907
Loop Atom = 0x5d04
Low Atom = 0xc303
Main Atom = 0x1004
Malignmark Atom = 0xb00a
Manifest Atom = 0x6d708
Map Atom = 0x31803
Mark Atom = 0xb604
Marquee Atom = 0x32707
Math Atom = 0x32e04
Max Atom = 0x33d03
Maxlength Atom = 0x33d09
Media Atom = 0xe605
Mediagroup Atom = 0xe60a
Menu Atom = 0x38704
Menuitem Atom = 0x38708
Meta Atom = 0x4b804
Meter Atom = 0x9805
Method Atom = 0x2a806
Mglyph Atom = 0x30806
Mi Atom = 0x34702
Min Atom = 0x34703
Minlength Atom = 0x34709
Mn Atom = 0x2b102
Mo Atom = 0xa402
Ms Atom = 0x67402
Mtext Atom = 0x35105
Multiple Atom = 0x35f08
Muted Atom = 0x36705
Name Atom = 0x9604
Nav Atom = 0x1303
Nobr Atom = 0x3704
Noembed Atom = 0x6c07
Noframes Atom = 0x8908
Nomodule Atom = 0xa208
Nonce Atom = 0x1a605
Noscript Atom = 0x21608
Novalidate Atom = 0x2b20a
Object Atom = 0x26806
Ol Atom = 0x13702
Onabort Atom = 0x19507
Onafterprint Atom = 0x2360c
Onautocomplete Atom = 0x2760e
Onautocompleteerror Atom = 0x27613
Onauxclick Atom = 0x61f0a
Onbeforeprint Atom = 0x69e0d
Onbeforeunload Atom = 0x6e70e
Onblur Atom = 0x56d06
Oncancel Atom = 0x11908
Oncanplay Atom = 0x14d09
Oncanplaythrough Atom = 0x14d10
Onchange Atom = 0x41b08
Onclick Atom = 0x2f507
Onclose Atom = 0x36c07
Oncontextmenu Atom = 0x37e0d
Oncopy Atom = 0x39106
Oncuechange Atom = 0x3970b
Oncut Atom = 0x3a205
Ondblclick Atom = 0x3a70a
Ondrag Atom = 0x3b106
Ondragend Atom = 0x3b109
Ondragenter Atom = 0x3ba0b
Ondragexit Atom = 0x3c50a
Ondragleave Atom = 0x3df0b
Ondragover Atom = 0x3ea0a
Ondragstart Atom = 0x3f40b
Ondrop Atom = 0x40306
Ondurationchange Atom = 0x41310
Onemptied Atom = 0x40a09
Onended Atom = 0x42307
Onerror Atom = 0x42a07
Onfocus Atom = 0x43107
Onhashchange Atom = 0x43d0c
Oninput Atom = 0x44907
Oninvalid Atom = 0x45509
Onkeydown Atom = 0x45e09
Onkeypress Atom = 0x46b0a
Onkeyup Atom = 0x48007
Onlanguagechange Atom = 0x48d10
Onload Atom = 0x49d06
Onloadeddata Atom = 0x49d0c
Onloadedmetadata Atom = 0x4b010
Onloadend Atom = 0x4c609
Onloadstart Atom = 0x4cf0b
Onmessage Atom = 0x4da09
Onmessageerror Atom = 0x4da0e
Onmousedown Atom = 0x4e80b
Onmouseenter Atom = 0x4f30c
Onmouseleave Atom = 0x4ff0c
Onmousemove Atom = 0x50b0b
Onmouseout Atom = 0x5160a
Onmouseover Atom = 0x5230b
Onmouseup Atom = 0x52e09
Onmousewheel Atom = 0x53c0c
Onoffline Atom = 0x54809
Ononline Atom = 0x55108
Onpagehide Atom = 0x5590a
Onpageshow Atom = 0x5730a
Onpaste Atom = 0x57f07
Onpause Atom = 0x59a07
Onplay Atom = 0x5a406
Onplaying Atom = 0x5a409
Onpopstate Atom = 0x5ad0a
Onprogress Atom = 0x5b70a
Onratechange Atom = 0x5cc0c
Onrejectionhandled Atom = 0x5d812
Onreset Atom = 0x5ea07
Onresize Atom = 0x5f108
Onscroll Atom = 0x60008
Onsecuritypolicyviolation Atom = 0x60819
Onseeked Atom = 0x62908
Onseeking Atom = 0x63109
Onselect Atom = 0x63a08
Onshow Atom = 0x64406
Onsort Atom = 0x64f06
Onstalled Atom = 0x65909
Onstorage Atom = 0x66209
Onsubmit Atom = 0x66b08
Onsuspend Atom = 0x67b09
Ontimeupdate Atom = 0x400c
Ontoggle Atom = 0x68408
Onunhandledrejection Atom = 0x68c14
Onunload Atom = 0x6ab08
Onvolumechange Atom = 0x6b30e
Onwaiting Atom = 0x6c109
Onwheel Atom = 0x6ca07
Open Atom = 0x1a304
Optgroup Atom = 0x5f08
Optimum Atom = 0x6d107
Option Atom = 0x6e306
Output Atom = 0x51d06
P Atom = 0xc01
Param Atom = 0xc05
Pattern Atom = 0x6607
Picture Atom = 0x7b07
Ping Atom = 0xef04
Placeholder Atom = 0x1310b
Plaintext Atom = 0x1b209
Playsinline Atom = 0x1400b
Poster Atom = 0x2cf06
Pre Atom = 0x47003
Preload Atom = 0x48607
Progress Atom = 0x5b908
Prompt Atom = 0x53606
Public Atom = 0x58606
Q Atom = 0xcf01
Radiogroup Atom = 0x30a
Rb Atom = 0x3a02
Readonly Atom = 0x35708
Referrerpolicy Atom = 0x3d10e
Rel Atom = 0x48703
Required Atom = 0x24c08
Reversed Atom = 0x8008
Rows Atom = 0x9c04
Rowspan Atom = 0x9c07
Rp Atom = 0x23c02
Rt Atom = 0x19a02
Rtc Atom = 0x19a03
Ruby Atom = 0xfb04
S Atom = 0x2501
Samp Atom = 0x7804
Sandbox Atom = 0x12907
Scope Atom = 0x67505
Scoped Atom = 0x67506
Script Atom = 0x21806
Seamless Atom = 0x37108
Section Atom = 0x56807
Select Atom = 0x63c06
Selected Atom = 0x63c08
Shape Atom = 0x1e505
Size Atom = 0x5f504
Sizes Atom = 0x5f505
Slot Atom = 0x1ef04
Small Atom = 0x20605
Sortable Atom = 0x65108
Sorted Atom = 0x33706
Source Atom = 0x37806
Spacer Atom = 0x43706
Span Atom = 0x9f04
Spellcheck Atom = 0x4740a
Src Atom = 0x5c003
Srcdoc Atom = 0x5c006
Srclang Atom = 0x5f907
Srcset Atom = 0x6f906
Start Atom = 0x3fa05
Step Atom = 0x58304
Strike Atom = 0xd206
Strong Atom = 0x6dd06
Style Atom = 0x6ff05
Sub Atom = 0x66d03
Summary Atom = 0x70407
Sup Atom = 0x70b03
Svg Atom = 0x70e03
System Atom = 0x71106
Tabindex Atom = 0x4be08
Table Atom = 0x59505
Target Atom = 0x2c406
Tbody Atom = 0x2705
Td Atom = 0x9202
Template Atom = 0x71408
Textarea Atom = 0x35208
Tfoot Atom = 0xf505
Th Atom = 0x15602
Thead Atom = 0x33005
Time Atom = 0x4204
Title Atom = 0x11005
Tr Atom = 0xcc02
Track Atom = 0x1ba05
Translate Atom = 0x1f209
Tt Atom = 0x6802
Type Atom = 0xd904
Typemustmatch Atom = 0x2900d
U Atom = 0xb01
Ul Atom = 0xa702
Updateviacache Atom = 0x460e
Usemap Atom = 0x59e06
Value Atom = 0x1505
Var Atom = 0x16d03
Video Atom = 0x2f105
Wbr Atom = 0x57c03
Width Atom = 0x64905
Workertype Atom = 0x71c0a
Wrap Atom = 0x72604
Xmp Atom = 0x12f03
)
const hash0 = 0x81cdf10e
const maxAtomLen = 25
var table = [1 << 9]Atom{
0x1: 0xe60a, // mediagroup
0x2: 0x2e404, // lang
0x4: 0x2c09, // accesskey
0x5: 0x8b08, // frameset
0x7: 0x63a08, // onselect
0x8: 0x71106, // system
0xa: 0x64905, // width
0xc: 0x2890b, // formenctype
0xd: 0x13702, // ol
0xe: 0x3970b, // oncuechange
0x10: 0x14b03, // bdo
0x11: 0x11505, // audio
0x12: 0x17a09, // draggable
0x14: 0x2f105, // video
0x15: 0x2b102, // mn
0x16: 0x38704, // menu
0x17: 0x2cf06, // poster
0x19: 0xf606, // footer
0x1a: 0x2a806, // method
0x1b: 0x2b808, // datetime
0x1c: 0x19507, // onabort
0x1d: 0x460e, // updateviacache
0x1e: 0xff05, // async
0x1f: 0x49d06, // onload
0x21: 0x11908, // oncancel
0x22: 0x62908, // onseeked
0x23: 0x30205, // image
0x24: 0x5d812, // onrejectionhandled
0x26: 0x17404, // link
0x27: 0x51d06, // output
0x28: 0x33104, // head
0x29: 0x4ff0c, // onmouseleave
0x2a: 0x57f07, // onpaste
0x2b: 0x5a409, // onplaying
0x2c: 0x1c407, // colspan
0x2f: 0x1bf05, // color
0x30: 0x5f504, // size
0x31: 0x2e80a, // http-equiv
0x33: 0x601, // i
0x34: 0x5590a, // onpagehide
0x35: 0x68c14, // onunhandledrejection
0x37: 0x42a07, // onerror
0x3a: 0x3b08, // basefont
0x3f: 0x1303, // nav
0x40: 0x17704, // kind
0x41: 0x35708, // readonly
0x42: 0x30806, // mglyph
0x44: 0xb202, // li
0x46: 0x2d506, // hidden
0x47: 0x70e03, // svg
0x48: 0x58304, // step
0x49: 0x23f09, // integrity
0x4a: 0x58606, // public
0x4c: 0x1ab03, // col
0x4d: 0x1870a, // blockquote
0x4e: 0x34f02, // h5
0x50: 0x5b908, // progress
0x51: 0x5f505, // sizes
0x52: 0x34502, // h4
0x56: 0x33005, // thead
0x57: 0xd607, // keytype
0x58: 0x5b70a, // onprogress
0x59: 0x44b09, // inputmode
0x5a: 0x3b109, // ondragend
0x5d: 0x3a205, // oncut
0x5e: 0x43706, // spacer
0x5f: 0x1ab08, // colgroup
0x62: 0x16502, // is
0x65: 0x3c02, // as
0x66: 0x54809, // onoffline
0x67: 0x33706, // sorted
0x69: 0x48d10, // onlanguagechange
0x6c: 0x43d0c, // onhashchange
0x6d: 0x9604, // name
0x6e: 0xf505, // tfoot
0x6f: 0x56104, // desc
0x70: 0x33d03, // max
0x72: 0x1ea06, // coords
0x73: 0x30d02, // h3
0x74: 0x6e70e, // onbeforeunload
0x75: 0x9c04, // rows
0x76: 0x63c06, // select
0x77: 0x9805, // meter
0x78: 0x38b06, // itemid
0x79: 0x53c0c, // onmousewheel
0x7a: 0x5c006, // srcdoc
0x7d: 0x1ba05, // track
0x7f: 0x31f08, // itemtype
0x82: 0xa402, // mo
0x83: 0x41b08, // onchange
0x84: 0x33107, // headers
0x85: 0x5cc0c, // onratechange
0x86: 0x60819, // onsecuritypolicyviolation
0x88: 0x4a508, // datalist
0x89: 0x4e80b, // onmousedown
0x8a: 0x1ef04, // slot
0x8b: 0x4b010, // onloadedmetadata
0x8c: 0x1a06, // accept
0x8d: 0x26806, // object
0x91: 0x6b30e, // onvolumechange
0x92: 0x2107, // charset
0x93: 0x27613, // onautocompleteerror
0x94: 0xc113, // allowpaymentrequest
0x95: 0x2804, // body
0x96: 0x10a07, // default
0x97: 0x63c08, // selected
0x98: 0x21e04, // face
0x99: 0x1e505, // shape
0x9b: 0x68408, // ontoggle
0x9e: 0x64b02, // dt
0x9f: 0xb604, // mark
0xa1: 0xb01, // u
0xa4: 0x6ab08, // onunload
0xa5: 0x5d04, // loop
0xa6: 0x16408, // disabled
0xaa: 0x42307, // onended
0xab: 0xb00a, // malignmark
0xad: 0x67b09, // onsuspend
0xae: 0x35105, // mtext
0xaf: 0x64f06, // onsort
0xb0: 0x19d08, // itemprop
0xb3: 0x67109, // itemscope
0xb4: 0x17305, // blink
0xb6: 0x3b106, // ondrag
0xb7: 0xa702, // ul
0xb8: 0x26e04, // form
0xb9: 0x12907, // sandbox
0xba: 0x8b05, // frame
0xbb: 0x1505, // value
0xbc: 0x66209, // onstorage
0xbf: 0xaa07, // acronym
0xc0: 0x19a02, // rt
0xc2: 0x202, // br
0xc3: 0x22608, // fieldset
0xc4: 0x2900d, // typemustmatch
0xc5: 0xa208, // nomodule
0xc6: 0x6c07, // noembed
0xc7: 0x69e0d, // onbeforeprint
0xc8: 0x19106, // button
0xc9: 0x2f507, // onclick
0xca: 0x70407, // summary
0xcd: 0xfb04, // ruby
0xce: 0x56405, // class
0xcf: 0x3f40b, // ondragstart
0xd0: 0x23107, // caption
0xd4: 0xdd0e, // allowusermedia
0xd5: 0x4cf0b, // onloadstart
0xd9: 0x16b03, // div
0xda: 0x4a904, // list
0xdb: 0x32e04, // math
0xdc: 0x44b05, // input
0xdf: 0x3ea0a, // ondragover
0xe0: 0x2de02, // h2
0xe2: 0x1b209, // plaintext
0xe4: 0x4f30c, // onmouseenter
0xe7: 0x47907, // checked
0xe8: 0x47003, // pre
0xea: 0x35f08, // multiple
0xeb: 0xba03, // bdi
0xec: 0x33d09, // maxlength
0xed: 0xcf01, // q
0xee: 0x61f0a, // onauxclick
0xf0: 0x57c03, // wbr
0xf2: 0x3b04, // base
0xf3: 0x6e306, // option
0xf5: 0x41310, // ondurationchange
0xf7: 0x8908, // noframes
0xf9: 0x40508, // dropzone
0xfb: 0x67505, // scope
0xfc: 0x8008, // reversed
0xfd: 0x3ba0b, // ondragenter
0xfe: 0x3fa05, // start
0xff: 0x12f03, // xmp
0x100: 0x5f907, // srclang
0x101: 0x30703, // img
0x104: 0x101, // b
0x105: 0x25403, // for
0x106: 0x10705, // aside
0x107: 0x44907, // oninput
0x108: 0x35604, // area
0x109: 0x2a40a, // formmethod
0x10a: 0x72604, // wrap
0x10c: 0x23c02, // rp
0x10d: 0x46b0a, // onkeypress
0x10e: 0x6802, // tt
0x110: 0x34702, // mi
0x111: 0x36705, // muted
0x112: 0xf303, // alt
0x113: 0x5c504, // code
0x114: 0x6e02, // em
0x115: 0x3c50a, // ondragexit
0x117: 0x9f04, // span
0x119: 0x6d708, // manifest
0x11a: 0x38708, // menuitem
0x11b: 0x58b07, // content
0x11d: 0x6c109, // onwaiting
0x11f: 0x4c609, // onloadend
0x121: 0x37e0d, // oncontextmenu
0x123: 0x56d06, // onblur
0x124: 0x3fc07, // article
0x125: 0x9303, // dir
0x126: 0xef04, // ping
0x127: 0x24c08, // required
0x128: 0x45509, // oninvalid
0x129: 0xb105, // align
0x12b: 0x58a04, // icon
0x12c: 0x64d02, // h6
0x12d: 0x1c404, // cols
0x12e: 0x22e0a, // figcaption
0x12f: 0x45e09, // onkeydown
0x130: 0x66b08, // onsubmit
0x131: 0x14d09, // oncanplay
0x132: 0x70b03, // sup
0x133: 0xc01, // p
0x135: 0x40a09, // onemptied
0x136: 0x39106, // oncopy
0x137: 0x19c04, // cite
0x138: 0x3a70a, // ondblclick
0x13a: 0x50b0b, // onmousemove
0x13c: 0x66d03, // sub
0x13d: 0x48703, // rel
0x13e: 0x5f08, // optgroup
0x142: 0x9c07, // rowspan
0x143: 0x37806, // source
0x144: 0x21608, // noscript
0x145: 0x1a304, // open
0x146: 0x20403, // ins
0x147: 0x2540d, // foreignObject
0x148: 0x5ad0a, // onpopstate
0x14a: 0x28d07, // enctype
0x14b: 0x2760e, // onautocomplete
0x14c: 0x35208, // textarea
0x14e: 0x2780c, // autocomplete
0x14f: 0x15702, // hr
0x150: 0x1de08, // controls
0x151: 0x10902, // id
0x153: 0x2360c, // onafterprint
0x155: 0x2610d, // foreignobject
0x156: 0x32707, // marquee
0x157: 0x59a07, // onpause
0x158: 0x5e602, // dl
0x159: 0x5206, // height
0x15a: 0x34703, // min
0x15b: 0x9307, // dirname
0x15c: 0x1f209, // translate
0x15d: 0x5604, // html
0x15e: 0x34709, // minlength
0x15f: 0x48607, // preload
0x160: 0x71408, // template
0x161: 0x3df0b, // ondragleave
0x162: 0x3a02, // rb
0x164: 0x5c003, // src
0x165: 0x6dd06, // strong
0x167: 0x7804, // samp
0x168: 0x6f307, // address
0x169: 0x55108, // ononline
0x16b: 0x1310b, // placeholder
0x16c: 0x2c406, // target
0x16d: 0x20605, // small
0x16e: 0x6ca07, // onwheel
0x16f: 0x1c90a, // annotation
0x170: 0x4740a, // spellcheck
0x171: 0x7207, // details
0x172: 0x10306, // canvas
0x173: 0x12109, // autofocus
0x174: 0xc05, // param
0x176: 0x46308, // download
0x177: 0x45203, // del
0x178: 0x36c07, // onclose
0x179: 0xb903, // kbd
0x17a: 0x31906, // applet
0x17b: 0x2e004, // href
0x17c: 0x5f108, // onresize
0x17e: 0x49d0c, // onloadeddata
0x180: 0xcc02, // tr
0x181: 0x2c00a, // formtarget
0x182: 0x11005, // title
0x183: 0x6ff05, // style
0x184: 0xd206, // strike
0x185: 0x59e06, // usemap
0x186: 0x2fc06, // iframe
0x187: 0x1004, // main
0x189: 0x7b07, // picture
0x18c: 0x31605, // ismap
0x18e: 0x4a504, // data
0x18f: 0x5905, // label
0x191: 0x3d10e, // referrerpolicy
0x192: 0x15602, // th
0x194: 0x53606, // prompt
0x195: 0x56807, // section
0x197: 0x6d107, // optimum
0x198: 0x2db04, // high
0x199: 0x15c02, // h1
0x19a: 0x65909, // onstalled
0x19b: 0x16d03, // var
0x19c: 0x4204, // time
0x19e: 0x67402, // ms
0x19f: 0x33106, // header
0x1a0: 0x4da09, // onmessage
0x1a1: 0x1a605, // nonce
0x1a2: 0x26e0a, // formaction
0x1a3: 0x22006, // center
0x1a4: 0x3704, // nobr
0x1a5: 0x59505, // table
0x1a6: 0x4a907, // listing
0x1a7: 0x18106, // legend
0x1a9: 0x29b09, // challenge
0x1aa: 0x24806, // figure
0x1ab: 0xe605, // media
0x1ae: 0xd904, // type
0x1af: 0x3f04, // font
0x1b0: 0x4da0e, // onmessageerror
0x1b1: 0x37108, // seamless
0x1b2: 0x8703, // dfn
0x1b3: 0x5c705, // defer
0x1b4: 0xc303, // low
0x1b5: 0x19a03, // rtc
0x1b6: 0x5230b, // onmouseover
0x1b7: 0x2b20a, // novalidate
0x1b8: 0x71c0a, // workertype
0x1ba: 0x3cd07, // itemref
0x1bd: 0x1, // a
0x1be: 0x31803, // map
0x1bf: 0x400c, // ontimeupdate
0x1c0: 0x15e07, // bgsound
0x1c1: 0x3206, // keygen
0x1c2: 0x2705, // tbody
0x1c5: 0x64406, // onshow
0x1c7: 0x2501, // s
0x1c8: 0x6607, // pattern
0x1cc: 0x14d10, // oncanplaythrough
0x1ce: 0x2d702, // dd
0x1cf: 0x6f906, // srcset
0x1d0: 0x17003, // big
0x1d2: 0x65108, // sortable
0x1d3: 0x48007, // onkeyup
0x1d5: 0x5a406, // onplay
0x1d7: 0x4b804, // meta
0x1d8: 0x40306, // ondrop
0x1da: 0x60008, // onscroll
0x1db: 0x1fb0b, // crossorigin
0x1dc: 0x5730a, // onpageshow
0x1dd: 0x4, // abbr
0x1de: 0x9202, // td
0x1df: 0x58b0f, // contenteditable
0x1e0: 0x27206, // action
0x1e1: 0x1400b, // playsinline
0x1e2: 0x43107, // onfocus
0x1e3: 0x2e008, // hreflang
0x1e5: 0x5160a, // onmouseout
0x1e6: 0x5ea07, // onreset
0x1e7: 0x13c08, // autoplay
0x1e8: 0x63109, // onseeking
0x1ea: 0x67506, // scoped
0x1ec: 0x30a, // radiogroup
0x1ee: 0x3800b, // contextmenu
0x1ef: 0x52e09, // onmouseup
0x1f1: 0x2ca06, // hgroup
0x1f2: 0x2080f, // allowfullscreen
0x1f3: 0x4be08, // tabindex
0x1f6: 0x30f07, // isindex
0x1f7: 0x1a0e, // accept-charset
0x1f8: 0x2ae0e, // formnovalidate
0x1fb: 0x1c90e, // annotation-xml
0x1fc: 0x6e05, // embed
0x1fd: 0x21806, // script
0x1fe: 0xbb06, // dialog
0x1ff: 0x1d707, // command
}
const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" +
"asefontimeupdateviacacheightmlabelooptgroupatternoembedetail" +
"sampictureversedfnoframesetdirnameterowspanomoduleacronymali" +
"gnmarkbdialogallowpaymentrequestrikeytypeallowusermediagroup" +
"ingaltfooterubyasyncanvasidefaultitleaudioncancelautofocusan" +
"dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" +
"bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" +
"penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" +
"ntrolshapecoordslotranslatecrossoriginsmallowfullscreenoscri" +
"ptfacenterfieldsetfigcaptionafterprintegrityfigurequiredfore" +
"ignObjectforeignobjectformactionautocompleteerrorformenctype" +
"mustmatchallengeformmethodformnovalidatetimeformtargethgroup" +
"osterhiddenhigh2hreflanghttp-equivideonclickiframeimageimgly" +
"ph3isindexismappletitemtypemarqueematheadersortedmaxlength4m" +
"inlength5mtextareadonlymultiplemutedoncloseamlessourceoncont" +
"extmenuitemidoncopyoncuechangeoncutondblclickondragendondrag" +
"enterondragexitemreferrerpolicyondragleaveondragoverondragst" +
"articleondropzonemptiedondurationchangeonendedonerroronfocus" +
"paceronhashchangeoninputmodeloninvalidonkeydownloadonkeypres" +
"spellcheckedonkeyupreloadonlanguagechangeonloadeddatalisting" +
"onloadedmetadatabindexonloadendonloadstartonmessageerroronmo" +
"usedownonmouseenteronmouseleaveonmousemoveonmouseoutputonmou" +
"seoveronmouseupromptonmousewheelonofflineononlineonpagehides" +
"classectionbluronpageshowbronpastepublicontenteditableonpaus" +
"emaponplayingonpopstateonprogressrcdocodeferonratechangeonre" +
"jectionhandledonresetonresizesrclangonscrollonsecuritypolicy" +
"violationauxclickonseekedonseekingonselectedonshowidth6onsor" +
"tableonstalledonstorageonsubmitemscopedonsuspendontoggleonun" +
"handledrejectionbeforeprintonunloadonvolumechangeonwaitingon" +
"wheeloptimumanifestrongoptionbeforeunloaddressrcsetstylesumm" +
"arysupsvgsystemplateworkertypewrap"

257
vendor/golang.org/x/net/html/charset/charset.go generated vendored Normal file
View file

@ -0,0 +1,257 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package charset provides common text encodings for HTML documents.
//
// The mapping from encoding labels to encodings is defined at
// https://encoding.spec.whatwg.org/.
package charset // import "golang.org/x/net/html/charset"
import (
"bytes"
"fmt"
"io"
"mime"
"strings"
"unicode/utf8"
"golang.org/x/net/html"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/htmlindex"
"golang.org/x/text/transform"
)
// Lookup returns the encoding with the specified label, and its canonical
// name. It returns nil and the empty string if label is not one of the
// standard encodings for HTML. Matching is case-insensitive and ignores
// leading and trailing whitespace. Encoders will use HTML escape sequences for
// runes that are not supported by the character set.
func Lookup(label string) (e encoding.Encoding, name string) {
e, err := htmlindex.Get(label)
if err != nil {
return nil, ""
}
name, _ = htmlindex.Name(e)
return &htmlEncoding{e}, name
}
type htmlEncoding struct{ encoding.Encoding }
func (h *htmlEncoding) NewEncoder() *encoding.Encoder {
// HTML requires a non-terminating legacy encoder. We use HTML escapes to
// substitute unsupported code points.
return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder())
}
// DetermineEncoding determines the encoding of an HTML document by examining
// up to the first 1024 bytes of content and the declared Content-Type.
//
// See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding
func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) {
if len(content) > 1024 {
content = content[:1024]
}
for _, b := range boms {
if bytes.HasPrefix(content, b.bom) {
e, name = Lookup(b.enc)
return e, name, true
}
}
if _, params, err := mime.ParseMediaType(contentType); err == nil {
if cs, ok := params["charset"]; ok {
if e, name = Lookup(cs); e != nil {
return e, name, true
}
}
}
if len(content) > 0 {
e, name = prescan(content)
if e != nil {
return e, name, false
}
}
// Try to detect UTF-8.
// First eliminate any partial rune at the end.
for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- {
b := content[i]
if b < 0x80 {
break
}
if utf8.RuneStart(b) {
content = content[:i]
break
}
}
hasHighBit := false
for _, c := range content {
if c >= 0x80 {
hasHighBit = true
break
}
}
if hasHighBit && utf8.Valid(content) {
return encoding.Nop, "utf-8", false
}
// TODO: change default depending on user's locale?
return charmap.Windows1252, "windows-1252", false
}
// NewReader returns an io.Reader that converts the content of r to UTF-8.
// It calls DetermineEncoding to find out what r's encoding is.
func NewReader(r io.Reader, contentType string) (io.Reader, error) {
preview := make([]byte, 1024)
n, err := io.ReadFull(r, preview)
switch {
case err == io.ErrUnexpectedEOF:
preview = preview[:n]
r = bytes.NewReader(preview)
case err != nil:
return nil, err
default:
r = io.MultiReader(bytes.NewReader(preview), r)
}
if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop {
r = transform.NewReader(r, e.NewDecoder())
}
return r, nil
}
// NewReaderLabel returns a reader that converts from the specified charset to
// UTF-8. It uses Lookup to find the encoding that corresponds to label, and
// returns an error if Lookup returns nil. It is suitable for use as
// encoding/xml.Decoder's CharsetReader function.
func NewReaderLabel(label string, input io.Reader) (io.Reader, error) {
e, _ := Lookup(label)
if e == nil {
return nil, fmt.Errorf("unsupported charset: %q", label)
}
return transform.NewReader(input, e.NewDecoder()), nil
}
func prescan(content []byte) (e encoding.Encoding, name string) {
z := html.NewTokenizer(bytes.NewReader(content))
for {
switch z.Next() {
case html.ErrorToken:
return nil, ""
case html.StartTagToken, html.SelfClosingTagToken:
tagName, hasAttr := z.TagName()
if !bytes.Equal(tagName, []byte("meta")) {
continue
}
attrList := make(map[string]bool)
gotPragma := false
const (
dontKnow = iota
doNeedPragma
doNotNeedPragma
)
needPragma := dontKnow
name = ""
e = nil
for hasAttr {
var key, val []byte
key, val, hasAttr = z.TagAttr()
ks := string(key)
if attrList[ks] {
continue
}
attrList[ks] = true
for i, c := range val {
if 'A' <= c && c <= 'Z' {
val[i] = c + 0x20
}
}
switch ks {
case "http-equiv":
if bytes.Equal(val, []byte("content-type")) {
gotPragma = true
}
case "content":
if e == nil {
name = fromMetaElement(string(val))
if name != "" {
e, name = Lookup(name)
if e != nil {
needPragma = doNeedPragma
}
}
}
case "charset":
e, name = Lookup(string(val))
needPragma = doNotNeedPragma
}
}
if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma {
continue
}
if strings.HasPrefix(name, "utf-16") {
name = "utf-8"
e = encoding.Nop
}
if e != nil {
return e, name
}
}
}
}
func fromMetaElement(s string) string {
for s != "" {
csLoc := strings.Index(s, "charset")
if csLoc == -1 {
return ""
}
s = s[csLoc+len("charset"):]
s = strings.TrimLeft(s, " \t\n\f\r")
if !strings.HasPrefix(s, "=") {
continue
}
s = s[1:]
s = strings.TrimLeft(s, " \t\n\f\r")
if s == "" {
return ""
}
if q := s[0]; q == '"' || q == '\'' {
s = s[1:]
closeQuote := strings.IndexRune(s, rune(q))
if closeQuote == -1 {
return ""
}
return s[:closeQuote]
}
end := strings.IndexAny(s, "; \t\n\f\r")
if end == -1 {
end = len(s)
}
return s[:end]
}
return ""
}
var boms = []struct {
bom []byte
enc string
}{
{[]byte{0xfe, 0xff}, "utf-16be"},
{[]byte{0xff, 0xfe}, "utf-16le"},
{[]byte{0xef, 0xbb, 0xbf}, "utf-8"},
}

111
vendor/golang.org/x/net/html/const.go generated vendored Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
// Section 12.2.4.2 of the HTML5 specification says "The following elements
// have varying levels of special parsing rules".
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
var isSpecialElementMap = map[string]bool{
"address": true,
"applet": true,
"area": true,
"article": true,
"aside": true,
"base": true,
"basefont": true,
"bgsound": true,
"blockquote": true,
"body": true,
"br": true,
"button": true,
"caption": true,
"center": true,
"col": true,
"colgroup": true,
"dd": true,
"details": true,
"dir": true,
"div": true,
"dl": true,
"dt": true,
"embed": true,
"fieldset": true,
"figcaption": true,
"figure": true,
"footer": true,
"form": true,
"frame": true,
"frameset": true,
"h1": true,
"h2": true,
"h3": true,
"h4": true,
"h5": true,
"h6": true,
"head": true,
"header": true,
"hgroup": true,
"hr": true,
"html": true,
"iframe": true,
"img": true,
"input": true,
"keygen": true,
"li": true,
"link": true,
"listing": true,
"main": true,
"marquee": true,
"menu": true,
"meta": true,
"nav": true,
"noembed": true,
"noframes": true,
"noscript": true,
"object": true,
"ol": true,
"p": true,
"param": true,
"plaintext": true,
"pre": true,
"script": true,
"section": true,
"select": true,
"source": true,
"style": true,
"summary": true,
"table": true,
"tbody": true,
"td": true,
"template": true,
"textarea": true,
"tfoot": true,
"th": true,
"thead": true,
"title": true,
"tr": true,
"track": true,
"ul": true,
"wbr": true,
"xmp": true,
}
func isSpecialElement(element *Node) bool {
switch element.Namespace {
case "", "html":
return isSpecialElementMap[element.Data]
case "math":
switch element.Data {
case "mi", "mo", "mn", "ms", "mtext", "annotation-xml":
return true
}
case "svg":
switch element.Data {
case "foreignObject", "desc", "title":
return true
}
}
return false
}

106
vendor/golang.org/x/net/html/doc.go generated vendored Normal file
View file

@ -0,0 +1,106 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package html implements an HTML5-compliant tokenizer and parser.
Tokenization is done by creating a Tokenizer for an io.Reader r. It is the
caller's responsibility to ensure that r provides UTF-8 encoded HTML.
z := html.NewTokenizer(r)
Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(),
which parses the next token and returns its type, or an error:
for {
tt := z.Next()
if tt == html.ErrorToken {
// ...
return ...
}
// Process the current token.
}
There are two APIs for retrieving the current token. The high-level API is to
call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs
allow optionally calling Raw after Next but before Token, Text, TagName, or
TagAttr. In EBNF notation, the valid call sequence per token is:
Next {Raw} [ Token | Text | TagName {TagAttr} ]
Token returns an independent data structure that completely describes a token.
Entities (such as "&lt;") are unescaped, tag names and attribute keys are
lower-cased, and attributes are collected into a []Attribute. For example:
for {
if z.Next() == html.ErrorToken {
// Returning io.EOF indicates success.
return z.Err()
}
emitToken(z.Token())
}
The low-level API performs fewer allocations and copies, but the contents of
the []byte values returned by Text, TagName and TagAttr may change on the next
call to Next. For example, to extract an HTML page's anchor text:
depth := 0
for {
tt := z.Next()
switch tt {
case html.ErrorToken:
return z.Err()
case html.TextToken:
if depth > 0 {
// emitBytes should copy the []byte it receives,
// if it doesn't process it immediately.
emitBytes(z.Text())
}
case html.StartTagToken, html.EndTagToken:
tn, _ := z.TagName()
if len(tn) == 1 && tn[0] == 'a' {
if tt == html.StartTagToken {
depth++
} else {
depth--
}
}
}
}
Parsing is done by calling Parse with an io.Reader, which returns the root of
the parse tree (the document element) as a *Node. It is the caller's
responsibility to ensure that the Reader provides UTF-8 encoded HTML. For
example, to process each anchor node in depth-first order:
doc, err := html.Parse(r)
if err != nil {
// ...
}
var f func(*html.Node)
f = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "a" {
// Do something with n...
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
f(doc)
The relevant specifications include:
https://html.spec.whatwg.org/multipage/syntax.html and
https://html.spec.whatwg.org/multipage/syntax.html#tokenization
*/
package html // import "golang.org/x/net/html"
// The tokenization algorithm implemented by this package is not a line-by-line
// transliteration of the relatively verbose state-machine in the WHATWG
// specification. A more direct approach is used instead, where the program
// counter implies the state, such as whether it is tokenizing a tag or a text
// node. Specification compliance is verified by checking expected and actual
// outputs over a test suite rather than aiming for algorithmic fidelity.
// TODO(nigeltao): Does a DOM API belong in this package or a separate one?
// TODO(nigeltao): How does parsing interact with a JavaScript engine?

156
vendor/golang.org/x/net/html/doctype.go generated vendored Normal file
View file

@ -0,0 +1,156 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"strings"
)
// parseDoctype parses the data from a DoctypeToken into a name,
// public identifier, and system identifier. It returns a Node whose Type
// is DoctypeNode, whose Data is the name, and which has attributes
// named "system" and "public" for the two identifiers if they were present.
// quirks is whether the document should be parsed in "quirks mode".
func parseDoctype(s string) (n *Node, quirks bool) {
n = &Node{Type: DoctypeNode}
// Find the name.
space := strings.IndexAny(s, whitespace)
if space == -1 {
space = len(s)
}
n.Data = s[:space]
// The comparison to "html" is case-sensitive.
if n.Data != "html" {
quirks = true
}
n.Data = strings.ToLower(n.Data)
s = strings.TrimLeft(s[space:], whitespace)
if len(s) < 6 {
// It can't start with "PUBLIC" or "SYSTEM".
// Ignore the rest of the string.
return n, quirks || s != ""
}
key := strings.ToLower(s[:6])
s = s[6:]
for key == "public" || key == "system" {
s = strings.TrimLeft(s, whitespace)
if s == "" {
break
}
quote := s[0]
if quote != '"' && quote != '\'' {
break
}
s = s[1:]
q := strings.IndexRune(s, rune(quote))
var id string
if q == -1 {
id = s
s = ""
} else {
id = s[:q]
s = s[q+1:]
}
n.Attr = append(n.Attr, Attribute{Key: key, Val: id})
if key == "public" {
key = "system"
} else {
key = ""
}
}
if key != "" || s != "" {
quirks = true
} else if len(n.Attr) > 0 {
if n.Attr[0].Key == "public" {
public := strings.ToLower(n.Attr[0].Val)
switch public {
case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html":
quirks = true
default:
for _, q := range quirkyIDs {
if strings.HasPrefix(public, q) {
quirks = true
break
}
}
}
// The following two public IDs only cause quirks mode if there is no system ID.
if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") ||
strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) {
quirks = true
}
}
if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
quirks = true
}
}
return n, quirks
}
// quirkyIDs is a list of public doctype identifiers that cause a document
// to be interpreted in quirks mode. The identifiers should be in lower case.
var quirkyIDs = []string{
"+//silmaril//dtd html pro v0r11 19970101//",
"-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
"-//as//dtd html 3.0 aswedit + extensions//",
"-//ietf//dtd html 2.0 level 1//",
"-//ietf//dtd html 2.0 level 2//",
"-//ietf//dtd html 2.0 strict level 1//",
"-//ietf//dtd html 2.0 strict level 2//",
"-//ietf//dtd html 2.0 strict//",
"-//ietf//dtd html 2.0//",
"-//ietf//dtd html 2.1e//",
"-//ietf//dtd html 3.0//",
"-//ietf//dtd html 3.2 final//",
"-//ietf//dtd html 3.2//",
"-//ietf//dtd html 3//",
"-//ietf//dtd html level 0//",
"-//ietf//dtd html level 1//",
"-//ietf//dtd html level 2//",
"-//ietf//dtd html level 3//",
"-//ietf//dtd html strict level 0//",
"-//ietf//dtd html strict level 1//",
"-//ietf//dtd html strict level 2//",
"-//ietf//dtd html strict level 3//",
"-//ietf//dtd html strict//",
"-//ietf//dtd html//",
"-//metrius//dtd metrius presentational//",
"-//microsoft//dtd internet explorer 2.0 html strict//",
"-//microsoft//dtd internet explorer 2.0 html//",
"-//microsoft//dtd internet explorer 2.0 tables//",
"-//microsoft//dtd internet explorer 3.0 html strict//",
"-//microsoft//dtd internet explorer 3.0 html//",
"-//microsoft//dtd internet explorer 3.0 tables//",
"-//netscape comm. corp.//dtd html//",
"-//netscape comm. corp.//dtd strict html//",
"-//o'reilly and associates//dtd html 2.0//",
"-//o'reilly and associates//dtd html extended 1.0//",
"-//o'reilly and associates//dtd html extended relaxed 1.0//",
"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
"-//spyglass//dtd html 2.0 extended//",
"-//sq//dtd html 2.0 hotmetal + extensions//",
"-//sun microsystems corp.//dtd hotjava html//",
"-//sun microsystems corp.//dtd hotjava strict html//",
"-//w3c//dtd html 3 1995-03-24//",
"-//w3c//dtd html 3.2 draft//",
"-//w3c//dtd html 3.2 final//",
"-//w3c//dtd html 3.2//",
"-//w3c//dtd html 3.2s draft//",
"-//w3c//dtd html 4.0 frameset//",
"-//w3c//dtd html 4.0 transitional//",
"-//w3c//dtd html experimental 19960712//",
"-//w3c//dtd html experimental 970421//",
"-//w3c//dtd w3 html//",
"-//w3o//dtd w3 html 3.0//",
"-//webtechs//dtd mozilla html 2.0//",
"-//webtechs//dtd mozilla html//",
}

2253
vendor/golang.org/x/net/html/entity.go generated vendored Normal file

File diff suppressed because it is too large Load diff

258
vendor/golang.org/x/net/html/escape.go generated vendored Normal file
View file

@ -0,0 +1,258 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"bytes"
"strings"
"unicode/utf8"
)
// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
var replacementTable = [...]rune{
'\u20AC', // First entry is what 0x80 should be replaced with.
'\u0081',
'\u201A',
'\u0192',
'\u201E',
'\u2026',
'\u2020',
'\u2021',
'\u02C6',
'\u2030',
'\u0160',
'\u2039',
'\u0152',
'\u008D',
'\u017D',
'\u008F',
'\u0090',
'\u2018',
'\u2019',
'\u201C',
'\u201D',
'\u2022',
'\u2013',
'\u2014',
'\u02DC',
'\u2122',
'\u0161',
'\u203A',
'\u0153',
'\u009D',
'\u017E',
'\u0178', // Last entry is 0x9F.
// 0x00->'\uFFFD' is handled programmatically.
// 0x0D->'\u000D' is a no-op.
}
// unescapeEntity reads an entity like "&lt;" from b[src:] and writes the
// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
// Precondition: b[src] == '&' && dst <= src.
// attribute should be true if parsing an attribute value.
func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
// i starts at 1 because we already know that s[0] == '&'.
i, s := 1, b[src:]
if len(s) <= 1 {
b[dst] = b[src]
return dst + 1, src + 1
}
if s[i] == '#' {
if len(s) <= 3 { // We need to have at least "&#.".
b[dst] = b[src]
return dst + 1, src + 1
}
i++
c := s[i]
hex := false
if c == 'x' || c == 'X' {
hex = true
i++
}
x := '\x00'
for i < len(s) {
c = s[i]
i++
if hex {
if '0' <= c && c <= '9' {
x = 16*x + rune(c) - '0'
continue
} else if 'a' <= c && c <= 'f' {
x = 16*x + rune(c) - 'a' + 10
continue
} else if 'A' <= c && c <= 'F' {
x = 16*x + rune(c) - 'A' + 10
continue
}
} else if '0' <= c && c <= '9' {
x = 10*x + rune(c) - '0'
continue
}
if c != ';' {
i--
}
break
}
if i <= 3 { // No characters matched.
b[dst] = b[src]
return dst + 1, src + 1
}
if 0x80 <= x && x <= 0x9F {
// Replace characters from Windows-1252 with UTF-8 equivalents.
x = replacementTable[x-0x80]
} else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
// Replace invalid characters with the replacement character.
x = '\uFFFD'
}
return dst + utf8.EncodeRune(b[dst:], x), src + i
}
// Consume the maximum number of characters possible, with the
// consumed characters matching one of the named references.
for i < len(s) {
c := s[i]
i++
// Lower-cased characters are more common in entities, so we check for them first.
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
continue
}
if c != ';' {
i--
}
break
}
entityName := string(s[1:i])
if entityName == "" {
// No-op.
} else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
// No-op.
} else if x := entity[entityName]; x != 0 {
return dst + utf8.EncodeRune(b[dst:], x), src + i
} else if x := entity2[entityName]; x[0] != 0 {
dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
} else if !attribute {
maxLen := len(entityName) - 1
if maxLen > longestEntityWithoutSemicolon {
maxLen = longestEntityWithoutSemicolon
}
for j := maxLen; j > 1; j-- {
if x := entity[entityName[:j]]; x != 0 {
return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
}
}
}
dst1, src1 = dst+i, src+i
copy(b[dst:dst1], b[src:src1])
return dst1, src1
}
// unescape unescapes b's entities in-place, so that "a&lt;b" becomes "a<b".
// attribute should be true if parsing an attribute value.
func unescape(b []byte, attribute bool) []byte {
for i, c := range b {
if c == '&' {
dst, src := unescapeEntity(b, i, i, attribute)
for src < len(b) {
c := b[src]
if c == '&' {
dst, src = unescapeEntity(b, dst, src, attribute)
} else {
b[dst] = c
dst, src = dst+1, src+1
}
}
return b[0:dst]
}
}
return b
}
// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
func lower(b []byte) []byte {
for i, c := range b {
if 'A' <= c && c <= 'Z' {
b[i] = c + 'a' - 'A'
}
}
return b
}
const escapedChars = "&'<>\"\r"
func escape(w writer, s string) error {
i := strings.IndexAny(s, escapedChars)
for i != -1 {
if _, err := w.WriteString(s[:i]); err != nil {
return err
}
var esc string
switch s[i] {
case '&':
esc = "&amp;"
case '\'':
// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
esc = "&#39;"
case '<':
esc = "&lt;"
case '>':
esc = "&gt;"
case '"':
// "&#34;" is shorter than "&quot;".
esc = "&#34;"
case '\r':
esc = "&#13;"
default:
panic("unrecognized escape character")
}
s = s[i+1:]
if _, err := w.WriteString(esc); err != nil {
return err
}
i = strings.IndexAny(s, escapedChars)
}
_, err := w.WriteString(s)
return err
}
// EscapeString escapes special characters like "<" to become "&lt;". It
// escapes only five such characters: <, >, &, ' and ".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
if strings.IndexAny(s, escapedChars) == -1 {
return s
}
var buf bytes.Buffer
escape(&buf, s)
return buf.String()
}
// UnescapeString unescapes entities like "&lt;" to become "<". It unescapes a
// larger range of entities than EscapeString escapes. For example, "&aacute;"
// unescapes to "á", as does "&#225;" and "&xE1;".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
for _, c := range s {
if c == '&' {
return string(unescape([]byte(s), false))
}
}
return s
}

225
vendor/golang.org/x/net/html/foreign.go generated vendored Normal file
View file

@ -0,0 +1,225 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"strings"
)
func adjustAttributeNames(aa []Attribute, nameMap map[string]string) {
for i := range aa {
if newName, ok := nameMap[aa[i].Key]; ok {
aa[i].Key = newName
}
}
}
func adjustForeignAttributes(aa []Attribute) {
for i, a := range aa {
if a.Key == "" || a.Key[0] != 'x' {
continue
}
switch a.Key {
case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show",
"xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink":
j := strings.Index(a.Key, ":")
aa[i].Namespace = a.Key[:j]
aa[i].Key = a.Key[j+1:]
}
}
}
func htmlIntegrationPoint(n *Node) bool {
if n.Type != ElementNode {
return false
}
switch n.Namespace {
case "math":
if n.Data == "annotation-xml" {
for _, a := range n.Attr {
if a.Key == "encoding" {
val := strings.ToLower(a.Val)
if val == "text/html" || val == "application/xhtml+xml" {
return true
}
}
}
}
case "svg":
switch n.Data {
case "desc", "foreignObject", "title":
return true
}
}
return false
}
func mathMLTextIntegrationPoint(n *Node) bool {
if n.Namespace != "math" {
return false
}
switch n.Data {
case "mi", "mo", "mn", "ms", "mtext":
return true
}
return false
}
// Section 12.2.6.5.
var breakout = map[string]bool{
"b": true,
"big": true,
"blockquote": true,
"body": true,
"br": true,
"center": true,
"code": true,
"dd": true,
"div": true,
"dl": true,
"dt": true,
"em": true,
"embed": true,
"h1": true,
"h2": true,
"h3": true,
"h4": true,
"h5": true,
"h6": true,
"head": true,
"hr": true,
"i": true,
"img": true,
"li": true,
"listing": true,
"menu": true,
"meta": true,
"nobr": true,
"ol": true,
"p": true,
"pre": true,
"ruby": true,
"s": true,
"small": true,
"span": true,
"strong": true,
"strike": true,
"sub": true,
"sup": true,
"table": true,
"tt": true,
"u": true,
"ul": true,
"var": true,
}
// Section 12.2.6.5.
var svgTagNameAdjustments = map[string]string{
"altglyph": "altGlyph",
"altglyphdef": "altGlyphDef",
"altglyphitem": "altGlyphItem",
"animatecolor": "animateColor",
"animatemotion": "animateMotion",
"animatetransform": "animateTransform",
"clippath": "clipPath",
"feblend": "feBlend",
"fecolormatrix": "feColorMatrix",
"fecomponenttransfer": "feComponentTransfer",
"fecomposite": "feComposite",
"feconvolvematrix": "feConvolveMatrix",
"fediffuselighting": "feDiffuseLighting",
"fedisplacementmap": "feDisplacementMap",
"fedistantlight": "feDistantLight",
"feflood": "feFlood",
"fefunca": "feFuncA",
"fefuncb": "feFuncB",
"fefuncg": "feFuncG",
"fefuncr": "feFuncR",
"fegaussianblur": "feGaussianBlur",
"feimage": "feImage",
"femerge": "feMerge",
"femergenode": "feMergeNode",
"femorphology": "feMorphology",
"feoffset": "feOffset",
"fepointlight": "fePointLight",
"fespecularlighting": "feSpecularLighting",
"fespotlight": "feSpotLight",
"fetile": "feTile",
"feturbulence": "feTurbulence",
"foreignobject": "foreignObject",
"glyphref": "glyphRef",
"lineargradient": "linearGradient",
"radialgradient": "radialGradient",
"textpath": "textPath",
}
// Section 12.2.6.1
var mathMLAttributeAdjustments = map[string]string{
"definitionurl": "definitionURL",
}
var svgAttributeAdjustments = map[string]string{
"attributename": "attributeName",
"attributetype": "attributeType",
"basefrequency": "baseFrequency",
"baseprofile": "baseProfile",
"calcmode": "calcMode",
"clippathunits": "clipPathUnits",
"contentscripttype": "contentScriptType",
"contentstyletype": "contentStyleType",
"diffuseconstant": "diffuseConstant",
"edgemode": "edgeMode",
"externalresourcesrequired": "externalResourcesRequired",
"filterunits": "filterUnits",
"glyphref": "glyphRef",
"gradienttransform": "gradientTransform",
"gradientunits": "gradientUnits",
"kernelmatrix": "kernelMatrix",
"kernelunitlength": "kernelUnitLength",
"keypoints": "keyPoints",
"keysplines": "keySplines",
"keytimes": "keyTimes",
"lengthadjust": "lengthAdjust",
"limitingconeangle": "limitingConeAngle",
"markerheight": "markerHeight",
"markerunits": "markerUnits",
"markerwidth": "markerWidth",
"maskcontentunits": "maskContentUnits",
"maskunits": "maskUnits",
"numoctaves": "numOctaves",
"pathlength": "pathLength",
"patterncontentunits": "patternContentUnits",
"patterntransform": "patternTransform",
"patternunits": "patternUnits",
"pointsatx": "pointsAtX",
"pointsaty": "pointsAtY",
"pointsatz": "pointsAtZ",
"preservealpha": "preserveAlpha",
"preserveaspectratio": "preserveAspectRatio",
"primitiveunits": "primitiveUnits",
"refx": "refX",
"refy": "refY",
"repeatcount": "repeatCount",
"repeatdur": "repeatDur",
"requiredextensions": "requiredExtensions",
"requiredfeatures": "requiredFeatures",
"specularconstant": "specularConstant",
"specularexponent": "specularExponent",
"spreadmethod": "spreadMethod",
"startoffset": "startOffset",
"stddeviation": "stdDeviation",
"stitchtiles": "stitchTiles",
"surfacescale": "surfaceScale",
"systemlanguage": "systemLanguage",
"tablevalues": "tableValues",
"targetx": "targetX",
"targety": "targetY",
"textlength": "textLength",
"viewbox": "viewBox",
"viewtarget": "viewTarget",
"xchannelselector": "xChannelSelector",
"ychannelselector": "yChannelSelector",
"zoomandpan": "zoomAndPan",
}

225
vendor/golang.org/x/net/html/node.go generated vendored Normal file
View file

@ -0,0 +1,225 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"golang.org/x/net/html/atom"
)
// A NodeType is the type of a Node.
type NodeType uint32
const (
ErrorNode NodeType = iota
TextNode
DocumentNode
ElementNode
CommentNode
DoctypeNode
// RawNode nodes are not returned by the parser, but can be part of the
// Node tree passed to func Render to insert raw HTML (without escaping).
// If so, this package makes no guarantee that the rendered HTML is secure
// (from e.g. Cross Site Scripting attacks) or well-formed.
RawNode
scopeMarkerNode
)
// Section 12.2.4.3 says "The markers are inserted when entering applet,
// object, marquee, template, td, th, and caption elements, and are used
// to prevent formatting from "leaking" into applet, object, marquee,
// template, td, th, and caption elements".
var scopeMarker = Node{Type: scopeMarkerNode}
// A Node consists of a NodeType and some Data (tag name for element nodes,
// content for text) and are part of a tree of Nodes. Element nodes may also
// have a Namespace and contain a slice of Attributes. Data is unescaped, so
// that it looks like "a<b" rather than "a&lt;b". For element nodes, DataAtom
// is the atom for Data, or zero if Data is not a known tag name.
//
// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
// "svg" is short for "http://www.w3.org/2000/svg".
type Node struct {
Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node
Type NodeType
DataAtom atom.Atom
Data string
Namespace string
Attr []Attribute
}
// InsertBefore inserts newChild as a child of n, immediately before oldChild
// in the sequence of n's children. oldChild may be nil, in which case newChild
// is appended to the end of n's children.
//
// It will panic if newChild already has a parent or siblings.
func (n *Node) InsertBefore(newChild, oldChild *Node) {
if newChild.Parent != nil || newChild.PrevSibling != nil || newChild.NextSibling != nil {
panic("html: InsertBefore called for an attached child Node")
}
var prev, next *Node
if oldChild != nil {
prev, next = oldChild.PrevSibling, oldChild
} else {
prev = n.LastChild
}
if prev != nil {
prev.NextSibling = newChild
} else {
n.FirstChild = newChild
}
if next != nil {
next.PrevSibling = newChild
} else {
n.LastChild = newChild
}
newChild.Parent = n
newChild.PrevSibling = prev
newChild.NextSibling = next
}
// AppendChild adds a node c as a child of n.
//
// It will panic if c already has a parent or siblings.
func (n *Node) AppendChild(c *Node) {
if c.Parent != nil || c.PrevSibling != nil || c.NextSibling != nil {
panic("html: AppendChild called for an attached child Node")
}
last := n.LastChild
if last != nil {
last.NextSibling = c
} else {
n.FirstChild = c
}
n.LastChild = c
c.Parent = n
c.PrevSibling = last
}
// RemoveChild removes a node c that is a child of n. Afterwards, c will have
// no parent and no siblings.
//
// It will panic if c's parent is not n.
func (n *Node) RemoveChild(c *Node) {
if c.Parent != n {
panic("html: RemoveChild called for a non-child Node")
}
if n.FirstChild == c {
n.FirstChild = c.NextSibling
}
if c.NextSibling != nil {
c.NextSibling.PrevSibling = c.PrevSibling
}
if n.LastChild == c {
n.LastChild = c.PrevSibling
}
if c.PrevSibling != nil {
c.PrevSibling.NextSibling = c.NextSibling
}
c.Parent = nil
c.PrevSibling = nil
c.NextSibling = nil
}
// reparentChildren reparents all of src's child nodes to dst.
func reparentChildren(dst, src *Node) {
for {
child := src.FirstChild
if child == nil {
break
}
src.RemoveChild(child)
dst.AppendChild(child)
}
}
// clone returns a new node with the same type, data and attributes.
// The clone has no parent, no siblings and no children.
func (n *Node) clone() *Node {
m := &Node{
Type: n.Type,
DataAtom: n.DataAtom,
Data: n.Data,
Attr: make([]Attribute, len(n.Attr)),
}
copy(m.Attr, n.Attr)
return m
}
// nodeStack is a stack of nodes.
type nodeStack []*Node
// pop pops the stack. It will panic if s is empty.
func (s *nodeStack) pop() *Node {
i := len(*s)
n := (*s)[i-1]
*s = (*s)[:i-1]
return n
}
// top returns the most recently pushed node, or nil if s is empty.
func (s *nodeStack) top() *Node {
if i := len(*s); i > 0 {
return (*s)[i-1]
}
return nil
}
// index returns the index of the top-most occurrence of n in the stack, or -1
// if n is not present.
func (s *nodeStack) index(n *Node) int {
for i := len(*s) - 1; i >= 0; i-- {
if (*s)[i] == n {
return i
}
}
return -1
}
// contains returns whether a is within s.
func (s *nodeStack) contains(a atom.Atom) bool {
for _, n := range *s {
if n.DataAtom == a && n.Namespace == "" {
return true
}
}
return false
}
// insert inserts a node at the given index.
func (s *nodeStack) insert(i int, n *Node) {
(*s) = append(*s, nil)
copy((*s)[i+1:], (*s)[i:])
(*s)[i] = n
}
// remove removes a node from the stack. It is a no-op if n is not present.
func (s *nodeStack) remove(n *Node) {
i := s.index(n)
if i == -1 {
return
}
copy((*s)[i:], (*s)[i+1:])
j := len(*s) - 1
(*s)[j] = nil
*s = (*s)[:j]
}
type insertionModeStack []insertionMode
func (s *insertionModeStack) pop() (im insertionMode) {
i := len(*s)
im = (*s)[i-1]
*s = (*s)[:i-1]
return im
}
func (s *insertionModeStack) top() insertionMode {
if i := len(*s); i > 0 {
return (*s)[i-1]
}
return nil
}

2425
vendor/golang.org/x/net/html/parse.go generated vendored Normal file

File diff suppressed because it is too large Load diff

273
vendor/golang.org/x/net/html/render.go generated vendored Normal file
View file

@ -0,0 +1,273 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"bufio"
"errors"
"fmt"
"io"
"strings"
)
type writer interface {
io.Writer
io.ByteWriter
WriteString(string) (int, error)
}
// Render renders the parse tree n to the given writer.
//
// Rendering is done on a 'best effort' basis: calling Parse on the output of
// Render will always result in something similar to the original tree, but it
// is not necessarily an exact clone unless the original tree was 'well-formed'.
// 'Well-formed' is not easily specified; the HTML5 specification is
// complicated.
//
// Calling Parse on arbitrary input typically results in a 'well-formed' parse
// tree. However, it is possible for Parse to yield a 'badly-formed' parse tree.
// For example, in a 'well-formed' parse tree, no <a> element is a child of
// another <a> element: parsing "<a><a>" results in two sibling elements.
// Similarly, in a 'well-formed' parse tree, no <a> element is a child of a
// <table> element: parsing "<p><table><a>" results in a <p> with two sibling
// children; the <a> is reparented to the <table>'s parent. However, calling
// Parse on "<a><table><a>" does not return an error, but the result has an <a>
// element with an <a> child, and is therefore not 'well-formed'.
//
// Programmatically constructed trees are typically also 'well-formed', but it
// is possible to construct a tree that looks innocuous but, when rendered and
// re-parsed, results in a different tree. A simple example is that a solitary
// text node would become a tree containing <html>, <head> and <body> elements.
// Another example is that the programmatic equivalent of "a<head>b</head>c"
// becomes "<html><head><head/><body>abc</body></html>".
func Render(w io.Writer, n *Node) error {
if x, ok := w.(writer); ok {
return render(x, n)
}
buf := bufio.NewWriter(w)
if err := render(buf, n); err != nil {
return err
}
return buf.Flush()
}
// plaintextAbort is returned from render1 when a <plaintext> element
// has been rendered. No more end tags should be rendered after that.
var plaintextAbort = errors.New("html: internal error (plaintext abort)")
func render(w writer, n *Node) error {
err := render1(w, n)
if err == plaintextAbort {
err = nil
}
return err
}
func render1(w writer, n *Node) error {
// Render non-element nodes; these are the easy cases.
switch n.Type {
case ErrorNode:
return errors.New("html: cannot render an ErrorNode node")
case TextNode:
return escape(w, n.Data)
case DocumentNode:
for c := n.FirstChild; c != nil; c = c.NextSibling {
if err := render1(w, c); err != nil {
return err
}
}
return nil
case ElementNode:
// No-op.
case CommentNode:
if _, err := w.WriteString("<!--"); err != nil {
return err
}
if _, err := w.WriteString(n.Data); err != nil {
return err
}
if _, err := w.WriteString("-->"); err != nil {
return err
}
return nil
case DoctypeNode:
if _, err := w.WriteString("<!DOCTYPE "); err != nil {
return err
}
if _, err := w.WriteString(n.Data); err != nil {
return err
}
if n.Attr != nil {
var p, s string
for _, a := range n.Attr {
switch a.Key {
case "public":
p = a.Val
case "system":
s = a.Val
}
}
if p != "" {
if _, err := w.WriteString(" PUBLIC "); err != nil {
return err
}
if err := writeQuoted(w, p); err != nil {
return err
}
if s != "" {
if err := w.WriteByte(' '); err != nil {
return err
}
if err := writeQuoted(w, s); err != nil {
return err
}
}
} else if s != "" {
if _, err := w.WriteString(" SYSTEM "); err != nil {
return err
}
if err := writeQuoted(w, s); err != nil {
return err
}
}
}
return w.WriteByte('>')
case RawNode:
_, err := w.WriteString(n.Data)
return err
default:
return errors.New("html: unknown node type")
}
// Render the <xxx> opening tag.
if err := w.WriteByte('<'); err != nil {
return err
}
if _, err := w.WriteString(n.Data); err != nil {
return err
}
for _, a := range n.Attr {
if err := w.WriteByte(' '); err != nil {
return err
}
if a.Namespace != "" {
if _, err := w.WriteString(a.Namespace); err != nil {
return err
}
if err := w.WriteByte(':'); err != nil {
return err
}
}
if _, err := w.WriteString(a.Key); err != nil {
return err
}
if _, err := w.WriteString(`="`); err != nil {
return err
}
if err := escape(w, a.Val); err != nil {
return err
}
if err := w.WriteByte('"'); err != nil {
return err
}
}
if voidElements[n.Data] {
if n.FirstChild != nil {
return fmt.Errorf("html: void element <%s> has child nodes", n.Data)
}
_, err := w.WriteString("/>")
return err
}
if err := w.WriteByte('>'); err != nil {
return err
}
// Add initial newline where there is danger of a newline beging ignored.
if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") {
switch n.Data {
case "pre", "listing", "textarea":
if err := w.WriteByte('\n'); err != nil {
return err
}
}
}
// Render any child nodes.
switch n.Data {
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == TextNode {
if _, err := w.WriteString(c.Data); err != nil {
return err
}
} else {
if err := render1(w, c); err != nil {
return err
}
}
}
if n.Data == "plaintext" {
// Don't render anything else. <plaintext> must be the
// last element in the file, with no closing tag.
return plaintextAbort
}
default:
for c := n.FirstChild; c != nil; c = c.NextSibling {
if err := render1(w, c); err != nil {
return err
}
}
}
// Render the </xxx> closing tag.
if _, err := w.WriteString("</"); err != nil {
return err
}
if _, err := w.WriteString(n.Data); err != nil {
return err
}
return w.WriteByte('>')
}
// writeQuoted writes s to w surrounded by quotes. Normally it will use double
// quotes, but if s contains a double quote, it will use single quotes.
// It is used for writing the identifiers in a doctype declaration.
// In valid HTML, they can't contain both types of quotes.
func writeQuoted(w writer, s string) error {
var q byte = '"'
if strings.Contains(s, `"`) {
q = '\''
}
if err := w.WriteByte(q); err != nil {
return err
}
if _, err := w.WriteString(s); err != nil {
return err
}
if err := w.WriteByte(q); err != nil {
return err
}
return nil
}
// Section 12.1.2, "Elements", gives this list of void elements. Void elements
// are those that can't have any contents.
var voidElements = map[string]bool{
"area": true,
"base": true,
"br": true,
"col": true,
"embed": true,
"hr": true,
"img": true,
"input": true,
"keygen": true,
"link": true,
"meta": true,
"param": true,
"source": true,
"track": true,
"wbr": true,
}

1224
vendor/golang.org/x/net/html/token.go generated vendored Normal file

File diff suppressed because it is too large Load diff

3
vendor/golang.org/x/text/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/golang.org/x/text/CONTRIBUTORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/text/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/text/PATENTS generated vendored Normal file
View file

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

249
vendor/golang.org/x/text/encoding/charmap/charmap.go generated vendored Normal file
View file

@ -0,0 +1,249 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run maketables.go
// Package charmap provides simple character encodings such as IBM Code Page 437
// and Windows 1252.
package charmap // import "golang.org/x/text/encoding/charmap"
import (
"unicode/utf8"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/internal"
"golang.org/x/text/encoding/internal/identifier"
"golang.org/x/text/transform"
)
// These encodings vary only in the way clients should interpret them. Their
// coded character set is identical and a single implementation can be shared.
var (
// ISO8859_6E is the ISO 8859-6E encoding.
ISO8859_6E encoding.Encoding = &iso8859_6E
// ISO8859_6I is the ISO 8859-6I encoding.
ISO8859_6I encoding.Encoding = &iso8859_6I
// ISO8859_8E is the ISO 8859-8E encoding.
ISO8859_8E encoding.Encoding = &iso8859_8E
// ISO8859_8I is the ISO 8859-8I encoding.
ISO8859_8I encoding.Encoding = &iso8859_8I
iso8859_6E = internal.Encoding{
Encoding: ISO8859_6,
Name: "ISO-8859-6E",
MIB: identifier.ISO88596E,
}
iso8859_6I = internal.Encoding{
Encoding: ISO8859_6,
Name: "ISO-8859-6I",
MIB: identifier.ISO88596I,
}
iso8859_8E = internal.Encoding{
Encoding: ISO8859_8,
Name: "ISO-8859-8E",
MIB: identifier.ISO88598E,
}
iso8859_8I = internal.Encoding{
Encoding: ISO8859_8,
Name: "ISO-8859-8I",
MIB: identifier.ISO88598I,
}
)
// All is a list of all defined encodings in this package.
var All []encoding.Encoding = listAll
// TODO: implement these encodings, in order of importance.
// ASCII, ISO8859_1: Rather common. Close to Windows 1252.
// ISO8859_9: Close to Windows 1254.
// utf8Enc holds a rune's UTF-8 encoding in data[:len].
type utf8Enc struct {
len uint8
data [3]byte
}
// Charmap is an 8-bit character set encoding.
type Charmap struct {
// name is the encoding's name.
name string
// mib is the encoding type of this encoder.
mib identifier.MIB
// asciiSuperset states whether the encoding is a superset of ASCII.
asciiSuperset bool
// low is the lower bound of the encoded byte for a non-ASCII rune. If
// Charmap.asciiSuperset is true then this will be 0x80, otherwise 0x00.
low uint8
// replacement is the encoded replacement character.
replacement byte
// decode is the map from encoded byte to UTF-8.
decode [256]utf8Enc
// encoding is the map from runes to encoded bytes. Each entry is a
// uint32: the high 8 bits are the encoded byte and the low 24 bits are
// the rune. The table entries are sorted by ascending rune.
encode [256]uint32
}
// NewDecoder implements the encoding.Encoding interface.
func (m *Charmap) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{Transformer: charmapDecoder{charmap: m}}
}
// NewEncoder implements the encoding.Encoding interface.
func (m *Charmap) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{Transformer: charmapEncoder{charmap: m}}
}
// String returns the Charmap's name.
func (m *Charmap) String() string {
return m.name
}
// ID implements an internal interface.
func (m *Charmap) ID() (mib identifier.MIB, other string) {
return m.mib, ""
}
// charmapDecoder implements transform.Transformer by decoding to UTF-8.
type charmapDecoder struct {
transform.NopResetter
charmap *Charmap
}
func (m charmapDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
for i, c := range src {
if m.charmap.asciiSuperset && c < utf8.RuneSelf {
if nDst >= len(dst) {
err = transform.ErrShortDst
break
}
dst[nDst] = c
nDst++
nSrc = i + 1
continue
}
decode := &m.charmap.decode[c]
n := int(decode.len)
if nDst+n > len(dst) {
err = transform.ErrShortDst
break
}
// It's 15% faster to avoid calling copy for these tiny slices.
for j := 0; j < n; j++ {
dst[nDst] = decode.data[j]
nDst++
}
nSrc = i + 1
}
return nDst, nSrc, err
}
// DecodeByte returns the Charmap's rune decoding of the byte b.
func (m *Charmap) DecodeByte(b byte) rune {
switch x := &m.decode[b]; x.len {
case 1:
return rune(x.data[0])
case 2:
return rune(x.data[0]&0x1f)<<6 | rune(x.data[1]&0x3f)
default:
return rune(x.data[0]&0x0f)<<12 | rune(x.data[1]&0x3f)<<6 | rune(x.data[2]&0x3f)
}
}
// charmapEncoder implements transform.Transformer by encoding from UTF-8.
type charmapEncoder struct {
transform.NopResetter
charmap *Charmap
}
func (m charmapEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
r, size := rune(0), 0
loop:
for nSrc < len(src) {
if nDst >= len(dst) {
err = transform.ErrShortDst
break
}
r = rune(src[nSrc])
// Decode a 1-byte rune.
if r < utf8.RuneSelf {
if m.charmap.asciiSuperset {
nSrc++
dst[nDst] = uint8(r)
nDst++
continue
}
size = 1
} else {
// Decode a multi-byte rune.
r, size = utf8.DecodeRune(src[nSrc:])
if size == 1 {
// All valid runes of size 1 (those below utf8.RuneSelf) were
// handled above. We have invalid UTF-8 or we haven't seen the
// full character yet.
if !atEOF && !utf8.FullRune(src[nSrc:]) {
err = transform.ErrShortSrc
} else {
err = internal.RepertoireError(m.charmap.replacement)
}
break
}
}
// Binary search in [low, high) for that rune in the m.charmap.encode table.
for low, high := int(m.charmap.low), 0x100; ; {
if low >= high {
err = internal.RepertoireError(m.charmap.replacement)
break loop
}
mid := (low + high) / 2
got := m.charmap.encode[mid]
gotRune := rune(got & (1<<24 - 1))
if gotRune < r {
low = mid + 1
} else if gotRune > r {
high = mid
} else {
dst[nDst] = byte(got >> 24)
nDst++
break
}
}
nSrc += size
}
return nDst, nSrc, err
}
// EncodeRune returns the Charmap's byte encoding of the rune r. ok is whether
// r is in the Charmap's repertoire. If not, b is set to the Charmap's
// replacement byte. This is often the ASCII substitute character '\x1a'.
func (m *Charmap) EncodeRune(r rune) (b byte, ok bool) {
if r < utf8.RuneSelf && m.asciiSuperset {
return byte(r), true
}
for low, high := int(m.low), 0x100; ; {
if low >= high {
return m.replacement, false
}
mid := (low + high) / 2
got := m.encode[mid]
gotRune := rune(got & (1<<24 - 1))
if gotRune < r {
low = mid + 1
} else if gotRune > r {
high = mid
} else {
return byte(got >> 24), true
}
}
}

7410
vendor/golang.org/x/text/encoding/charmap/tables.go generated vendored Normal file

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more