Showing preview only (670K chars total). Download the full file or copy to clipboard to get everything.
Repository: draco1023/poi-tl-ext
Branch: main
Commit: 9bb03841fcd3
Files: 114
Total size: 616.6 KB
Directory structure:
gitextract_tlgs8gmd/
├── .gitattributes
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── bug_report.md
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── gradle/
│ └── wrapper/
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ ├── apache/
│ │ │ └── poi/
│ │ │ └── xwpf/
│ │ │ └── usermodel/
│ │ │ ├── SVGPictureData.java
│ │ │ └── SVGRelation.java
│ │ ├── ddr/
│ │ │ ├── image/
│ │ │ │ ├── ImageInfo.java
│ │ │ │ ├── ImageInputStreamWrapper.java
│ │ │ │ ├── ImageType.java
│ │ │ │ ├── MetadataReader.java
│ │ │ │ ├── MetadataReaders.java
│ │ │ │ ├── avif/
│ │ │ │ │ ├── AvifImageReader.java
│ │ │ │ │ ├── AvifImageReaderSpi.java
│ │ │ │ │ ├── AvifMetadataReader.java
│ │ │ │ │ └── AvifProviderInfo.java
│ │ │ │ ├── bmp/
│ │ │ │ │ └── BmpMetadataReader.java
│ │ │ │ ├── eps/
│ │ │ │ │ └── EpsMetadataReader.java
│ │ │ │ ├── gif/
│ │ │ │ │ └── GifMetadataReader.java
│ │ │ │ ├── heif/
│ │ │ │ │ ├── HeifImageReader.java
│ │ │ │ │ ├── HeifImageReaderSpi.java
│ │ │ │ │ ├── HeifMetadataReader.java
│ │ │ │ │ └── HeifProviderInfo.java
│ │ │ │ ├── jpeg/
│ │ │ │ │ └── JpegMetadataReader.java
│ │ │ │ ├── png/
│ │ │ │ │ └── PngMetadataReader.java
│ │ │ │ ├── tiff/
│ │ │ │ │ └── TiffMetadataReader.java
│ │ │ │ └── webp/
│ │ │ │ └── WebpMetadataReader.java
│ │ │ └── poi/
│ │ │ ├── html/
│ │ │ │ ├── ElementRenderer.java
│ │ │ │ ├── ElementRendererProvider.java
│ │ │ │ ├── HtmlConstants.java
│ │ │ │ ├── HtmlRenderConfig.java
│ │ │ │ ├── HtmlRenderContext.java
│ │ │ │ ├── HtmlRenderPolicy.java
│ │ │ │ ├── tag/
│ │ │ │ │ ├── ARenderer.java
│ │ │ │ │ ├── BigRenderer.java
│ │ │ │ │ ├── BoldRenderer.java
│ │ │ │ │ ├── BreakRenderer.java
│ │ │ │ │ ├── DeleteRenderer.java
│ │ │ │ │ ├── FigureCaptionRenderer.java
│ │ │ │ │ ├── FigureRenderer.java
│ │ │ │ │ ├── HeaderBreakRenderer.java
│ │ │ │ │ ├── HeaderRenderer.java
│ │ │ │ │ ├── ImageRenderer.java
│ │ │ │ │ ├── ItalicRenderer.java
│ │ │ │ │ ├── LaTeXRenderer.java
│ │ │ │ │ ├── ListItemRenderer.java
│ │ │ │ │ ├── ListRenderer.java
│ │ │ │ │ ├── MarkRenderer.java
│ │ │ │ │ ├── MathRenderer.java
│ │ │ │ │ ├── OmittedRenderer.java
│ │ │ │ │ ├── PreRenderer.java
│ │ │ │ │ ├── RubyRenderer.java
│ │ │ │ │ ├── SmallRenderer.java
│ │ │ │ │ ├── SubscriptRenderer.java
│ │ │ │ │ ├── SuperscriptRenderer.java
│ │ │ │ │ ├── SvgRenderer.java
│ │ │ │ │ ├── TableCellRenderer.java
│ │ │ │ │ ├── TableRenderer.java
│ │ │ │ │ ├── UnderlineRenderer.java
│ │ │ │ │ └── WalkThroughRenderer.java
│ │ │ │ └── util/
│ │ │ │ ├── BoxProperty.java
│ │ │ │ ├── CSSLength.java
│ │ │ │ ├── CSSLengthUnit.java
│ │ │ │ ├── CSSStyleUtils.java
│ │ │ │ ├── Colors.java
│ │ │ │ ├── ColumnStyle.java
│ │ │ │ ├── EmptyCSSStyle.java
│ │ │ │ ├── InlineStyle.java
│ │ │ │ ├── JsoupUtils.java
│ │ │ │ ├── ListStyle.java
│ │ │ │ ├── ListStyleType.java
│ │ │ │ ├── NamedBorderWidth.java
│ │ │ │ ├── NamedFontSize.java
│ │ │ │ ├── NumberingContext.java
│ │ │ │ ├── RenderUtils.java
│ │ │ │ ├── Span.java
│ │ │ │ ├── SpanWidth.java
│ │ │ │ ├── WhiteSpaceRule.java
│ │ │ │ └── XWPFParagraphRuns.java
│ │ │ ├── latex/
│ │ │ │ ├── LaTeXRenderPolicy.java
│ │ │ │ ├── LaTeXUtils.java
│ │ │ │ ├── TagHandler.java
│ │ │ │ └── TextCircledHandler.java
│ │ │ ├── math/
│ │ │ │ ├── EmptyEOfNaryDisplayMode.java
│ │ │ │ ├── MathMLRenderPolicy.java
│ │ │ │ ├── MathMLUtils.java
│ │ │ │ └── MathRenderConfig.java
│ │ │ └── util/
│ │ │ ├── ByteArrayCopyStream.java
│ │ │ ├── HttpURLConnectionUtils.java
│ │ │ └── XmlUtils.java
│ │ └── jsoup/
│ │ └── parser/
│ │ └── CustomHtmlTreeBuilder.java
│ └── resources/
│ ├── META-INF/
│ │ └── services/
│ │ └── javax.imageio.spi.ImageReaderSpi
│ ├── MML2OMML.XSL
│ ├── math-character-aliases.txt
│ └── math-character-circled.txt
└── test/
├── java/
│ └── org/
│ └── ddr/
│ └── poi/
│ ├── FileReader.java
│ ├── html/
│ │ └── HtmlRenderPolicyTest.java
│ ├── latex/
│ │ └── LaTeXRenderPolicyTest.java
│ └── math/
│ └── MathMLRenderPolicyTest.java
└── resources/
├── 0.xml
├── 1.html
├── 1.xml
├── 2.html
├── 2.xml
├── 3.xml
├── math.docx
├── notes.docx
├── poi.docx
└── simplelogger.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**HTML content:**
<p>Some html content to reproduce the issue</p>
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**poi-tl-ext version:**
[e.g. 0.3.12]
**poi-tl version:**
[e.g. 1.9.1]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .gitignore
================================================
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
/*.docx
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea/
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
!dev/build/*
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Ignore Gradle project-specific cache directory
.gradle/
gradle.properties
# Ignore Gradle build output directory
build/
out/
.gradletasknamecache
secring.gpg
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor 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, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# poi-tl-ext
 
# Maven
poi 4.x poi-tl 1.11 以前的版本
```xml
<dependency>
<groupId>io.github.draco1023</groupId>
<artifactId>poi-tl-ext</artifactId>
<version>0.4.26</version>
</dependency>
```
poi 5.x poi-tl 1.11.0+
```xml
<dependency>
<groupId>io.github.draco1023</groupId>
<artifactId>poi-tl-ext</artifactId>
<version>0.4.26-poi5</version>
</dependency>
```
# 扩展功能
在 [poi-tl](https://github.com/Sayi/poi-tl) 的基础上扩展了如下功能:
- 支持渲染`HTML`字符串,插件`HtmlRenderPolicy`的使用方法如下(也可参考[文档](http://deepoove.com/poi-tl/#_%E4%BD%BF%E7%94%A8%E6%8F%92%E4%BB%B6))
```java
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
Configure configure = Configure.builder()
.bind("key", htmlRenderPolicy)
.build();
Map<String, Object> data = new HashMap<>();
data.put("key", "<p>Hello <b>world</b>!</p>");
XWPFTemplate.compile("input.docx", configure).render(data).writeToFile("output.docx");
```
`HtmlRenderPolicy`可以通过`HtmlRenderConfig`进行如下设置:
- `globalFont` 全局默认字体(用于归一化处理,而不是用于样式兜底)
- `globalFontSize` 全局默认字号(用于归一化处理,而不是用于样式兜底)
- `showDefaultTableBorderInTableCell` 是否显示嵌套表格的边框(`poi`生成嵌套表格时默认不显示边框,见[#12](https://github.com/draco1023/poi-tl-ext/issues/12))
- `numberingIndent` 多级列表项缩进长度,默认值360
- `numberingHanging` 列表项悬挂长度,默认值360,CSS样式`list-style-position`为`inside`时该参数无效
- `numberingSpacing` 列表编号与内容之间的间隔类型,`STLevelSuffix.NOTHING`/`STLevelSuffix.SPACE`/`STLevelSuffix.TAB`
自定义`<latex>`标签,允许渲染嵌入在`HTML`中的`LaTeX`,字符串格式可参考[文档](https://www2.ph.ed.ac.uk/snuggletex/documentation/supported-latex.html)。
_目前实现了富文本编辑器可实现的大部分效果,后续继续改进..._
- 支持渲染`MathML`字符串,插件类为`MathMLRenderPolicy`
- 支持渲染`LaTeX`字符串,插件类为`LaTeXRenderPolicy`
## 支持我
如果您觉得这个插件节省了您的时间和精力,或者解决了您的难题,可以考虑支持一下我的工作,感谢! ⚡⚡⚡

================================================
FILE: build.gradle
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id 'java-library'
id 'io.github.sgtsilvio.gradle.maven-central-publishing' version '0.4.0'
}
def getBranchName() {
providers.exec {
commandLine("git", "branch", "--show-current")
}.standardOutput.asText.get().trim()
}
ext {
VERSION_NAME = 'poi-5' == getBranchName()? "${project.V}-poi5" : project.V
}
group = GROUP
version = VERSION_NAME
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
api 'com.deepoove:poi-tl:1.9.1'
api 'org.apache.poi:ooxml-schemas:1.4'
api 'org.apache.commons:commons-lang3:3.10'
api 'commons-io:commons-io:2.11.0'
implementation 'net.sourceforge.cssparser:cssparser:0.9.29'
implementation 'org.jsoup:jsoup:1.15.3'
implementation 'net.sf.saxon:Saxon-HE:11.4'
implementation 'de.rototor.snuggletex:snuggletex-core:1.3.0'
implementation 'com.drewnoakes:metadata-extractor:2.19.0'
implementation 'com.twelvemonkeys.imageio:imageio-batik:3.10.1'
implementation 'org.apache.xmlgraphics:batik-rasterizer-ext:1.17'
implementation 'com.twelvemonkeys.imageio:imageio-webp:3.10.1'
implementation 'com.twelvemonkeys.imageio:imageio-pict:3.10.1'
implementation 'com.twelvemonkeys.imageio:imageio-tiff:3.10.1'
implementation 'com.twelvemonkeys.imageio:imageio-jpeg:3.10.1'
implementation 'com.twelvemonkeys.imageio:imageio-bmp:3.10.1'
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.7'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
test {
useJUnitPlatform()
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.fork = true
}
javadoc {
options {
encoding 'UTF-8'
charSet 'UTF-8'
}
}
javadoc.options.addBooleanOption('Xdoclint:none', true)
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
maven(MavenPublication) {
from components.java
groupId = GROUP
artifactId = POM_ARTIFACT_ID
version = VERSION_NAME
pom {
name = POM_NAME
description = POM_DESCRIPTION
url = POM_URL
licenses {
license {
name = POM_LICENCE_NAME
url = POM_LICENCE_URL
}
}
developers {
developer {
id = POM_DEVELOPER_ID
name = POM_DEVELOPER_NAME
url = POM_DEVELOPER_URL
}
}
scm {
connection = POM_SCM_CONNECTION
developerConnection = POM_SCM_DEV_CONNECTION
url = POM_SCM_URL
}
}
}
}
}
signing {
sign publishing.publications.maven
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#
# Copyright 2016 - 2021 Draco, https://github.com/draco1023
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Dfile.encoding=UTF-8" "-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Dfile.encoding=UTF-8" "-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
rootProject.name = 'poi-tl-ext'
================================================
FILE: src/main/java/org/apache/poi/xwpf/usermodel/SVGPictureData.java
================================================
package org.apache.poi.xwpf.usermodel;
import org.apache.poi.openxml4j.opc.PackagePart;
/**
* SVGPictureData
*
* @author Draco
* @since 2022-04-12
*/
public class SVGPictureData extends XWPFPictureData {
public static final int PICTURE_TYPE_SVG = 1;
public SVGPictureData() {
}
public SVGPictureData(PackagePart part) {
super(part);
}
public static void initRelation() {
RELATIONS[PICTURE_TYPE_SVG] = SVGRelation.INSTANCE;
}
}
================================================
FILE: src/main/java/org/apache/poi/xwpf/usermodel/SVGRelation.java
================================================
package org.apache.poi.xwpf.usermodel;
import org.apache.poi.ooxml.POIXMLRelation;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.sl.usermodel.PictureData;
import javax.xml.namespace.QName;
/**
* SVGRelation
*
* @author Draco
* @since 2022-04-12
*/
public class SVGRelation extends POIXMLRelation {
public static final SVGRelation INSTANCE = new SVGRelation();
/**
* @see org.apache.poi.xslf.usermodel.XSLFPictureShape#MS_SVG_NS
*/
public static final String MS_SVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
/**
* @see org.apache.poi.xslf.usermodel.XSLFPictureShape#SVG_URI
*/
public static final String SVG_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}";
/**
* @see org.apache.poi.xslf.usermodel.XSLFPictureShape#EMBED_TAG
*/
public static final QName EMBED_TAG = new QName(PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS,
"embed", "r");
public static final String SVG_BLIP = "svgBlip";
public static final String SVG_PREFIX = "asvg";
public static final QName SVG_QNAME = new QName(MS_SVG_NS, SVG_BLIP, SVG_PREFIX);
/**
* @see XWPFRelation#IMAGE_PNG
*/
private SVGRelation() {
super(PictureData.PictureType.SVG.contentType,
PackageRelationshipTypes.IMAGE_PART,
"/word/media/image#.svg",
SVGPictureData::new, SVGPictureData::new, null);
}
}
================================================
FILE: src/main/java/org/ddr/image/ImageInfo.java
================================================
package org.ddr.image;
import java.awt.*;
import java.io.ByteArrayInputStream;
public class ImageInfo {
private ByteArrayInputStream stream;
private ImageType type;
private Dimension dimension;
public ImageInfo(ByteArrayInputStream stream) {
this.stream = stream;
}
public ImageInfo(ByteArrayInputStream stream, ImageType type, Dimension dimension) {
this.stream = stream;
this.type = type;
this.dimension = dimension;
}
public ByteArrayInputStream getStream() {
return stream;
}
public void setStream(ByteArrayInputStream stream) {
this.stream = stream;
}
public ImageType getType() {
return type;
}
public void setType(ImageType type) {
this.type = type;
}
public Dimension getDimension() {
return dimension;
}
public void setDimension(Dimension dimension) {
this.dimension = dimension;
}
public int getWidth() {
return dimension == null ? 0 : dimension.width;
}
public int getHeight() {
return dimension == null ? 0 : dimension.height;
}
public int getRawType() {
return type == null ? -1 : type.getType();
}
}
================================================
FILE: src/main/java/org/ddr/image/ImageInputStreamWrapper.java
================================================
package org.ddr.image;
import javax.imageio.stream.IIOByteBuffer;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
public class ImageInputStreamWrapper extends InputStream implements ImageInputStream {
private final ImageInputStream input;
public ImageInputStreamWrapper(ImageInputStream input) {
this.input = input;
}
@Override
public void setByteOrder(ByteOrder byteOrder) {
input.setByteOrder(byteOrder);
}
@Override
public ByteOrder getByteOrder() {
return input.getByteOrder();
}
@Override
public int read() throws IOException {
return input.read();
}
@Override
public void readBytes(IIOByteBuffer buf, int len) throws IOException {
input.readBytes(buf, len);
}
@Override
public boolean readBoolean() throws IOException {
return input.readBoolean();
}
@Override
public byte readByte() throws IOException {
return input.readByte();
}
@Override
public int readUnsignedByte() throws IOException {
return input.readUnsignedByte();
}
@Override
public short readShort() throws IOException {
return input.readShort();
}
@Override
public int readUnsignedShort() throws IOException {
return input.readUnsignedShort();
}
@Override
public char readChar() throws IOException {
return input.readChar();
}
@Override
public int readInt() throws IOException {
return input.readInt();
}
@Override
public long readUnsignedInt() throws IOException {
return input.readUnsignedInt();
}
@Override
public long readLong() throws IOException {
return input.readLong();
}
@Override
public float readFloat() throws IOException {
return input.readFloat();
}
@Override
public double readDouble() throws IOException {
return input.readDouble();
}
@Override
public String readLine() throws IOException {
return input.readLine();
}
@Override
public String readUTF() throws IOException {
return input.readUTF();
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
input.readFully(b, off, len);
}
@Override
public void readFully(byte[] b) throws IOException {
input.readFully(b);
}
@Override
public void readFully(short[] s, int off, int len) throws IOException {
input.readFully(s, off, len);
}
@Override
public void readFully(char[] c, int off, int len) throws IOException {
input.readFully(c, off, len);
}
@Override
public void readFully(int[] i, int off, int len) throws IOException {
input.readFully(i, off, len);
}
@Override
public void readFully(long[] l, int off, int len) throws IOException {
input.readFully(l, off, len);
}
@Override
public void readFully(float[] f, int off, int len) throws IOException {
input.readFully(f, off, len);
}
@Override
public void readFully(double[] d, int off, int len) throws IOException {
input.readFully(d, off, len);
}
@Override
public long getStreamPosition() throws IOException {
return input.getStreamPosition();
}
@Override
public int getBitOffset() throws IOException {
return input.getBitOffset();
}
@Override
public void setBitOffset(int bitOffset) throws IOException {
input.setBitOffset(bitOffset);
}
@Override
public int readBit() throws IOException {
return input.readBit();
}
@Override
public long readBits(int numBits) throws IOException {
return input.readBits(numBits);
}
@Override
public long length() throws IOException {
return input.length();
}
@Override
public int skipBytes(int n) throws IOException {
return input.skipBytes(n);
}
@Override
public long skipBytes(long n) throws IOException {
return input.skipBytes(n);
}
@Override
public void seek(long pos) throws IOException {
input.seek(pos);
}
@Override
public void mark() {
input.mark();
}
@Override
public void flushBefore(long pos) throws IOException {
input.flushBefore(pos);
}
@Override
public void flush() throws IOException {
input.flush();
}
@Override
public long getFlushedPosition() {
return input.getFlushedPosition();
}
@Override
public boolean isCached() {
return input.isCached();
}
@Override
public boolean isCachedMemory() {
return input.isCachedMemory();
}
@Override
public boolean isCachedFile() {
return input.isCachedFile();
}
}
================================================
FILE: src/main/java/org/ddr/image/ImageType.java
================================================
package org.ddr.image;
import org.apache.poi.xwpf.usermodel.Document;
public enum ImageType {
EMF(Document.PICTURE_TYPE_EMF),
WMF(Document.PICTURE_TYPE_WMF),
PICT(Document.PICTURE_TYPE_PICT),
JPEG(Document.PICTURE_TYPE_JPEG),
JPG(Document.PICTURE_TYPE_JPEG),
PNG(Document.PICTURE_TYPE_PNG),
DIB(Document.PICTURE_TYPE_DIB),
GIF(Document.PICTURE_TYPE_GIF),
TIF(Document.PICTURE_TYPE_TIFF),
TIFF(Document.PICTURE_TYPE_TIFF),
EPS(Document.PICTURE_TYPE_EPS),
BMP(Document.PICTURE_TYPE_BMP),
WPG(Document.PICTURE_TYPE_WPG);
private final int type;
ImageType(int type) {
this.type = type;
}
public String getExtension() {
return name().toLowerCase();
}
public int getType() {
return type;
}
}
================================================
FILE: src/main/java/org/ddr/image/MetadataReader.java
================================================
package org.ddr.image;
import com.drew.imaging.FileType;
import com.drew.metadata.Metadata;
import java.awt.*;
public interface MetadataReader {
boolean canRead(FileType type);
ImageType getType(Metadata metadata);
Dimension getDimension(Metadata metadata);
}
================================================
FILE: src/main/java/org/ddr/image/MetadataReaders.java
================================================
package org.ddr.image;
import org.ddr.image.avif.AvifMetadataReader;
import org.ddr.image.bmp.BmpMetadataReader;
import org.ddr.image.eps.EpsMetadataReader;
import org.ddr.image.gif.GifMetadataReader;
import org.ddr.image.heif.HeifMetadataReader;
import org.ddr.image.jpeg.JpegMetadataReader;
import org.ddr.image.png.PngMetadataReader;
import org.ddr.image.tiff.TiffMetadataReader;
import org.ddr.image.webp.WebpMetadataReader;
public class MetadataReaders {
public static final MetadataReader[] INSTANCES = {
new JpegMetadataReader(),
new PngMetadataReader(),
new GifMetadataReader(),
new WebpMetadataReader(),
new AvifMetadataReader(),
new HeifMetadataReader(),
new BmpMetadataReader(),
new TiffMetadataReader(),
new EpsMetadataReader()
};
}
================================================
FILE: src/main/java/org/ddr/image/avif/AvifImageReader.java
================================================
package org.ddr.image.avif;
import org.ddr.image.heif.HeifImageReader;
import javax.imageio.spi.ImageReaderSpi;
public class AvifImageReader extends HeifImageReader {
public AvifImageReader(ImageReaderSpi provider) {
super(provider, new AvifMetadataReader(), "avif");
}
}
================================================
FILE: src/main/java/org/ddr/image/avif/AvifImageReaderSpi.java
================================================
package org.ddr.image.avif;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public final class AvifImageReaderSpi extends ImageReaderSpiBase {
private static final Set<String> TYPES;
static {
TYPES = new HashSet<>(4);
// TYPES.add("mif1");
// TYPES.add("msf1");
TYPES.add("miaf");
TYPES.add("avif");
TYPES.add("avis");
TYPES.add("avio");
}
public AvifImageReaderSpi() {
super(new AvifProviderInfo());
}
@Override
public boolean canDecodeInput(Object source) throws IOException {
return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
}
private boolean canDecode(ImageInputStream input) throws IOException {
try {
input.mark();
for (int i = 0; i < 4; i++) {
input.read();
}
if (input.read() == 'f' && input.read() == 't' && input.read() == 'y' && input.read() == 'p') {
byte[] bytes = new byte[4];
int length = input.read(bytes);
if (length == 4) {
String s = new String(bytes);
return TYPES.contains(s);
}
}
} catch (Exception ignored) {
} finally {
input.reset();
}
return false;
}
@Override
public ImageReader createReaderInstance(Object extension) throws IOException {
return new AvifImageReader(this);
}
@Override
public String getDescription(Locale locale) {
return "AV1 Image File (AVIF) format image reader";
}
}
================================================
FILE: src/main/java/org/ddr/image/avif/AvifMetadataReader.java
================================================
package org.ddr.image.avif;
import com.drew.imaging.FileType;
import org.ddr.image.heif.HeifMetadataReader;
public class AvifMetadataReader extends HeifMetadataReader {
@Override
public boolean canRead(FileType type) {
// FIXME metadata-extractor 一直未发版支持 AVIF 格式,会被归为 QuickTime 格式
return type == FileType.QuickTime || type == FileType.Heif;
}
}
================================================
FILE: src/main/java/org/ddr/image/avif/AvifProviderInfo.java
================================================
package org.ddr.image.avif;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
final class AvifProviderInfo extends ReaderWriterProviderInfo {
AvifProviderInfo() {
super(
AvifProviderInfo.class,
new String[]{"avif", "AVIF"}, // Names
new String[]{"avif", "avifs"}, // Suffixes
new String[]{"image/avif", "image/avifs"}, // Mime-types
"org.ddr.image.avif.AvifImageReader", // Reader class name
new String[]{"org.ddr.image.avif.AvifImageReaderSpi"},
null,
null,
false, null, null, null, null,
true, null, null, null, null
);
}
}
================================================
FILE: src/main/java/org/ddr/image/bmp/BmpMetadataReader.java
================================================
package org.ddr.image.bmp;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.bmp.BmpHeaderDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class BmpMetadataReader implements MetadataReader {
@Override
public boolean canRead(FileType type) {
return type == FileType.Bmp;
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.BMP;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof BmpHeaderDirectory) {
Integer width = directory.getInteger(BmpHeaderDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(BmpHeaderDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/eps/EpsMetadataReader.java
================================================
package org.ddr.image.eps;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.eps.EpsDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class EpsMetadataReader implements MetadataReader{
@Override
public boolean canRead(FileType type) {
return type == FileType.Eps;
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.EPS;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof EpsDirectory) {
Integer width = directory.getInteger(EpsDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(EpsDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/gif/GifMetadataReader.java
================================================
package org.ddr.image.gif;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.gif.GifHeaderDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class GifMetadataReader implements MetadataReader{
@Override
public boolean canRead(FileType type) {
return type == FileType.Gif;
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.GIF;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof GifHeaderDirectory) {
Integer width = directory.getInteger(GifHeaderDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(GifHeaderDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/heif/HeifImageReader.java
================================================
package org.ddr.image.heif;
import com.drew.imaging.FileType;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.twelvemonkeys.imageio.ImageReaderBase;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.ddr.image.ImageInputStreamWrapper;
import org.ddr.image.MetadataReader;
import org.ddr.poi.util.HttpURLConnectionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class HeifImageReader extends ImageReaderBase {
private static final Logger log = LoggerFactory.getLogger(HeifImageReader.class);
protected final MetadataReader metadataReader;
protected ImageInputStreamWrapper wrapper;
protected Metadata metadata;
protected Dimension dimension;
protected String format;
public HeifImageReader(ImageReaderSpi provider) {
this(provider, new HeifMetadataReader(), "heic");
}
protected HeifImageReader(ImageReaderSpi provider, MetadataReader metadataReader, String format) {
super(provider);
this.metadataReader = metadataReader;
this.format = format;
}
@Override
protected void resetMembers() {
metadata = null;
dimension = null;
}
@Override
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
super.setInput(input, seekForwardOnly, ignoreMetadata);
if (imageInput != null) {
wrapper = new ImageInputStreamWrapper(imageInput);
try {
metadata = ImageMetadataReader.readMetadata(wrapper, 0, FileType.Heif);
dimension = metadataReader.getDimension(metadata);
} catch (IOException | ImageProcessingException e) {
log.warn("Failed to read metadata", e);
}
}
}
@Override
public int getWidth(int imageIndex) throws IOException {
return dimension == null ? 0 : dimension.width;
}
@Override
public int getHeight(int imageIndex) throws IOException {
return dimension == null ? 0 : dimension.height;
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
return null;
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
return convert(param);
}
BufferedImage convert(ImageReadParam param) {
HttpURLConnection uploadConnection = null;
HttpURLConnection convertConnection = null;
HttpURLConnection downloadConnection = null;
try {
uploadConnection = HttpURLConnectionUtils.connect("https://ezgif.com/" + format + "-to-jpg");
uploadConnection.setInstanceFollowRedirects(false);
HttpURLConnectionUtils.initUserAgent(uploadConnection);
uploadConnection.setRequestProperty("Referer", "https://ezgif.com/" + format + "-to-jpg");
String boundary = HttpURLConnectionUtils.initFormData(uploadConnection);
try (OutputStream outputStream = uploadConnection.getOutputStream()) {
byte[] boundaryBytes = ("--" + boundary).getBytes();
wrapper.seek(0);
HttpURLConnectionUtils.addFormData(outputStream, boundaryBytes, "new-image", "some." + format, wrapper);
outputStream.write(boundaryBytes);
outputStream.write("--".getBytes());
outputStream.write(HttpURLConnectionUtils.newLineBytes);
outputStream.flush();
}
// 获取上传响应
int uploadResponseCode = uploadConnection.getResponseCode();
if (uploadResponseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
String location = uploadConnection.getHeaderField("Location");
String convertUrl = StringUtils.substringBeforeLast(location, ".");
String fileId = StringUtils.substringAfterLast(convertUrl, "/");
convertUrl += "?ajax=true";
if (log.isDebugEnabled()) {
log.debug("{} uploaded: {}", format, fileId);
}
convertConnection = HttpURLConnectionUtils.connect(convertUrl);
HttpURLConnectionUtils.initUserAgent(convertConnection);
convertConnection.setRequestProperty("Referer", location);
boundary = HttpURLConnectionUtils.initFormData(convertConnection);
try (OutputStream convertOutput = convertConnection.getOutputStream()) {
byte[] boundaryBytes = ("--" + boundary).getBytes();
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "file", fileId, null);
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "percentage", "90", null);
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "percentager", "90", null);
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "background", "#ffffff", null);
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "backgroundc", "#ffffff", null);
HttpURLConnectionUtils.addFormData(convertOutput, boundaryBytes, "ajax", "true", null);
convertOutput.write(boundaryBytes);
convertOutput.write("--".getBytes());
convertOutput.write(HttpURLConnectionUtils.newLineBytes);
convertOutput.flush();
}
int convertResponseCode = convertConnection.getResponseCode();
if (convertResponseCode == HttpURLConnection.HTTP_OK) {
try (InputStream convertResponse = convertConnection.getInputStream()) {
Element body = Jsoup.parse(convertResponse, StandardCharsets.UTF_8.name(), "").body();
if (log.isDebugEnabled()) {
log.debug("{} converted: {}", format, body.html());
}
for (Element img : body.select("img")) {
String src = img.attr("src");
if (StringUtils.contains(src, "ezgif")) {
String url = "https:" + src;
downloadConnection = HttpURLConnectionUtils.connect(url);
HttpURLConnectionUtils.initUserAgent(downloadConnection);
try (InputStream downloadResponse = downloadConnection.getInputStream()) {
return ImageIO.read(downloadResponse);
}
}
}
}
} else {
log.warn("Failed to convert {} image. Response code: {}", format, convertResponseCode);
}
} else {
log.warn("Failed to upload image. Response code: {}", uploadResponseCode);
}
} catch (Exception e) {
log.warn("Failed to convert {} image", format, e);
IOUtils.close(uploadConnection);
IOUtils.close(convertConnection);
IOUtils.close(downloadConnection);
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/heif/HeifImageReaderSpi.java
================================================
package org.ddr.image.heif;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public final class HeifImageReaderSpi extends ImageReaderSpiBase {
private static final Set<String> TYPES;
static {
TYPES = new HashSet<>(8);
TYPES.add("mif1");
TYPES.add("msf1");
TYPES.add("heic");
TYPES.add("heix");
TYPES.add("hevc");
TYPES.add("hevx");
}
public HeifImageReaderSpi() {
super(new HeifProviderInfo());
}
@Override
public boolean canDecodeInput(Object source) throws IOException {
return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
}
private boolean canDecode(ImageInputStream input) throws IOException {
// https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/extending.fm3.html
// https://github.com/strukturag/libheif/blob/e64bb552f5d48fee5daf69c8c2fd59ec3eee0818/libheif/heif.cc#L102
// https://devstreaming-cdn.apple.com/videos/wwdc/2017/513fzgbviu23l/513/513_high_efficiency_image_file_format.pdf?dl=1
try {
input.mark();
for (int i = 0; i < 4; i++) {
input.read();
}
if (input.read() == 'f' && input.read() == 't' && input.read() == 'y' && input.read() == 'p') {
byte[] bytes = new byte[4];
int length = input.read(bytes);
if (length == 4) {
String s = new String(bytes);
return TYPES.contains(s);
}
}
} catch (Exception ignored) {
} finally {
input.reset();
}
return false;
}
@Override
public ImageReader createReaderInstance(Object extension) throws IOException {
return new HeifImageReader(this);
}
@Override
public String getDescription(Locale locale) {
return "High Efficiency Image File (HEIF) format image reader";
}
}
================================================
FILE: src/main/java/org/ddr/image/heif/HeifMetadataReader.java
================================================
package org.ddr.image.heif;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifDescriptorBase;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import com.drew.metadata.heif.HeifDescriptor;
import com.drew.metadata.heif.HeifDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class HeifMetadataReader implements MetadataReader {
@Override
public boolean canRead(FileType type) {
return type == FileType.Heif;
}
@Override
public ImageType getType(Metadata metadata) {
// FIXME read icc profile
return ImageType.JPG;
}
/**
* @see HeifDescriptor#getRotationDescription()
* @see ExifDescriptorBase#getOrientationDescription()
*/
@Override
public Dimension getDimension(Metadata metadata) {
Integer width = null;
Integer height = null;
Boolean rotated = null;
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof HeifDirectory) {
Integer r = directory.getInteger(HeifDirectory.TAG_IMAGE_ROTATION);
if (r != null) {
rotated = r % 2 == 1;
}
// FIXME 实际上可以在此处获取到 width 和 height,但是图片会直接嵌入到 word 中,能否呈现因系统支持而异,特意不获取走格式转换流程
// if (width == null) {
// width = directory.getInteger(HeifDirectory.TAG_IMAGE_WIDTH);
// }
// if (height == null) {
// height = directory.getInteger(HeifDirectory.TAG_IMAGE_HEIGHT);
// }
} else if (directory instanceof ExifIFD0Directory) {
if (rotated == null) {
Integer r = directory.getInteger(ExifIFD0Directory.TAG_ORIENTATION);
if (r != null) {
rotated = r > 4;
}
}
} else if (directory instanceof ExifSubIFDDirectory) {
if (width == null) {
width = directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH);
}
if (height == null) {
height = directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT);
}
}
}
if (width != null && height != null) {
if (Boolean.TRUE.equals(rotated)) {
//noinspection SuspiciousNameCombination
return new Dimension(height, width);
}
return new Dimension(width, height);
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/heif/HeifProviderInfo.java
================================================
package org.ddr.image.heif;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
final class HeifProviderInfo extends ReaderWriterProviderInfo {
HeifProviderInfo() {
super(
HeifProviderInfo.class,
new String[]{"heif", "HEIF"}, // Names
new String[]{"heif", "heic"}, // Suffixes
new String[]{"image/heif", "image/heic", "image/heif-sequence", "image/heic-sequence"}, // Mime-types
"org.ddr.image.heif.HeifImageReader", // Reader class name
new String[]{"org.ddr.image.heif.HeifImageReaderSpi"},
null,
null,
false, null, null, null, null,
true, null, null, null, null
);
}
}
================================================
FILE: src/main/java/org/ddr/image/jpeg/JpegMetadataReader.java
================================================
package org.ddr.image.jpeg;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.jpeg.JpegDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class JpegMetadataReader implements MetadataReader {
@Override
public boolean canRead(FileType type) {
return type == FileType.Jpeg;
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.JPG;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof JpegDirectory) {
Integer width = directory.getInteger(JpegDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(JpegDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/png/PngMetadataReader.java
================================================
package org.ddr.image.png;
import com.drew.imaging.FileType;
import com.drew.imaging.png.PngChunkType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.png.PngDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class PngMetadataReader implements MetadataReader {
@Override
public boolean canRead(FileType type) {
return type == FileType.Png;
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.PNG;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof PngDirectory) {
if (((PngDirectory) directory).getPngChunkType() == PngChunkType.IHDR) {
Integer width = directory.getInteger(PngDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(PngDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/tiff/TiffMetadataReader.java
================================================
package org.ddr.image.tiff;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
import java.util.EnumSet;
public class TiffMetadataReader implements MetadataReader {
private static final EnumSet<FileType> TIFF_TYPES = EnumSet.of(FileType.Tiff, FileType.Arw, FileType.Cr2, FileType.Nef, FileType.Orf, FileType.Rw2);
@Override
public boolean canRead(FileType type) {
return TIFF_TYPES.contains(type);
}
@Override
public ImageType getType(Metadata metadata) {
return ImageType.TIFF;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof ExifIFD0Directory) {
Integer width = directory.getInteger(ExifIFD0Directory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(ExifIFD0Directory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/image/webp/WebpMetadataReader.java
================================================
package org.ddr.image.webp;
import com.drew.imaging.FileType;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.webp.WebpDirectory;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import java.awt.*;
public class WebpMetadataReader implements MetadataReader{
@Override
public boolean canRead(FileType type) {
return type == FileType.WebP;
}
@Override
public ImageType getType(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof WebpDirectory) {
Boolean hasAlpha = directory.getBooleanObject(WebpDirectory.TAG_HAS_ALPHA);
if (Boolean.TRUE.equals(hasAlpha)) {
return ImageType.PNG;
}
}
}
return ImageType.JPG;
}
@Override
public Dimension getDimension(Metadata metadata) {
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof WebpDirectory) {
Integer width = directory.getInteger(WebpDirectory.TAG_IMAGE_WIDTH);
Integer height = directory.getInteger(WebpDirectory.TAG_IMAGE_HEIGHT);
if (width != null && height != null) {
return new Dimension(width, height);
}
}
}
return null;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/ElementRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
import org.jsoup.nodes.Element;
/**
* HTML元素渲染器
*
* @author Draco
* @since 2021-02-08
*/
public interface ElementRenderer {
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
boolean renderStart(Element element, HtmlRenderContext context);
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
default void renderEnd(Element element, HtmlRenderContext context) {
}
/**
* @return 支持的HTML标签
*/
String[] supportedTags();
/**
* @return 是否为块状渲染,如果为true在Word中会另起一个Paragraph
*/
boolean renderAsBlock();
}
================================================
FILE: src/main/java/org/ddr/poi/html/ElementRendererProvider.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
/**
* HTML元素渲染器提供者
*
* @author Draco
* @since 2022-10-21
*/
@FunctionalInterface
public interface ElementRendererProvider {
/**
* 根据HTML元素名称获取渲染器
*
* @param tagNormalName HTML元素名称(小写)
* @return HTML元素渲染器
*/
ElementRenderer get(String tagNormalName);
}
================================================
FILE: src/main/java/org/ddr/poi/html/HtmlConstants.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
import org.apache.commons.compress.utils.Sets;
import java.util.Set;
/**
* HTML常量
*
* @author Draco
* @since 2021-02-23
*/
public interface HtmlConstants {
String TAG_A = "a";
String TAG_IMG = "img";
String TAG_BR = "br";
String TAG_MATH = "math";
String TAG_HR = "hr";
String TAG_OL = "ol";
String TAG_UL = "ul";
String TAG_LI = "li";
String TAG_TABLE = "table";
String TAG_S = "s";
String TAG_DEL = "del";
/**
* HTML5不支持strike
*/
String TAG_STRIKE = "strike";
String TAG_I = "i";
String TAG_EM = "em";
String TAG_B = "b";
String TAG_STRONG = "strong";
String TAG_U = "u";
String TAG_MARK = "mark";
String TAG_SUB = "sub";
String TAG_SUP = "sup";
String TAG_H1 = "h1";
String TAG_H2 = "h2";
String TAG_H3 = "h3";
String TAG_H4 = "h4";
String TAG_H5 = "h5";
String TAG_H6 = "h6";
/**
* HTML5不支持big
*/
String TAG_BIG = "big";
String TAG_SMALL = "small";
String TAG_CAPTION = "caption";
String TAG_COLGROUP = "colgroup";
String TAG_COL = "col";
String TAG_TR = "tr";
String TAG_TH = "th";
String TAG_TD = "td";
String TAG_THEAD = "thead";
String TAG_TBODY = "tbody";
String TAG_TFOOT = "tfoot";
String TAG_FRAME = "frame";
String TAG_FRAMESET = "frameset";
String TAG_IFRAME = "iframe";
String TAG_NOFRAMES = "noframes";
String TAG_HTML = "html";
String TAG_HEAD = "head";
String TAG_BODY = "body";
String TAG_SCRIPT = "script";
String TAG_NOSCRIPT = "noscript";
String TAG_TEMPLATE = "template";
String TAG_SVG = "svg";
String TAG_RUBY = "ruby";
String TAG_RP = "rp";
String TAG_RT = "rt";
String TAG_FIGURE = "figure";
String TAG_FIGURE_CAPTION = "figcaption";
String TAG_PRE = "pre";
String TAG_XMP = "xmp";
String TAG_LATEX = "latex";
String ATTR_STYLE = "style";
String ATTR_SRC = "src";
String ATTR_WIDTH = "width";
String ATTR_HEIGHT = "height";
String ATTR_SPAN = "span";
String ATTR_ROWSPAN = "rowspan";
String ATTR_COLSPAN = "colspan";
String ATTR_HREF = "href";
String ATTR_TYPE = "type";
String ATTR_FRAME = "frame";
String ATTR_RULES = "rules";
/**
* 自定义属性:行索引
*/
String ATTR_ROW_INDEX = "_r";
/**
* 自定义属性:列索引
*/
String ATTR_COLUMN_INDEX = "_c";
String CSS_BACKGROUND = "background";
String CSS_BACKGROUND_COLOR = "background-color";
String CSS_BORDER = "border";
String CSS_BORDER_STYLE = "border-style";
String CSS_BORDER_WIDTH = "border-width";
String CSS_BORDER_COLOR = "border-color";
String CSS_FONT = "font";
String CSS_MARGIN = "margin";
String CSS_MARGIN_TOP = "margin-top";
String CSS_MARGIN_RIGHT = "margin-right";
String CSS_MARGIN_BOTTOM = "margin-bottom";
String CSS_MARGIN_LEFT = "margin-left";
String CSS_PADDING = "padding";
String CSS_PADDING_TOP = "padding-top";
String CSS_PADDING_RIGHT = "padding-right";
String CSS_PADDING_BOTTOM = "padding-bottom";
String CSS_PADDING_LEFT = "padding-left";
String CSS_FONT_STYLE = "font-style";
String CSS_FONT_VARIANT_CAPS = "font-variant-caps";
String CSS_FONT_WEIGHT = "font-weight";
String CSS_FONT_SIZE = "font-size";
String CSS_LINE_HEIGHT = "line-height";
String CSS_FONT_FAMILY = "font-family";
String CSS_TEXT_DECORATION = "text-decoration";
String CSS_TEXT_DECORATION_LINE = "text-decoration-line";
String CSS_TEXT_DECORATION_STYLE = "text-decoration-style";
String CSS_TEXT_DECORATION_COLOR = "text-decoration-color";
String CSS_TEXT_INDENT = "text-indent";
String CSS_VERTICAL_ALIGN = "vertical-align";
String CSS_VISIBILITY = "visibility";
String CSS_DISPLAY = "display";
String CSS_COLOR = "color";
String CSS_WIDTH = ATTR_WIDTH;
String CSS_MAX_WIDTH = "max-width";
String CSS_HEIGHT = ATTR_HEIGHT;
String CSS_MAX_HEIGHT = "max-height";
String CSS_BORDER_TOP = "border-top";
String CSS_BORDER_RIGHT = "border-right";
String CSS_BORDER_BOTTOM = "border-bottom";
String CSS_BORDER_LEFT = "border-left";
String CSS_BORDER_TOP_STYLE = "border-top-style";
String CSS_BORDER_RIGHT_STYLE = "border-right-style";
String CSS_BORDER_BOTTOM_STYLE = "border-bottom-style";
String CSS_BORDER_LEFT_STYLE = "border-left-style";
String CSS_BORDER_TOP_WIDTH = "border-top-width";
String CSS_BORDER_RIGHT_WIDTH = "border-right-width";
String CSS_BORDER_BOTTOM_WIDTH = "border-bottom-width";
String CSS_BORDER_LEFT_WIDTH = "border-left-width";
String CSS_BORDER_TOP_COLOR = "border-top-color";
String CSS_BORDER_RIGHT_COLOR = "border-right-color";
String CSS_BORDER_BOTTOM_COLOR = "border-bottom-color";
String CSS_BORDER_LEFT_COLOR = "border-left-color";
String CSS_FLOAT = "float";
String CSS_WHITE_SPACE = "white-space";
String CSS_LIST_STYLE = "list-style";
String CSS_LIST_STYLE_TYPE = "list-style-type";
String CSS_LIST_STYLE_POSITION = "list-style-position";
String CSS_BORDER_COLLAPSE = "border-collapse";
String CSS_BORDER_SPACING = "border-spacing";
String CSS_CAPTION_SIDE = "caption-side";
String CSS_LETTER_SPACING = "letter-spacing";
String CSS_TEXT_ALIGN = "text-align";
String NORMAL = "normal";
String ITALIC = "italic";
String OBLIQUE = "oblique";
String SMALL_CAPS = "small-caps";
String BOLD = "bold";
String BOLDER = "bolder";
String LIGHTER = "lighter";
String START = "start";
String LEFT = "left";
String END = "end";
String RIGHT = "right";
String CENTER = "center";
String JUSTIFY = "justify";
String JUSTIFY_ALL = "justify-all";
String TOP = "top";
String BOTTOM = "bottom";
String MIDDLE = "middle";
String AUTO = "auto";
String XX_SMALL = "xx-small";
String X_SMALL = "x-small";
String SMALL = "small";
String MEDIUM = "medium";
String LARGE = "large";
String X_LARGE = "x-large";
String XX_LARGE = "xx-large";
String XXX_LARGE = "xxx-large";
String SMALLER = "smaller";
String LARGER = "larger";
String THIN = "thin";
String THICK = "thick";
String PT = "pt";
String PC = "pc";
String IN = "in";
String CM = "cm";
String MM = "mm";
String PX = "px";
String EM = "em";
String REM = "rem";
String VW = "vw";
String VH = "vh";
String VMIN = "vmin";
String VMAX = "vmax";
String PERCENT = "%";
// 自定义单位
String EMU = "emu";
/**
* dxa的单位,twentieth of a point = 1 / 20 pt
*/
String TWIP = "twip";
String SLASH = "/";
String COMMA = ",";
String COLON = ":";
String SHARP = "#";
String SEMICOLON = ";";
String QUESTION = "?";
String PLUS = "+";
String MINUS = "-";
String LEFT_PARENTHESIS = "(";
String LINE_THROUGH = "line-through";
String UNDERLINE = "underline";
String SOLID = "solid";
String DOUBLE = "double";
String DOTTED = "dotted";
String DASHED = "dashed";
String WAVY = "wavy";
String NONE = "none";
String GROOVE = "groove";
String RIDGE = "ridge";
// 类似groove
String INSET = "inset";
// 类似ridge
String OUTSET = "outset";
String HIDDEN = "hidden";
String COLLAPSE = "collapse";
String SUPER = "super";
String SUB = "sub";
String NO_WRAP = "nowrap";
String PRE = "pre";
String PRE_WRAP = "pre-wrap";
String PRE_LINE = "pre-line";
String BREAK_SPACES = "break-spaces";
String INSIDE = "inside";
String OUTSIDE = "outside";
String VOID = "void";
String ABOVE = "above";
String BELOW = "below";
String H_SIDES = "hsides";
String V_SIDES = "vsides";
String LHS = "lhs";
String RHS = "rhs";
String BOX = "box";
String BORDER = "border";
String GROUPS = "groups";
String ROWS = "rows";
String COLS = "cols";
String ALL = "all";
Set<String> FONT_STYLES = Sets.newHashSet(NORMAL, ITALIC, OBLIQUE);
Set<String> FONT_VARIANTS = Sets.newHashSet(NORMAL, SMALL_CAPS);
Set<String> FONT_WEIGHTS = Sets.newHashSet(NORMAL, BOLD, BOLDER, LIGHTER);
Set<String> BORDER_STYLES = Sets.newHashSet(NONE, HIDDEN, DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET);
// 不支持overline
Set<String> TEXT_DECORATION_LINES = Sets.newHashSet(UNDERLINE, LINE_THROUGH);
Set<String> TEXT_DECORATION_STYLES = Sets.newHashSet(SOLID, DOUBLE, DOTTED, DASHED, WAVY);
Set<String> LIST_STYLE_POSITIONS = Sets.newHashSet(INSIDE, OUTSIDE);
/**
* 可继承的样式
* <a href="https://www.w3.org/TR/CSS22/propidx.html">Specification</a>
*/
Set<String> INHERITABLE_STYLES = Sets.newHashSet(
"azimuth",
CSS_BORDER_COLLAPSE,
CSS_BORDER_SPACING,
CSS_CAPTION_SIDE,
CSS_COLOR,
"cursor",
"direction",
"elevation",
"empty-cells",
CSS_FONT_FAMILY,
CSS_FONT_SIZE,
CSS_FONT_STYLE,
CSS_FONT_VARIANT_CAPS,
CSS_FONT_WEIGHT,
CSS_FONT,
CSS_LETTER_SPACING,
CSS_LINE_HEIGHT,
"list-style-image",
"list-style-position",
CSS_LIST_STYLE_TYPE,
CSS_LIST_STYLE,
"orphans",
"pitch-range",
"pitch",
"quotes",
"richness",
"speak-header",
"speak-numeral",
"speak-punctuation",
"speak",
"speech-rate",
"stress",
CSS_TEXT_ALIGN,
CSS_TEXT_INDENT,
"text-transform",
CSS_VISIBILITY,
"voice-family",
"volume",
CSS_WHITE_SPACE,
"widows",
"word-spacing"
);
/**
* 需要保留的空标签
*/
Set<String> KEEP_EMPTY_TAGS = Sets.newHashSet(TAG_LI, TAG_HR);
/**
* Word中一些主要的默认字体
*/
Set<String> MAJOR_FONT = Sets.newHashSet("宋体", "SIMSUN", "新細明體", "TIMES NEW ROMAN", "ARIAL");
String DEFINED_ITALIC = inlineStyle(CSS_FONT_STYLE, ITALIC);
String DEFINED_STRIKE = inlineStyle(CSS_TEXT_DECORATION_LINE, LINE_THROUGH);
String DEFINED_BOLD = inlineStyle(CSS_FONT_WEIGHT, BOLD);
String DEFINED_UNDERLINE = inlineStyle(CSS_TEXT_DECORATION_LINE, UNDERLINE);
String DEFINED_SUPERSCRIPT = inlineStyle(CSS_VERTICAL_ALIGN, SUPER);
String DEFINED_SUBSCRIPT = inlineStyle(CSS_VERTICAL_ALIGN, SUB);
String DEFINED_LARGER = inlineStyle(CSS_FONT_SIZE, LARGER);
String DEFINED_SMALLER = inlineStyle(CSS_FONT_SIZE, SMALLER);
String DEFINED_PRE = inlineStyle(CSS_WHITE_SPACE, PRE);
/**
* 生成行内样式声明
*
* @param key 样式属性
* @param value 样式值
* @return 行内样式声明
*/
static String inlineStyle(String key, String value) {
return key + COLON + value + SEMICOLON;
}
/**
* @param fontName 字体名称
* @return 是否为主要字体
*/
static boolean isMajorFont(String fontName) {
return MAJOR_FONT.contains(fontName.toUpperCase());
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/HtmlRenderConfig.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
import org.ddr.poi.html.util.CSSLength;
import org.ddr.poi.math.MathRenderConfig;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STLevelSuffix;
import java.util.List;
/**
* @author Draco
* @since 2021-10-26
*/
public class HtmlRenderConfig {
private String globalFont;
private CSSLength globalFontSize;
private int globalFontSizeInHalfPoints;
private boolean showDefaultTableBorderInTableCell;
private List<ElementRenderer> customRenderers;
private int numberingIndent = -1;
private int numberingHanging = -1;
private STLevelSuffix.Enum numberingSpacing;
private final MathRenderConfig mathRenderConfig = new MathRenderConfig();
/**
* @return global font family
*/
public String getGlobalFont() {
return globalFont;
}
public void setGlobalFont(String globalFont) {
this.globalFont = globalFont;
}
/**
* @return global font size
*/
public CSSLength getGlobalFontSize() {
return globalFontSize;
}
public void setGlobalFontSize(CSSLength globalFontSize) {
this.globalFontSize = globalFontSize;
globalFontSizeInHalfPoints = globalFontSize == null ? 0 : globalFontSize.toHalfPoints();
}
public int getGlobalFontSizeInHalfPoints() {
return globalFontSizeInHalfPoints;
}
/**
* @return whether to show default table borders if the table inside a table cell
*/
public boolean isShowDefaultTableBorderInTableCell() {
return showDefaultTableBorderInTableCell;
}
public void setShowDefaultTableBorderInTableCell(boolean showDefaultTableBorderInTableCell) {
this.showDefaultTableBorderInTableCell = showDefaultTableBorderInTableCell;
}
/**
* @return custom html tag renderers
*/
public List<ElementRenderer> getCustomRenderers() {
return customRenderers;
}
public void setCustomRenderers(List<ElementRenderer> customRenderers) {
this.customRenderers = customRenderers;
}
/**
* @return custom numbering indent
*/
public int getNumberingIndent() {
return numberingIndent;
}
public void setNumberingIndent(int numberingIndent) {
this.numberingIndent = numberingIndent;
}
/**
* @return custom numbering hanging
*/
public int getNumberingHanging() {
return numberingHanging;
}
public void setNumberingHanging(int numberingHanging) {
this.numberingHanging = numberingHanging;
}
/**
* @return custom numbering spacing
*/
public STLevelSuffix.Enum getNumberingSpacing() {
return numberingSpacing;
}
public void setNumberingSpacing(STLevelSuffix.Enum numberingSpacing) {
this.numberingSpacing = numberingSpacing;
}
public MathRenderConfig getMathRenderConfig() {
return mathRenderConfig;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/HtmlRenderContext.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
import com.deepoove.poi.render.RenderContext;
import com.steadystate.css.dom.CSSStyleDeclarationImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.BodyType;
import org.apache.poi.xwpf.usermodel.IBody;
import org.apache.poi.xwpf.usermodel.IRunBody;
import org.apache.poi.xwpf.usermodel.SVGPictureData;
import org.apache.poi.xwpf.usermodel.SVGRelation;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFooter;
import org.apache.poi.xwpf.usermodel.XWPFFootnote;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPicture;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.ddr.poi.html.util.CSSLength;
import org.ddr.poi.html.util.CSSLengthUnit;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.ddr.poi.html.util.Colors;
import org.ddr.poi.html.util.InlineStyle;
import org.ddr.poi.html.util.NamedFontSize;
import org.ddr.poi.html.util.NumberingContext;
import org.ddr.poi.html.util.RenderUtils;
import org.ddr.poi.html.util.WhiteSpaceRule;
import org.ddr.poi.html.util.XWPFParagraphRuns;
import org.ddr.poi.math.MathMLUtils;
import org.ddr.poi.math.MathRenderConfig;
import org.ddr.poi.util.XmlUtils;
import org.jsoup.internal.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectFrameLocking;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualGraphicFrameProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTPosH;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTPosV;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STAlignH;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STAlignV;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromH;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromV;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STWrapText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTColor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTUnderline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STStyleType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STThemeColor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STUnderline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalAlignRun;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* HTML字符串渲染上下文
*
* @author Draco
* @since 2021-02-08
*/
public class HtmlRenderContext extends RenderContext<String> {
private static final Logger log = LoggerFactory.getLogger(HtmlRenderContext.class);
/**
* 默认字号 小四 12pt 16px
*/
private static final CSSLength DEFAULT_FONT_SIZE = new CSSLength(12, CSSLengthUnit.PT);
/**
* 默认超链接颜色
*/
private static final String DEFAULT_HYPERLINK_COLOR = "0563C1";
/**
* HTML元素渲染器提供者
*/
private final ElementRendererProvider rendererProvider;
/**
* 父容器(子元素通常为段落/表格)栈,主要用于渲染HTML表格时父容器的切换
*/
private final LinkedList<IBody> ancestors = new LinkedList<>();
/**
* 行内样式栈,最近声明的样式最先生效
*/
private final LinkedList<InlineStyle> inlineStyles = new LinkedList<>();
/**
* 字号栈,一些相对大小的字号值将被进行换算
*/
private final LinkedList<Integer> fontSizesInHalfPoints = new LinkedList<>();
/**
* 列表上下文,用于处理嵌套列表
*/
private final NumberingContext numberingContext;
/**
* 默认字号
*/
private final CSSLength defaultFontSize;
/**
* 页面宽度
*/
private final CSSLength pageWidth;
/**
* 页面高度
*/
private final CSSLength pageHeight;
/**
* 页面顶部边距
*/
private final CSSLength marginTop;
/**
* 页面右侧边距
*/
private final CSSLength marginRight;
/**
* 页面底部边距
*/
private final CSSLength marginBottom;
/**
* 页面左侧边距
*/
private final CSSLength marginLeft;
/**
* 可用页面宽度
*/
private final int availablePageWidth;
/**
* 可用页面高度
*/
private final int availablePageHeight;
/**
* 占位符所在段落的样式ID
*/
private String placeholderStyleId;
/**
* 当前Run元素,可能为超链接
*/
private XWPFRun currentRun;
/**
* 全局字体,声明后所有样式中的字体将失效
*/
private String globalFont;
/**
* 全局字号,声明后所有样式中的字号将失效
*/
private BigInteger globalFontSize;
/**
* 嵌套表格是否默认显示边框
*/
private boolean showDefaultTableBorderInTableCell;
/**
* 数学公式渲染配置
*/
private MathRenderConfig mathRenderConfig;
/**
* 块状元素深度计数器
*/
private int blockLevel;
/**
* 全局xml指针,保证其总是处于将要插入内容的位置,仅在需要移动之前进行push,适时pop还原位置
*/
private final XmlCursor globalCursor;
/**
* 防重段落
*/
private XWPFParagraph dedupeParagraph;
/**
* 同一段落内的前一个文本节点
*/
private TextWrapper previousText;
/**
* 前一个图片所在节点
*/
private CTR previousDrawingRun;
/**
* 构造方法
*
* @param context 原始渲染上下文
*/
public HtmlRenderContext(RenderContext<String> context, ElementRendererProvider rendererProvider) {
super(context.getEleTemplate(), context.getData(), context.getTemplate());
this.rendererProvider = rendererProvider;
globalCursor = getRun().getCTR().newCursor();
numberingContext = new NumberingContext(getXWPFDocument());
int w = RenderUtils.A4_WIDTH;
int h = RenderUtils.A4_HEIGHT;
int top = RenderUtils.DEFAULT_TOP_MARGIN;
int right = RenderUtils.DEFAULT_RIGHT_MARGIN;
int bottom = RenderUtils.DEFAULT_BOTTOM_MARGIN;
int left = RenderUtils.DEFAULT_LEFT_MARGIN;
CTSectPr sectPr = getXWPFDocument().getDocument().getBody().getSectPr();
if (sectPr != null) {
CTPageSz pgSz = sectPr.getPgSz();
// 页面尺寸单位是twip
if (pgSz != null) {
w = pgSz.getW().intValue();
h = pgSz.getH().intValue();
}
CTPageMar pgMar = sectPr.getPgMar();
if (pgMar != null) {
top = pgMar.getTop().intValue();
right = pgMar.getRight().intValue();
bottom = pgMar.getBottom().intValue();
left = pgMar.getLeft().intValue();
}
}
pageWidth = new CSSLength(w, CSSLengthUnit.TWIP);
pageHeight = new CSSLength(h, CSSLengthUnit.TWIP);
marginTop = new CSSLength(top, CSSLengthUnit.TWIP);
marginRight = new CSSLength(right, CSSLengthUnit.TWIP);
marginBottom = new CSSLength(bottom, CSSLengthUnit.TWIP);
marginLeft = new CSSLength(left, CSSLengthUnit.TWIP);
availablePageWidth = new CSSLength(w - left - right, CSSLengthUnit.TWIP).toEMU();
availablePageHeight = new CSSLength(h - top - bottom, CSSLengthUnit.TWIP).toEMU();
int fontSize = getXWPFDocument().getStyles().getDefaultRunStyle().getFontSize();
defaultFontSize = fontSize > 0 ? new CSSLength(fontSize, CSSLengthUnit.PT) : DEFAULT_FONT_SIZE;
extractPlaceholderStyle();
}
/**
* 抽取占位符所在段落的样式
*/
private void extractPlaceholderStyle() {
XWPFRun run = getRun();
IRunBody runParent = run.getParent();
if (runParent instanceof XWPFParagraph) {
XWPFParagraph paragraph = (XWPFParagraph) runParent;
String styleId = paragraph.getStyleID();
boolean existsRPr = run.getCTR().isSetRPr();
if (styleId == null && !existsRPr) {
return;
} else if (styleId != null && !existsRPr) {
placeholderStyleId = styleId;
return;
}
XWPFStyles styles = getXWPFDocument().getStyles();
CTStyle newCTStyle = CTStyle.Factory.newInstance();
newCTStyle.setCustomStyle(STOnOff.TRUE);
newCTStyle.setType(STStyleType.PARAGRAPH);
newCTStyle.addNewHidden();
newCTStyle.setRPr(run.getCTR().getRPr());
XmlUtils.removeNamespaces(newCTStyle.getRPr());
String newStyleId = styleId + styles.getNumberOfStyles();
newCTStyle.setStyleId(newStyleId);
newCTStyle.addNewName().setVal(newStyleId);
placeholderStyleId = newStyleId;
if (styleId != null) {
newCTStyle.addNewBasedOn().setVal(styleId);
}
XWPFStyle newStyle = new XWPFStyle(newCTStyle, styles);
styles.addStyle(newStyle);
paragraph.setStyle(newStyleId);
}
}
@Override
public IBody getContainer() {
IBody container = ancestors.peek();
return container == null ? super.getContainer() : container;
}
/**
* 父容器入栈
*
* @param body 父容器
*/
public void pushContainer(IBody body) {
ancestors.push(body);
}
/**
* 父容器出栈
*/
public void popContainer() {
ancestors.pop();
}
/**
* 获取最近的段落,如果当前最近位置的内容元素是表格,则创建一个与之平级的段落
*
* @return 最近的段落
*/
public XWPFParagraph getClosestParagraph() {
if (globalCursor.getObject() == getRun().getCTR()) {
return (XWPFParagraph) getRun().getParent();
}
globalCursor.push();
XWPFParagraph paragraph = null;
if (globalCursor.toPrevSibling()) {
XmlObject object = globalCursor.getObject();
if (object instanceof CTP) {
paragraph = getContainer().getParagraph((CTP) object);
} else {
// pop() is safer than toNextSibling()
globalCursor.pop();
globalCursor.push();
paragraph = newParagraph(null, globalCursor);
RenderUtils.paragraphStyle(this, paragraph, CSSStyleUtils.EMPTY_STYLE);
}
}
globalCursor.pop();
if (paragraph != null) {
return paragraph;
}
throw new IllegalStateException("No paragraph in stack");
}
/**
* 开始渲染超链接
*
* @param uri 链接地址
*/
public void startHyperlink(String uri) {
try {
URI.create(uri);
} catch (Exception e) {
log.warn("Illegal href", e);
uri = "#";
}
if (isBlocked()) {
XWPFParagraph paragraph = getClosestParagraph();
currentRun = paragraph.createHyperlinkRun(uri);
if (dedupeParagraph == paragraph) {
unmarkDedupe();
}
} else {
// 在占位符之前插入超链接
String rId = getRun().getParent().getPart().getPackagePart()
.addExternalRelationship(uri, XWPFRelation.HYPERLINK.getRelation()).getId();
XmlCursor xmlCursor = getRun().getCTR().newCursor();
xmlCursor.insertElement(XmlUtils.HYPERLINK_QNAME);
xmlCursor.toPrevSibling();
CTHyperlink ctHyperlink = (CTHyperlink) xmlCursor.getObject();
xmlCursor.dispose();
ctHyperlink.setId(rId);
ctHyperlink.addNewR();
currentRun = new XWPFHyperlinkRun(ctHyperlink, ctHyperlink.getRArray(0), getRun().getParent());
}
}
/**
* 结束渲染超链接
*/
public void endHyperlink() {
currentRun = null;
}
/**
* 新建段落
*
* @param container 容器
* @param cursor xml指针
* @return 段落
*/
public XWPFParagraph newParagraph(IBody container, XmlCursor cursor) {
if (container == null) {
container = getContainer();
}
XWPFParagraph xwpfParagraph = container.insertNewParagraph(cursor);
if (placeholderStyleId != null) {
xwpfParagraph.setStyle(placeholderStyleId);
}
markDedupe(xwpfParagraph);
previousText = null;
adjustPicture();
return xwpfParagraph;
}
private void adjustPicture() {
if (previousDrawingRun != null) {
XmlCursor xmlCursor = previousDrawingRun.newCursor();
List<CTDrawing> drawings = new ArrayList<>(previousDrawingRun.getDrawingList());
boolean hasText = false;
while (xmlCursor.toPrevSibling()) {
if (XmlUtils.R_QNAME.equals(xmlCursor.getName())) {
CTR ctr = (CTR) xmlCursor.getObject();
for (int i = 0, l = ctr.sizeOfTArray(); i < l; i++) {
CTText ctText = ctr.getTArray(i);
if (StringUtils.isNotBlank(ctText.getStringValue())) {
hasText = true;
break;
}
}
for (int i = 0, l = ctr.sizeOfDrawingArray(); i < l; i++) {
drawings.add(ctr.getDrawingArray(i));
}
} else if (MathMLUtils.OMATH_QNAME.equals(xmlCursor.getName())) {
hasText = true;
}
}
if (!hasText) {
for (CTDrawing drawing : drawings) {
CTAnchor ctAnchor = RenderUtils.inlineToAnchor(drawing);
ctAnchor.addNewWrapTopAndBottom();
CTPosH ctPosH = ctAnchor.addNewPositionH();
ctPosH.setRelativeFrom(STRelFromH.MARGIN);
ctPosH.setAlign(STAlignH.LEFT);
CTPosV ctPosV = ctAnchor.addNewPositionV();
ctPosV.setRelativeFrom(STRelFromV.PARAGRAPH);
ctPosV.setAlign(STAlignV.TOP);
}
}
xmlCursor.dispose();
previousDrawingRun = null;
}
}
/**
* 新建CTR
*
* @return CTR
*/
public CTR newRun() {
// 超链接虽然不是段落,但是内部可以容纳多个run
if (currentRun instanceof XWPFHyperlinkRun) {
XmlCursor xmlCursor = currentRun.getCTR().newCursor();
CTR ctr;
if (xmlCursor.toFirstChild()) {
ctr = ((XWPFHyperlinkRun) currentRun).getCTHyperlink().addNewR();
} else {
// run没有内容则直接复用
ctr = currentRun.getCTR();
}
xmlCursor.dispose();
// 默认链接样式
initHyperlinkStyle(ctr);
return ctr;
}
// 考虑到样式可能不一致,总是创建新的run
if (isBlocked()) {
XWPFParagraph paragraph = getClosestParagraph();
currentRun = paragraph.createRun();
if (dedupeParagraph == paragraph) {
unmarkDedupe();
}
} else {
// 在占位符之前插入run
XmlCursor xmlCursor = getRun().getCTR().newCursor();
xmlCursor.insertElement(XmlUtils.R_QNAME);
xmlCursor.toPrevSibling();
CTR ctr = (CTR) xmlCursor.getObject();
xmlCursor.dispose();
currentRun = new XWPFRun(ctr, getRun().getParent());
}
return currentRun.getCTR();
}
/**
* 初始化超链接样式
*
* @param ctr CTR
*/
private void initHyperlinkStyle(CTR ctr) {
CTRPr rPr = RenderUtils.getRPr(ctr);
CTColor ctColor = rPr.addNewColor();
ctColor.setVal(DEFAULT_HYPERLINK_COLOR);
ctColor.setThemeColor(STThemeColor.HYPERLINK);
rPr.addNewU().setVal(STUnderline.SINGLE);
}
/**
* 获取最近的表格,仅可在渲染表格及其内部元素的时候使用
*
* @return 最近的表格
*/
public XWPFTable getClosestTable() {
globalCursor.push();
XWPFTable table = null;
if (globalCursor.toPrevSibling()) {
XmlObject object = globalCursor.getObject();
if (object instanceof CTTbl) {
table = getContainer().getTable((CTTbl) object);
}
}
globalCursor.pop();
if (table != null) {
return table;
}
throw new IllegalStateException("No table in stack");
}
/**
* 行内样式入栈
*
* @param inlineStyle 样式声明
* @param block 是否为块状元素
*/
public void pushInlineStyle(CSSStyleDeclarationImpl inlineStyle, boolean block) {
String newFontSize = inlineStyle.getFontSize();
// 默认值表示未声明字号
int fontSize = Integer.MIN_VALUE;
if (StringUtils.isNotBlank(newFontSize)) {
NamedFontSize namedFontSize = NamedFontSize.of(newFontSize);
if (namedFontSize != null) {
// 固定名称的字号
fontSize = namedFontSize.getSize().toHalfPoints();
} else if (HtmlConstants.SMALLER.equalsIgnoreCase(newFontSize)) {
// 相对小一号
int inheritedFontSize = getInheritedFontSizeInHalfPoints();
fontSize = RenderUtils.smallerFontSizeInHalfPoints(inheritedFontSize);
} else if (HtmlConstants.LARGER.equalsIgnoreCase(newFontSize)) {
// 相对大一号
int inheritedFontSize = getInheritedFontSizeInHalfPoints();
fontSize = RenderUtils.largerFontSizeInHalfPoints(inheritedFontSize);
} else {
CSSLength cssLength = CSSLength.of(newFontSize);
if (cssLength.isValid()) {
if (cssLength.getUnit() == CSSLengthUnit.PERCENT) {
fontSize = (int) Math.rint(getInheritedFontSizeInHalfPoints()
* cssLength.getValue() * cssLength.getUnit().absoluteFactor());
} else {
int emu = lengthToEMU(cssLength);
fontSize = emu * 2 / Units.EMU_PER_POINT;
}
}
}
}
fontSizesInHalfPoints.push(fontSize);
// text-decoration-line 在继承时需要合并
String textDecorationLine = inlineStyle.getPropertyValue(HtmlConstants.CSS_TEXT_DECORATION_LINE);
if (StringUtils.isNotBlank(textDecorationLine) && !HtmlConstants.NONE.equals(textDecorationLine)) {
Set<String> remainValues = new HashSet<>(HtmlConstants.TEXT_DECORATION_LINES);
String[] values = StringUtils.split(textDecorationLine, ' ');
for (String value : values) {
remainValues.remove(value);
}
if (!remainValues.isEmpty()) {
StringBuilder lines = new StringBuilder(textDecorationLine);
for (InlineStyle inheritedStyle : inlineStyles) {
String s = inheritedStyle.getDeclaration().getPropertyValue(HtmlConstants.CSS_TEXT_DECORATION_LINE);
if (HtmlConstants.NONE.equals(s)) {
break;
} else if (remainValues.contains(s)) {
lines.append(' ').append(s);
remainValues.remove(s);
if (remainValues.isEmpty()) {
break;
}
}
}
if (lines.length() > textDecorationLine.length()) {
inlineStyle.setProperty(HtmlConstants.CSS_TEXT_DECORATION_LINE, lines.toString(), null);
}
}
}
inlineStyles.push(new InlineStyle(inlineStyle, block));
}
/**
* 行内样式出栈
*/
public void popInlineStyle() {
fontSizesInHalfPoints.pop();
inlineStyles.pop();
}
/**
* 当前元素的样式声明,在HTML元素渲染开始时立即调用才可得到正确的声明,因为在解析的过程中可能会动态插入样式
*
* @return 当前元素的样式声明
*/
public CSSStyleDeclarationImpl currentElementStyle() {
InlineStyle inlineStyle = inlineStyles.peek();
return inlineStyle == null ? CSSStyleUtils.EMPTY_STYLE : inlineStyle.getDeclaration();
}
/**
* 获取样式值,将被转换为小写
*
* @param property 样式名称
* @return 样式值,未声明时返回空字符串
*/
public String getPropertyValue(String property) {
return getPropertyValue(property, false);
}
/**
* 获取样式值,将被转换为小写
*
* @param property 样式名称
* @param inlineOnly 是否仅获取行内元素样式
* @return 样式值,未声明时返回空字符串
*/
public String getPropertyValue(String property, boolean inlineOnly) {
return getPropertyValue(property, false, inlineOnly);
}
/**
* 获取样式值
*
* @param property 样式名称
* @param caseSensitive 是否大小写无关,如果无关则将转换为小写,否则保留原始值
* @param inlineOnly 是否仅获取行内元素样式
* @return 样式值,未声明时返回空字符串
*/
public String getPropertyValue(String property, boolean caseSensitive, boolean inlineOnly) {
for (InlineStyle inlineStyle : inlineStyles) {
if (inlineOnly && inlineStyle.isBlock()) {
break;
}
String propertyValue = inlineStyle.getDeclaration().getPropertyValue(property);
if (StringUtils.isNotBlank(propertyValue)) {
return caseSensitive ? propertyValue : propertyValue.toLowerCase();
}
}
return "";
}
/**
* @return Word中设置的默认字号
*/
public CSSLength getDefaultFontSize() {
return defaultFontSize;
}
/**
* @return 获取当前元素继承的字号,以“半点”为单位
*/
public int getInheritedFontSizeInHalfPoints() {
for (Integer fontSize : fontSizesInHalfPoints) {
if (fontSize > 0) {
return fontSize;
}
}
return defaultFontSize.toHalfPoints();
}
/**
* @return 父容器的可用宽度,以EMU为单位
*/
public int getAvailableWidthInEMU() {
IBody container = getContainer();
if (container.getPartType() == BodyType.DOCUMENT) {
return availablePageWidth;
} else {
return RenderUtils.getAvailableWidthInEMU(container);
}
}
/**
* 考虑约束计算长度,以EMU为单位
*
* @param length 长度声明
* @param maxLength 最大长度声明
* @param naturalEMU 原始长度
* @param parentEMU 父容器长度
* @return 以EMU为单位的长度值
*/
public int computeLengthInEMU(String length, String maxLength, int naturalEMU, int parentEMU) {
int emu = naturalEMU;
if (length.length() > 0) {
CSSLength cssLength = CSSLength.of(length);
if (cssLength.isValid()) {
emu = computeLengthInEMU(cssLength, naturalEMU, parentEMU);
}
}
if (maxLength.length() > 0) {
CSSLength cssLength = CSSLength.of(maxLength);
if (cssLength.isValid()) {
int maxEMU = computeLengthInEMU(cssLength, naturalEMU, parentEMU);
emu = Math.min(maxEMU, emu);
}
}
return Math.min(emu, parentEMU);
}
/**
* 考虑约束计算长度,以EMU为单位
*
* @param cssLength 长度声明
* @param naturalEMU 原始长度
* @param parentEMU 父容器长度
* @return 以EMU为单位的长度值
*/
public int computeLengthInEMU(CSSLength cssLength, int naturalEMU, int parentEMU) {
int length;
if (cssLength.getUnit() == CSSLengthUnit.PERCENT) {
if (parentEMU != Integer.MAX_VALUE) {
length = (int) (parentEMU * cssLength.getValue() * cssLength.getUnit().absoluteFactor());
} else {
length = naturalEMU;
}
} else {
length = lengthToEMU(cssLength);
}
return length;
}
/**
* 渲染文本
*
* @param text 文本
*/
public void renderText(String text) {
String whiteSpace = getPropertyValue(HtmlConstants.CSS_WHITE_SPACE);
WhiteSpaceRule rule = WhiteSpaceRule.of(whiteSpace, WhiteSpaceRule.NORMAL);
StringBuilder sb = StringUtil.borrowBuilder();
boolean mergeWhitespace = false;
// https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
int len = text.length();
int c;
if (!rule.isKeepTrailingSpace()) {
boolean reachedLastNonWhite = false;
for (int i = len - 1; i >= 0; i -= Character.charCount(c)) {
c = text.codePointAt(i);
switch (c) {
case ' ':
case '\r':
case '\n':
case '\t':
case 173: // soft hyphen
case 8203: // zero width space
case 8204: // zero width non-joiner
case 8205: // zero width joiner
case 8206: // lrm
case 8207: // rlm
case 8288: // word joiner
case 8289: // apply function
case 8290: // invisible times
case 8291: // invisible separator
len = i;
break;
default:
if (Character.getType(c) == 16) {
len = i;
} else {
reachedLastNonWhite = true;
}
break;
}
if (reachedLastNonWhite) {
break;
}
}
}
if (len == 0) {
return;
}
boolean endTrimmed = len < text.length();
CTR ctr = newRun();
for (int i = 0; i < len; i += Character.charCount(c)) {
c = text.codePointAt(i);
switch (c) {
case '\r':
if (i + 1 < len && text.codePointAt(i + 1) == '\n') {
continue;
}
if (rule.isKeepLineBreak()) {
addText(ctr, sb, false);
ctr.addNewCr();
} else {
mergeWhitespace = true;
}
break;
case '\n':
if (rule.isKeepLineBreak()) {
addText(ctr, sb, false);
ctr.addNewBr();
} else {
mergeWhitespace = true;
}
break;
case ' ':
if (rule.isKeepSpaceAndTab()) {
sb.appendCodePoint(c);
} else {
mergeWhitespace = true;
}
break;
case '\t':
if (rule.isKeepSpaceAndTab()) {
addText(ctr, sb, false);
ctr.addNewTab();
} else {
mergeWhitespace = true;
}
break;
case 160: // nbsp
case 8192: // enquad
case 8193: // emquad
case 8194: // ensp
case 8195: // emsp
case 8196: // emsp13
case 8197: // emsp14
case 8199: // numsp
case 8200: // puncsp
case 8201: // thinsp
case 8202: // hairsp
case 8239: // narrow space
case 8287: // medium space
if (mergeWhitespace) {
sb.append(' ');
mergeWhitespace = false;
}
sb.append(' ');
break;
case 173: // soft hyphen
case 8203: // zero width space
case 8204: // zero width non-joiner
case 8205: // zero width joiner
case 8206: // lrm
case 8207: // rlm
case 8288: // word joiner
case 8289: // apply function
case 8290: // invisible times
case 8291: // invisible separator
continue;
default:
if (Character.getType(c) == 16) {
continue;
}
if (mergeWhitespace) {
if (sb.length() > 0) {
sb.append(' ');
} else if (previousText != null && !previousText.isEndTrimmed()) {
sb.append(' ');
previousText = null;
}
mergeWhitespace = false;
}
sb.appendCodePoint(c);
if (previousText != null && previousText.isEndTrimmed()) {
CTText previous = previousText.getText();
previous.setStringValue(previous.getStringValue() + ' ');
previous.setSpace(SpaceAttribute.Space.PRESERVE);
previousText = null;
}
break;
}
}
addText(ctr, sb, endTrimmed);
StringUtil.releaseBuilder(sb);
// 应用样式
applyTextStyle(ctr);
if (!(currentRun instanceof XWPFHyperlinkRun)) {
currentRun = null;
}
}
private void addText(CTR ctr, StringBuilder sb, boolean endTrimmed) {
if (sb.length() > 0) {
CTText ctText = ctr.addNewT();
String text = sb.toString();
ctText.setStringValue(text);
if (text.charAt(0) == ' ' || text.charAt(sb.length() - 1) == ' ') {
ctText.setSpace(SpaceAttribute.Space.PRESERVE);
}
sb.delete(0, sb.length());
previousText = new TextWrapper(ctText, endTrimmed);
}
}
/**
* 应用文本样式
*
* @param ctr CTR
*/
private void applyTextStyle(CTR ctr) {
CTRPr rPr = RenderUtils.getRPr(ctr);
// 字体,如果声明了全局字体则忽略样式声明
String fontFamily = StringUtils.isBlank(globalFont) ? getPropertyValue(HtmlConstants.CSS_FONT_FAMILY) : globalFont;
if (StringUtils.isNotBlank(fontFamily)) {
CTFonts ctFonts = rPr.addNewRFonts();
// ASCII
ctFonts.setAscii(fontFamily);
// High ANSI
ctFonts.setHAnsi(fontFamily);
// Complex Script
ctFonts.setCs(fontFamily);
// East Asian
ctFonts.setEastAsia(fontFamily);
}
// 字号
if (globalFontSize == null) {
String fontSize = getPropertyValue(HtmlConstants.CSS_FONT_SIZE);
if (StringUtils.isNotBlank(fontSize)) {
int sz = getInheritedFontSizeInHalfPoints();
rPr.addNewSz().setVal(BigInteger.valueOf(sz));
}
} else {
// 如果定义了全局字号则忽略样式声明
rPr.addNewSz().setVal(globalFontSize);
}
// 加粗
String fontWeight = getPropertyValue(HtmlConstants.CSS_FONT_WEIGHT);
if (fontWeight.contains(HtmlConstants.BOLD)) {
rPr.addNewB();
} else if (NumberUtils.isParsable(fontWeight) && Float.parseFloat(fontWeight) > 500) {
rPr.addNewB();
}
// 斜体
String fontStyle = getPropertyValue(HtmlConstants.CSS_FONT_STYLE);
if (HtmlConstants.ITALIC.equals(fontStyle) || HtmlConstants.OBLIQUE.equals(fontStyle)) {
rPr.addNewI();
}
// 颜色
String color = getPropertyValue(HtmlConstants.CSS_COLOR);
if (StringUtils.isNotBlank(color)) {
String hex = Colors.fromStyle(color);
RenderUtils.getColor(rPr).setVal(hex);
}
String caps = getPropertyValue(HtmlConstants.CSS_FONT_VARIANT_CAPS);
if (HtmlConstants.SMALL_CAPS.equals(caps)) {
rPr.addNewSmallCaps();
}
// 中划线/下划线
String textDecoration = getPropertyValue(HtmlConstants.CSS_TEXT_DECORATION_LINE);
if (HtmlConstants.NONE.equals(textDecoration)) {
RenderUtils.getUnderline(rPr).setVal(STUnderline.NONE);
} else {
if (StringUtils.contains(textDecoration, HtmlConstants.LINE_THROUGH)) {
rPr.addNewStrike();
}
if (StringUtils.contains(textDecoration, HtmlConstants.UNDERLINE)) {
CTUnderline ctUnderline = RenderUtils.getUnderline(rPr);
String textDecorationStyle = getPropertyValue(HtmlConstants.CSS_TEXT_DECORATION_STYLE);
ctUnderline.setVal(RenderUtils.underline(textDecorationStyle));
String textDecorationColor = getPropertyValue(HtmlConstants.CSS_TEXT_DECORATION_COLOR);
if (StringUtils.isNotBlank(textDecorationColor)) {
String hex = Colors.fromStyle(textDecorationColor);
ctUnderline.setColor(hex);
}
}
}
// 上下标
String verticalAlign = getPropertyValue(HtmlConstants.CSS_VERTICAL_ALIGN);
if (HtmlConstants.SUPER.equals(verticalAlign)) {
rPr.addNewVertAlign().setVal(STVerticalAlignRun.SUPERSCRIPT);
} else if (HtmlConstants.SUB.equals(verticalAlign)) {
rPr.addNewVertAlign().setVal(STVerticalAlignRun.SUBSCRIPT);
}
// FIXME 段落边框与行内边框分离,行内只有全边框,段落分四边
// 背景色
String backgroundColor = getPropertyValue(HtmlConstants.CSS_BACKGROUND_COLOR, true);
if (StringUtils.isNotBlank(backgroundColor)) {
String hex = Colors.fromStyle(backgroundColor, null);
if (hex != null) {
CTShd ctShd = rPr.addNewShd();
ctShd.setFill(hex);
ctShd.setVal(STShd.CLEAR);
}
}
// 可见性
String visibility = getPropertyValue(HtmlConstants.CSS_VISIBILITY);
if (HtmlConstants.HIDDEN.equals(visibility) || HtmlConstants.COLLAPSE.equals(visibility)) {
rPr.addNewVanish();
}
}
/**
* 渲染图片
*
* @param pictureData 图片数据流
* @param pictureType 图片类型
* @param filename 文件名
* @param width 宽度
* @param height 高度
* @param svgData SVG数据
*/
public void renderPicture(InputStream pictureData, int pictureType, String filename, int width, int height, byte[] svgData)
throws IOException, InvalidFormatException {
CTR ctr = newRun();
XWPFPicture xwpfPicture = currentRun.addPicture(pictureData, pictureType, filename, width, height);
CTR r = currentRun.getCTR();
boolean isSvg = svgData != null;
if (isSvg) {
attachSvgData(xwpfPicture, svgData);
}
CSSStyleDeclarationImpl styleDeclaration = currentElementStyle();
String cssFloat = styleDeclaration.getPropertyValue(HtmlConstants.CSS_FLOAT);
boolean floatLeft = HtmlConstants.LEFT.equals(cssFloat);
boolean floatRight = !floatLeft && HtmlConstants.RIGHT.equals(cssFloat);
boolean floatCenter = !floatLeft && !floatRight
&& HtmlConstants.AUTO.equals(styleDeclaration.getPropertyValue(HtmlConstants.CSS_MARGIN_LEFT))
&& HtmlConstants.AUTO.equals(styleDeclaration.getPropertyValue(HtmlConstants.CSS_MARGIN_RIGHT));
// vertical-align seems not working
boolean floated = floatLeft || floatRight || floatCenter;
CTDrawing drawing = null;
if (r != ctr) {
int lastDrawingIndex = r.sizeOfDrawingArray() - 1;
drawing = r.getDrawingArray(lastDrawingIndex);
ctr.setDrawingArray(new CTDrawing[]{drawing});
r.removeDrawing(lastDrawingIndex);
drawing = ctr.getDrawingArray(0);
} else if (isSvg || floated) {
drawing = ctr.getDrawingArray(ctr.sizeOfDrawingArray() - 1);
}
previousDrawingRun = ctr;
if (drawing != null && drawing.sizeOfInlineArray() > 0) {
if (floated) {
previousDrawingRun = null;
CTAnchor ctAnchor = RenderUtils.inlineToAnchor(drawing);
CTPosH ctPosH = ctAnchor.addNewPositionH();
ctPosH.setRelativeFrom(STRelFromH.MARGIN);
if (floatCenter) {
moveContentToNewPrevParagraph(ctr);
ctAnchor.addNewWrapTopAndBottom();
ctPosH.setAlign(STAlignH.CENTER);
} else {
ctAnchor.addNewWrapSquare().setWrapText(STWrapText.LARGEST);
ctPosH.setAlign(floatRight ? STAlignH.RIGHT : STAlignH.LEFT);
}
CTPosV ctPosV = ctAnchor.addNewPositionV();
ctPosV.setRelativeFrom(STRelFromV.PARAGRAPH);
ctPosV.setAlign(STAlignV.TOP);
if (isSvg) {
CTNonVisualGraphicFrameProperties properties = ctAnchor.addNewCNvGraphicFramePr();
CTGraphicalObjectFrameLocking frameLocking = properties.addNewGraphicFrameLocks();
frameLocking.setNoChangeAspect(true);
}
} else if (isSvg) {
CTInline ctInline = drawing.getInlineArray(0);
CTNonVisualGraphicFrameProperties properties = ctInline.isSetCNvGraphicFramePr()
? ctInline.getCNvGraphicFramePr() : ctInline.addNewCNvGraphicFramePr();
CTGraphicalObjectFrameLocking frameLocking = properties.isSetGraphicFrameLocks()
? properties.getGraphicFrameLocks() : properties.addNewGraphicFrameLocks();
frameLocking.setNoChangeAspect(true);
}
}
}
/**
* 附加SVG数据
*
* @param xwpfPicture 图片
* @param svgData SVG数据
* @throws InvalidFormatException 非法格式
*/
private void attachSvgData(XWPFPicture xwpfPicture, byte[] svgData) throws InvalidFormatException {
CTPicture ctPicture = xwpfPicture.getCTPicture();
String svgRelId = getXWPFDocument().addPictureData(svgData, SVGPictureData.PICTURE_TYPE_SVG);
CTBlip blip = ctPicture.getBlipFill().getBlip();
if (blip != null) {
CTOfficeArtExtensionList extList = blip.isSetExtLst() ? blip.getExtLst() : blip.addNewExtLst();
CTOfficeArtExtension svgBitmap = extList.addNewExt();
svgBitmap.setUri(SVGRelation.SVG_URI);
XmlCursor cur = svgBitmap.newCursor();
cur.toEndToken();
cur.beginElement(SVGRelation.SVG_QNAME);
cur.insertNamespace(SVGRelation.SVG_PREFIX, SVGRelation.MS_SVG_NS);
cur.insertAttributeWithValue(SVGRelation.EMBED_TAG, svgRelId);
cur.dispose();
}
}
/**
* 将长度换算为EMU
*
* @param length 长度
* @return EMU
*/
public int lengthToEMU(CSSLength length) {
if (!length.isValid()) {
throw new UnsupportedOperationException("Invalid CSS length");
}
if (!length.getUnit().isRelative()) {
return length.toEMU();
}
double emu;
switch (length.getUnit()) {
case REM:
emu = length.unitValue() * getDefaultFontSize().toEMU();
break;
case EM:
emu = length.unitValue() * getInheritedFontSizeInHalfPoints() * Units.EMU_PER_POINT / 2;
break;
case VW:
emu = length.unitValue() * getPageWidth().toEMU();
break;
case VH:
emu = length.unitValue() * getPageHeight().toEMU();
break;
case VMIN:
emu = length.unitValue() * Math.min(getPageWidth().toEMU(), getPageHeight().toEMU());
break;
case VMAX:
emu = length.unitValue() * Math.max(getPageWidth().toEMU(), getPageHeight().toEMU());
break;
// Unable to determine the use of width or height as a relative length for percent unit
default:
throw new UnsupportedOperationException("Can not convert to EMU with length: " + length);
}
return (int) Math.rint(emu);
}
public NumberingContext getNumberingContext() {
return numberingContext;
}
public CSSLength getPageWidth() {
return pageWidth;
}
public CSSLength getPageHeight() {
return pageHeight;
}
public CSSLength getMarginTop() {
return marginTop;
}
public CSSLength getMarginRight() {
return marginRight;
}
public CSSLength getMarginBottom() {
return marginBottom;
}
public CSSLength getMarginLeft() {
return marginLeft;
}
public int getAvailablePageWidth() {
return availablePageWidth;
}
public int getAvailablePageHeight() {
return availablePageHeight;
}
public MathRenderConfig getMathRenderConfig() {
return mathRenderConfig;
}
public void setMathRenderConfig(MathRenderConfig mathRenderConfig) {
this.mathRenderConfig = mathRenderConfig;
}
public XWPFRun getCurrentRun() {
return currentRun;
}
public String getGlobalFont() {
return globalFont;
}
public BigInteger getGlobalFontSize() {
return globalFontSize;
}
public boolean isShowDefaultTableBorderInTableCell() {
return showDefaultTableBorderInTableCell;
}
public void setShowDefaultTableBorderInTableCell(boolean showDefaultTableBorderInTableCell) {
this.showDefaultTableBorderInTableCell = showDefaultTableBorderInTableCell;
}
public void setGlobalFont(String globalFont) {
this.globalFont = globalFont;
}
public void setGlobalFontSize(BigInteger globalFontSize) {
this.globalFontSize = globalFontSize;
}
public boolean isBlocked() {
return blockLevel > 0;
}
public void incrementBlockLevel() {
blockLevel++;
}
public void decrementBlockLevel() {
blockLevel--;
}
public void renderDocument(Document document) {
Element body = document.body();
Element html = body.parent();
if (html.hasAttr(HtmlConstants.ATTR_STYLE)) {
pushInlineStyle(getCssStyleDeclaration(html), html.isBlock());
}
if (body.hasAttr(HtmlConstants.ATTR_STYLE)) {
pushInlineStyle(getCssStyleDeclaration(body), body.isBlock());
}
for (Node node : body.childNodes()) {
renderNode(node);
}
globalCursor.dispose();
}
public void renderNode(Node node) {
boolean isElement = node instanceof Element;
if (isElement) {
Element element = ((Element) node);
renderElement(element);
} else if (node instanceof TextNode) {
renderText(((TextNode) node).getWholeText());
}
}
public void renderElement(Element element) {
if (log.isDebugEnabled()) {
log.info("Start rendering html tag: <{}{}>", element.normalName(), element.attributes());
}
if (element.tag().isFormListed() || element.tag().isFormSubmittable()) {
return;
}
CSSStyleDeclarationImpl cssStyleDeclaration = getCssStyleDeclaration(element);
String display = cssStyleDeclaration.getPropertyValue(HtmlConstants.CSS_DISPLAY);
if (HtmlConstants.NONE.equalsIgnoreCase(display)) {
return;
}
pushInlineStyle(cssStyleDeclaration, element.isBlock());
ElementRenderer elementRenderer = rendererProvider.get(element.normalName());
boolean blocked = false;
if (renderAsBlock(element, elementRenderer)) {
if (element.childNodeSize() == 0 && !HtmlConstants.KEEP_EMPTY_TAGS.contains(element.normalName())) {
popInlineStyle();
return;
}
if (!isBlocked()) {
// 复制段落中占位符之前的部分内容
moveContentToNewPrevParagraph(getRun().getCTR());
}
incrementBlockLevel();
blocked = true;
IBody container = getContainer();
boolean isTableTag = HtmlConstants.TAG_TABLE.equals(element.normalName());
adjustCursor(container, isTableTag);
if (isTableTag) {
globalCursor.push();
XWPFTable xwpfTable = container.insertNewTbl(globalCursor);
globalCursor.pop();
if (dedupeParagraph != null && !numberingContext.contains(dedupeParagraph)) {
if (!dedupeParagraph.equals(getRun().getParent()) && isEmptyParagraph(dedupeParagraph)) {
removeParagraph(container, dedupeParagraph);
}
unmarkDedupe();
}
// 新增时会自动创建一行一列,会影响自定义的表格渲染逻辑,故删除
xwpfTable.removeRow(0);
if (container.getPartType() == BodyType.TABLECELL && isShowDefaultTableBorderInTableCell()) {
CTTbl ctTbl = xwpfTable.getCTTbl();
CTTblBorders tblBorders = RenderUtils.getTblBorders(ctTbl);
tblBorders.addNewTop().setVal(STBorder.SINGLE);
tblBorders.addNewLeft().setVal(STBorder.SINGLE);
tblBorders.addNewBottom().setVal(STBorder.SINGLE);
tblBorders.addNewRight().setVal(STBorder.SINGLE);
tblBorders.addNewInsideH().setVal(STBorder.SINGLE);
tblBorders.addNewInsideV().setVal(STBorder.SINGLE);
}
RenderUtils.tableStyle(this, xwpfTable, cssStyleDeclaration);
} else if (shouldNewParagraph(element)) {
globalCursor.push();
XWPFParagraph xwpfParagraph = newParagraph(container, globalCursor);
globalCursor.pop();
if (xwpfParagraph == null) {
log.warn("Can not add new paragraph for element: {}, attributes: {}", element.tagName(), element.attributes().html());
}
RenderUtils.paragraphStyle(this, xwpfParagraph, cssStyleDeclaration);
} else {
RenderUtils.paragraphStyle(this, dedupeParagraph, cssStyleDeclaration);
}
}
if (elementRenderer != null) {
if (!elementRenderer.renderStart(element, this)) {
renderElementEnd(element, this, elementRenderer, blocked);
return;
}
}
for (Node child : element.childNodes()) {
renderNode(child);
}
renderElementEnd(element, this, elementRenderer, blocked);
}
private boolean isEmptyParagraph(XWPFParagraph paragraph) {
for (XWPFRun run : paragraph.getRuns()) {
if (StringUtils.isNotBlank(run.text())) {
return false;
}
if (!run.getEmbeddedPictures().isEmpty()) {
return false;
}
}
CTP ctp = paragraph.getCTP();
return ctp.sizeOfOMathArray() == 0 && ctp.sizeOfOMathParaArray() == 0;
}
private void removeParagraph(IBody container, XWPFParagraph paragraph) {
switch (container.getPartType()) {
case CONTENTCONTROL:
break;
case DOCUMENT:
XWPFDocument xwpfDocument = (XWPFDocument) container;
int posOfParagraph = xwpfDocument.getPosOfParagraph(paragraph);
xwpfDocument.removeBodyElement(posOfParagraph);
break;
case HEADER:
XWPFHeader xwpfHeader = (XWPFHeader) container;
xwpfHeader.removeParagraph(paragraph);
break;
case FOOTER:
XWPFFooter xwpfFooter = (XWPFFooter) container;
xwpfFooter.removeParagraph(paragraph);
break;
case FOOTNOTE:
XWPFFootnote xwpfFootnote = (XWPFFootnote) container;
xwpfFootnote.getParagraphs().remove(paragraph);
break;
case TABLECELL:
XWPFTableCell xwpfTableCell = (XWPFTableCell) container;
xwpfTableCell.removeParagraph(xwpfTableCell.getParagraphs().indexOf(paragraph));
break;
}
}
/**
* HTML元素是否按照块状进行渲染
*
* @param element HTML元素
* @return 是否按照块状进行渲染
*/
public boolean renderAsBlock(Element element) {
return renderAsBlock(element, rendererProvider.get(element.normalName()));
}
/**
* HTML元素是否按照块状进行渲染
*
* @param element HTML元素
* @param elementRenderer 元素渲染器
* @return 是否按照块状进行渲染
*/
private boolean renderAsBlock(Element element, ElementRenderer elementRenderer) {
return element.isBlock() && (elementRenderer == null || elementRenderer.renderAsBlock());
}
private boolean shouldNewParagraph(Element element) {
if (dedupeParagraph == null) {
return true;
// return !HtmlConstants.TAG_HR.equals(element.normalName());
}
boolean newParagraph = false;
XmlCursor xmlCursor = dedupeParagraph.getCTP().newCursor();
xmlCursor.push();
if (xmlCursor.toPrevSibling()) {
if (XmlUtils.P_QNAME.equals(xmlCursor.getName())) {
newParagraph = removeLastBrRun(xmlCursor);
}
}
if (!newParagraph) {
xmlCursor.pop();
newParagraph = removeLastBrRun(xmlCursor);
}
xmlCursor.dispose();
return newParagraph;
}
private boolean removeLastBrRun(XmlCursor xmlCursor) {
boolean removed = false;
if (xmlCursor.toLastChild()) {
if (XmlUtils.R_QNAME.equals(xmlCursor.getName())) {
xmlCursor.push();
if (xmlCursor.toFirstChild() && XmlUtils.BR_QNAME.equals(xmlCursor.getName()) && !xmlCursor.toNextSibling()) {
xmlCursor.pop();
xmlCursor.removeXml();
unmarkDedupe();
removed = true;
} else {
xmlCursor.pop();
}
}
}
return removed;
}
private void adjustCursor(IBody container, boolean isTableTag) {
if (XmlUtils.R_QNAME.equals(globalCursor.getName())) {
globalCursor.push();
globalCursor.toParent();
}
globalCursor.push();
// 如果是表格,检查当前word容器的前一个兄弟元素是否为表格,是则插入一个段落,防止表格粘连在一起
if (isTableTag && globalCursor.toPrevSibling()) {
if (XmlUtils.TBL_QNAME.equals(globalCursor.getName())) {
// pop() is safer than toNextSibling()
globalCursor.pop();
globalCursor.push();
XWPFParagraph paragraph = newParagraph(container, globalCursor);
unmarkDedupe();
RenderUtils.paragraphStyle(this, paragraph, CSSStyleUtils.EMPTY_STYLE);
}
}
globalCursor.pop();
}
private void renderElementEnd(Element element, HtmlRenderContext context, ElementRenderer elementRenderer, boolean blocked) {
if (elementRenderer != null) {
elementRenderer.renderEnd(element, context);
}
context.popInlineStyle();
if (blocked) {
context.decrementBlockLevel();
}
}
private void moveContentToNewPrevParagraph(CTR ctr) {
XmlCursor rCursor = ctr.newCursor();
boolean hasPrevSibling = false;
while (rCursor.toPrevSibling()) {
XmlObject object = rCursor.getObject();
if (object instanceof CTMarkupRange) {
continue;
}
if (!XmlUtils.PPR_QNAME.equals(rCursor.getName())) {
hasPrevSibling = true;
break;
}
}
if (!hasPrevSibling) {
rCursor.dispose();
return;
}
rCursor.toParent();
rCursor.push();
CTP ctp = ((CTP) rCursor.getObject());
XWPFParagraph paragraph = getContainer().getParagraph(ctp);
XWPFParagraph newParagraph = getContainer().insertNewParagraph(rCursor);
XmlCursor pCursor = newParagraph.getCTP().newCursor();
pCursor.toEndToken();
rCursor.pop();
rCursor.toFirstChild();
XmlObject previousRun = null;
if (previousText != null) {
XmlCursor tCursor = previousText.getText().newCursor();
tCursor.toParent();
previousRun = tCursor.getObject();
tCursor.dispose();
}
while (true) {
XmlObject object = rCursor.getObject();
if (ctr == object) break;
QName name = rCursor.getName();
if (XmlUtils.PPR_QNAME.equals(name)) {
rCursor.copyXml(pCursor);
rCursor.toNextSibling();
} else if (XmlUtils.BOOKMARK_START_QNAME.equals(name) || XmlUtils.BOOKMARK_END_QNAME.equals(name)) {
rCursor.toNextSibling();
} else {
if (previousDrawingRun == object) {
previousDrawingRun = null;
}
if (previousRun == object) {
previousText = null;
}
// moveXml附带了toNextSibling的效果
rCursor.moveXml(pCursor);
}
}
rCursor.dispose();
pCursor.dispose();
XWPFParagraphRuns runs = new XWPFParagraphRuns(paragraph);
for (int i = runs.runCount() - ctp.getRList().size() - 1; i >= 0; i--) {
runs.remove(i);
}
}
public CSSStyleDeclarationImpl getCssStyleDeclaration(Element element) {
String style = element.attr(HtmlConstants.ATTR_STYLE);
CSSStyleDeclarationImpl cssStyleDeclaration = CSSStyleUtils.parse(style);
CSSStyleUtils.split(cssStyleDeclaration);
return cssStyleDeclaration;
}
/**
* 保存当前指针位置并移动到目标指针位置
*
* @param targetCursor 目标指针
*/
public void pushCursor(XmlCursor targetCursor) {
globalCursor.push();
globalCursor.toCursor(targetCursor);
}
/**
* 返回之前保存的指针位置
*
* @return 是否返回成功
*/
public boolean popCursor() {
return globalCursor.pop();
}
/**
* @return 指针当前指向的对象
*/
public XmlObject currentCursorObject() {
return globalCursor.getObject();
}
/**
* 标记段落以防止块状元素嵌套产生多余的空段落
*
* @param paragraph 段落
*/
public void markDedupe(XWPFParagraph paragraph) {
dedupeParagraph = paragraph;
}
/**
* 取消段落防重标记
*/
public void unmarkDedupe() {
dedupeParagraph = null;
}
/**
* 文本封装类,用于空白字符折叠处理
*/
private static class TextWrapper {
private final CTText text;
private final boolean endTrimmed;
public TextWrapper(CTText text, boolean endTrimmed) {
this.text = text;
this.endTrimmed = endTrimmed;
}
public CTText getText() {
return text;
}
public boolean isEndTrimmed() {
return endTrimmed;
}
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/HtmlRenderPolicy.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html;
import com.deepoove.poi.policy.AbstractRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.BodyType;
import org.apache.poi.xwpf.usermodel.IBody;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.xmlbeans.XmlCursor;
import org.ddr.poi.html.tag.ARenderer;
import org.ddr.poi.html.tag.BigRenderer;
import org.ddr.poi.html.tag.BoldRenderer;
import org.ddr.poi.html.tag.BreakRenderer;
import org.ddr.poi.html.tag.DeleteRenderer;
import org.ddr.poi.html.tag.FigureCaptionRenderer;
import org.ddr.poi.html.tag.FigureRenderer;
import org.ddr.poi.html.tag.HeaderBreakRenderer;
import org.ddr.poi.html.tag.HeaderRenderer;
import org.ddr.poi.html.tag.ImageRenderer;
import org.ddr.poi.html.tag.ItalicRenderer;
import org.ddr.poi.html.tag.LaTeXRenderer;
import org.ddr.poi.html.tag.ListItemRenderer;
import org.ddr.poi.html.tag.ListRenderer;
import org.ddr.poi.html.tag.MarkRenderer;
import org.ddr.poi.html.tag.MathRenderer;
import org.ddr.poi.html.tag.OmittedRenderer;
import org.ddr.poi.html.tag.PreRenderer;
import org.ddr.poi.html.tag.RubyRenderer;
import org.ddr.poi.html.tag.SmallRenderer;
import org.ddr.poi.html.tag.SubscriptRenderer;
import org.ddr.poi.html.tag.SuperscriptRenderer;
import org.ddr.poi.html.tag.SvgRenderer;
import org.ddr.poi.html.tag.TableCellRenderer;
import org.ddr.poi.html.tag.TableRenderer;
import org.ddr.poi.html.tag.UnderlineRenderer;
import org.ddr.poi.html.tag.WalkThroughRenderer;
import org.ddr.poi.html.util.CSSLength;
import org.ddr.poi.html.util.JsoupUtils;
import org.ddr.poi.util.XmlUtils;
import org.jsoup.nodes.Document;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* HTML字符串渲染策略
*
* @author Draco
* @since 2021-02-07
*/
public class HtmlRenderPolicy extends AbstractRenderPolicy<String> {
private final Map<String, ElementRenderer> elRenderers;
private final HtmlRenderConfig config;
public HtmlRenderPolicy() {
this(new HtmlRenderConfig());
}
@Deprecated
public HtmlRenderPolicy(String globalFont, CSSLength globalFontSize) {
this(new HtmlRenderConfig());
config.setGlobalFont(globalFont);
config.setGlobalFontSize(globalFontSize);
}
public HtmlRenderPolicy(HtmlRenderConfig config) {
ElementRenderer[] renderers = {
new ARenderer(),
new BigRenderer(),
new BoldRenderer(),
new BreakRenderer(),
new DeleteRenderer(),
new FigureRenderer(),
new FigureCaptionRenderer(),
new HeaderBreakRenderer(),
new HeaderRenderer(),
new ImageRenderer(),
new ItalicRenderer(),
new LaTeXRenderer(),
new ListItemRenderer(),
new ListRenderer(),
new MarkRenderer(),
new MathRenderer(),
new OmittedRenderer(),
new PreRenderer(),
new RubyRenderer(),
new SmallRenderer(),
new SubscriptRenderer(),
new SuperscriptRenderer(),
new SvgRenderer(),
new TableCellRenderer(),
new TableRenderer(),
new UnderlineRenderer(),
new WalkThroughRenderer()
};
elRenderers = new HashMap<>(renderers.length);
for (ElementRenderer renderer : renderers) {
for (String tag : renderer.supportedTags()) {
elRenderers.put(tag, renderer);
}
}
this.config = config;
// custom tag renderer will overwrite the built-in renderer
if (config.getCustomRenderers() != null) {
for (ElementRenderer customRenderer : config.getCustomRenderers()) {
for (String tag : customRenderer.supportedTags()) {
elRenderers.put(tag, customRenderer);
}
}
}
}
public HtmlRenderConfig getConfig() {
return config;
}
@Override
protected boolean validate(String data) {
return StringUtils.isNotEmpty(data);
}
@Override
public void doRender(RenderContext<String> context) throws Exception {
Document document = JsoupUtils.parse(context.getData());
document.outputSettings().prettyPrint(false).indentAmount(0);
HtmlRenderContext htmlRenderContext = new HtmlRenderContext(context, elRenderers::get);
htmlRenderContext.setGlobalFont(config.getGlobalFont());
if (config.getGlobalFontSizeInHalfPoints() > 0) {
htmlRenderContext.setGlobalFontSize(BigInteger.valueOf(config.getGlobalFontSizeInHalfPoints()));
}
htmlRenderContext.getNumberingContext().setIndent(config.getNumberingIndent());
htmlRenderContext.getNumberingContext().setHanging(config.getNumberingHanging());
htmlRenderContext.getNumberingContext().setSpacing(config.getNumberingSpacing());
htmlRenderContext.setShowDefaultTableBorderInTableCell(config.isShowDefaultTableBorderInTableCell());
htmlRenderContext.setMathRenderConfig(config.getMathRenderConfig());
htmlRenderContext.renderDocument(document);
}
@Override
protected void afterRender(RenderContext<String> context) {
boolean hasSibling = hasSibling(context.getRun());
clearPlaceholder(context, !hasSibling);
IBody container = context.getContainer();
if (container.getPartType() == BodyType.TABLECELL) {
// 单元格的最后一个元素应为p,否则可能无法正常打开文件
List<IBodyElement> bodyElements = container.getBodyElements();
if (bodyElements.isEmpty() || bodyElements.get(bodyElements.size() - 1).getElementType() != BodyElementType.PARAGRAPH) {
((XWPFTableCell) container).addParagraph();
}
}
}
private boolean hasSibling(XWPFRun run) {
boolean hasSibling = false;
CTR ctr = run.getCTR();
XmlCursor xmlCursor = ctr.newCursor();
xmlCursor.push();
while (xmlCursor.toNextSibling()) {
if (isValidSibling(xmlCursor)) {
hasSibling = true;
break;
}
}
if (!hasSibling) {
xmlCursor.pop();
while (xmlCursor.toPrevSibling()) {
if (isValidSibling(xmlCursor)) {
hasSibling = true;
break;
}
}
}
xmlCursor.dispose();
return hasSibling;
}
private boolean isValidSibling(XmlCursor cursor) {
return !XmlUtils.INVALID_R_SIBLINGS.contains(cursor.getName());
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/ARenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
/**
* a标签渲染器
*
* @author Draco
* @since 2021-03-31
*/
public class ARenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_A};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
String href = element.attr(HtmlConstants.ATTR_HREF);
context.startHyperlink(href);
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.endHyperlink();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/BigRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.jsoup.nodes.Element;
/**
* big标签渲染器,HTML5不支持big
*
* @author Draco
* @since 2021-02-23
*/
@Deprecated
public class BigRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_BIG};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
context.pushInlineStyle(CSSStyleUtils.parse(HtmlConstants.DEFINED_LARGER), element.isBlock());
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.popInlineStyle();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/BoldRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.jsoup.nodes.Element;
/**
* 加粗标签渲染器
*
* @author Draco
* @since 2021-02-23
*/
public class BoldRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_B, HtmlConstants.TAG_STRONG};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
context.pushInlineStyle(CSSStyleUtils.parse(HtmlConstants.DEFINED_BOLD), element.isBlock());
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.popInlineStyle();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/BreakRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.apache.poi.xwpf.usermodel.IRunBody;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
/**
* br标签渲染器
*
* @author Draco
* @since 2021-02-09
*/
public class BreakRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_BR};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
CTR ctr = context.newRun();
ctr.addNewBr();
IRunBody parent = context.getCurrentRun().getParent();
if (parent instanceof XWPFParagraph) {
context.markDedupe((XWPFParagraph) parent);
}
return false;
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/DeleteRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.jsoup.nodes.Element;
/**
* 删除线标签渲染器
*
* @author Draco
* @since 2021-02-23
*/
public class DeleteRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_S, HtmlConstants.TAG_DEL, HtmlConstants.TAG_STRIKE};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
context.pushInlineStyle(CSSStyleUtils.parse(HtmlConstants.DEFINED_STRIKE), element.isBlock());
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.popInlineStyle();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/FigureCaptionRenderer.java
================================================
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
/**
* figcaption标签渲染器
*
* @author Draco
* @since 2022-11-03
*/
public class FigureCaptionRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_FIGURE_CAPTION};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
context.markDedupe(context.getClosestParagraph());
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.unmarkDedupe();
}
/**
* @return 支持的HTML标签
*/
@Override
public String[] supportedTags() {
return TAGS;
}
/**
* @return 是否为块状渲染,如果为true在Word中会另起一个Paragraph
*/
@Override
public boolean renderAsBlock() {
return true;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/FigureRenderer.java
================================================
package org.ddr.poi.html.tag;
import com.steadystate.css.dom.CSSStyleDeclarationImpl;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.JsoupUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
* figure标签渲染器
*
* @author Draco
* @since 2022-11-03
*/
public class FigureRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_FIGURE};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure#usage_notes
Elements captions = JsoupUtils.children(element, HtmlConstants.TAG_FIGURE_CAPTION);
if (captions.size() > 1) {
captions.remove(0);
captions.remove();
}
XWPFParagraph paragraph = context.getClosestParagraph();
context.markDedupe(paragraph);
CSSStyleDeclarationImpl styleDeclaration = context.currentElementStyle();
String cssFloat = styleDeclaration.getPropertyValue(HtmlConstants.CSS_FLOAT);
if (HtmlConstants.LEFT.equals(cssFloat)) {
paragraph.setAlignment(ParagraphAlignment.LEFT);
styleDeclaration.setTextAlign(HtmlConstants.LEFT);
} else if (HtmlConstants.RIGHT.equals(cssFloat)) {
paragraph.setAlignment(ParagraphAlignment.RIGHT);
styleDeclaration.setTextAlign(HtmlConstants.RIGHT);
} else {
paragraph.setAlignment(ParagraphAlignment.CENTER);
styleDeclaration.setTextAlign(HtmlConstants.CENTER);
}
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.unmarkDedupe();
}
/**
* @return 支持的HTML标签
*/
@Override
public String[] supportedTags() {
return TAGS;
}
/**
* @return 是否为块状渲染,如果为true在Word中会另起一个Paragraph
*/
@Override
public boolean renderAsBlock() {
return true;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/HeaderBreakRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.RenderUtils;
import org.jsoup.nodes.Element;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPBdr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
import java.math.BigInteger;
/**
* hr标签渲染器
*
* @author Draco
* @since 2021-02-18
*/
public class HeaderBreakRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_HR};
/**
* 线粗细,相当于3px
*/
private static final BigInteger SIZE = BigInteger.valueOf(6);
/**
* 间距
*/
private static final BigInteger SPACE = BigInteger.ONE;
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
CTP ctp = context.getClosestParagraph().getCTP();
CTPBdr pBdr = RenderUtils.getPBdr(RenderUtils.getPPr(ctp));
CTBorder ctBorder = pBdr.addNewBottom();
ctBorder.setVal(STBorder.SINGLE);
ctBorder.setSz(SIZE);
ctBorder.setSpace(SPACE);
ctBorder = pBdr.addNewBetween();
ctBorder.setVal(STBorder.SINGLE);
ctBorder.setSz(SIZE);
ctBorder.setSpace(SPACE);
return false;
}
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.unmarkDedupe();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return true;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/HeaderRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.ddr.poi.html.util.RenderUtils;
import org.jsoup.nodes.Element;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import java.math.BigInteger;
/**
* h1~h6标签渲染器
*
* @author Draco
* @since 2021-02-24
*/
public class HeaderRenderer implements ElementRenderer {
private static final String[] TAGS = {
HtmlConstants.TAG_H1, HtmlConstants.TAG_H2, HtmlConstants.TAG_H3,
HtmlConstants.TAG_H4, HtmlConstants.TAG_H5, HtmlConstants.TAG_H6
};
/**
* 各级别标题对应字号
*/
private static final String[] FONT_SIZES = {"24pt", "18pt", "14pt", "12pt", "10pt", "7.5pt"};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
int index = Integer.parseInt(element.normalName().substring(1)) - 1;
String fontSizeStyle = HtmlConstants.inlineStyle(HtmlConstants.CSS_FONT_SIZE, FONT_SIZES[index]);
context.pushInlineStyle(CSSStyleUtils.parse(HtmlConstants.DEFINED_BOLD + fontSizeStyle), element.isBlock());
CTP ctp = context.getClosestParagraph().getCTP();
CTDecimalNumber ctDecimalNumber = CTDecimalNumber.Factory.newInstance();
ctDecimalNumber.setVal(BigInteger.valueOf(index));
RenderUtils.getPPr(ctp).setOutlineLvl(ctDecimalNumber);
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.popInlineStyle();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return true;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/ImageRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import com.drew.imaging.FileType;
import com.drew.imaging.FileTypeDetector;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.SVGPictureData;
import org.ddr.image.ImageInfo;
import org.ddr.image.ImageType;
import org.ddr.image.MetadataReader;
import org.ddr.image.MetadataReaders;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSLength;
import org.ddr.poi.math.MathMLUtils;
import org.ddr.poi.util.ByteArrayCopyStream;
import org.ddr.poi.util.HttpURLConnectionUtils;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Iterator;
/**
* img标签渲染器
*
* @author Draco
* @since 2021-02-09
*/
public class ImageRenderer implements ElementRenderer {
private static final Logger log = LoggerFactory.getLogger(ImageRenderer.class);
private static final String[] TAGS = {HtmlConstants.TAG_IMG};
private static final String HTTP = "http";
private static final String DOUBLE_SLASH = "//";
private static final String DATA_PREFIX = "data:";
private static final String COMMENT_MATH_PREFIX = "<!--MathML: <math ";
private static final String COMMENT_MATH_SUFFIX = "</math>-->";
static {
SVGPictureData.initRelation();
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
String src = element.attr(HtmlConstants.ATTR_SRC);
if (StringUtils.startsWithIgnoreCase(src, HTTP)) {
handleRemoteImage(element, context, src);
} else if (StringUtils.startsWith(src, DOUBLE_SLASH)) {
// 某些图片链接为了跟随网站协议而隐去了协议名称
handleRemoteImage(element, context, HTTP + HtmlConstants.COLON + src);
} else if (StringUtils.startsWith(src, DATA_PREFIX)) {
handleData(element, context, src);
}
return false;
}
/**
* 处理Data URL
*
* @param element HTML元素
* @param context 渲染上下文
* @param src 数据
*/
private void handleData(Element element, HtmlRenderContext context, String src) {
int index = src.indexOf(HtmlConstants.COMMA.charAt(0));
String data = src.substring(index + 1);
String declaration = src.substring(0, index);
String format = StringUtils.substringBetween(declaration, HtmlConstants.SLASH, HtmlConstants.SEMICOLON);
// org.apache.poi.sl.usermodel.PictureData.PictureType
if (format.contains(HtmlConstants.MINUS)) {
format = StringUtils.substringAfterLast(format, HtmlConstants.MINUS);
} else if (format.contains(HtmlConstants.PLUS)) {
format = StringUtils.substringBefore(format, HtmlConstants.PLUS);
}
byte[] bytes;
if (declaration.contains("base64")) {
try {
bytes = Base64.getDecoder().decode(data);
} catch (Exception e) {
log.warn("Failed to load image due to illegal base64 data: {}", src);
return;
}
} else {
if (data.startsWith(HtmlConstants.PERCENT)) {
try {
data = URLDecoder.decode(data, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
log.warn("Failed to load image due to illegal data url: {}", src);
return;
}
}
// wiris support
int startOfMath = data.indexOf(COMMENT_MATH_PREFIX);
if (startOfMath >= 0) {
try {
int endOfMath = data.indexOf(COMMENT_MATH_SUFFIX, startOfMath + COMMENT_MATH_PREFIX.length());
String math = data.substring(startOfMath + 12, endOfMath + 7);
MathMLUtils.renderTo(context.getClosestParagraph(), context.newRun(), math, context.getMathRenderConfig());
return;
} catch (Exception e) {
log.warn("Failed to render math in wiris svg, will try to render as svg image: {}", data, e);
}
}
bytes = data.getBytes(StandardCharsets.UTF_8);
}
boolean svg = HtmlConstants.TAG_SVG.equals(format);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
ImageInfo info = analyzeImage(inputStream, svg);
if (info == null) {
log.warn("Illegal image url: {}", src);
return;
}
addPicture(element, context, info.getStream(), info.getRawType(), info.getWidth(), info.getHeight(), svg ? bytes : null);
} catch (IOException | InvalidFormatException e) {
log.warn("Failed to load image: {}", src, e);
}
}
/**
* 根据图片反推类型
*
* @param image 图片
* @return 图片类型
*/
protected ImageType typeOf(BufferedImage image) {
return image.getColorModel().hasAlpha() ? ImageType.PNG : ImageType.JPG;
}
/**
* 处理远程图片
*
* @param element HTML元素
* @param context 渲染上下文
* @param src 图片链接地址
*/
private void handleRemoteImage(Element element, HtmlRenderContext context, String src) {
HttpURLConnection connect = null;
try {
connect = HttpURLConnectionUtils.connect(src);
HttpURLConnectionUtils.initUserAgent(connect);
int firstSlashPosition = src.indexOf('/', src.indexOf("://") + 3);
connect.setRequestProperty("Referrer", src.substring(0, firstSlashPosition));
InputStream urlStream = connect.getInputStream();
boolean svg = StringUtils.contains(connect.getHeaderField("content-type"), HtmlConstants.TAG_SVG);
ByteArrayCopyStream outputStream = new ByteArrayCopyStream(urlStream.available());
IOUtils.copy(urlStream, outputStream);
final byte[] svgData = svg ? outputStream.toByteArray() : null;
ByteArrayInputStream inputStream = outputStream.toInput();
ImageInfo info = analyzeImage(inputStream, svg);
if (info == null) {
log.warn("Illegal image url: {}", src);
return;
}
addPicture(element, context, info.getStream(), info.getRawType(), info.getWidth(), info.getHeight(), svgData);
} catch (IOException | InvalidFormatException e) {
log.warn("Failed to load image: {}", src, e);
} finally {
IOUtils.close(connect);
}
}
private ImageInfo analyzeImage(ByteArrayInputStream inputStream, boolean svg) throws IOException, InvalidFormatException {
final long length = inputStream.available();
// actual image data stream
ByteArrayInputStream stream = inputStream;
ImageType type = null;
Dimension dimension = null;
if (svg) {
BufferedImage image = ImageIO.read(inputStream);
inputStream.reset();
type = typeOf(image);
ByteArrayCopyStream imageStream = new ByteArrayCopyStream(image.getData().getDataBuffer().getSize());
ImageIO.write(image, type.getExtension(), imageStream);
stream = imageStream.toInput();
dimension = new Dimension(image.getWidth(), image.getHeight());
} else {
FileType fileType = FileTypeDetector.detectFileType(inputStream);
for (MetadataReader metadataReader : MetadataReaders.INSTANCES) {
if (metadataReader.canRead(fileType)) {
try {
// FIXME metadata-extractor 一直未发版支持 AVIF 格式,会被归为 QuickTime 格式
if (fileType == FileType.QuickTime) {
fileType = FileType.Heif;
}
Metadata metadata = ImageMetadataReader.readMetadata(inputStream, length, fileType);
type = metadataReader.getType(metadata);
dimension = metadataReader.getDimension(metadata);
break;
} catch (ImageProcessingException ignored) {
}
}
}
inputStream.reset();
if (dimension == null) {
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(inputStream);
while (imageReaders.hasNext()) {
ImageReader reader = imageReaders.next();
try {
dimension = new Dimension(reader.getWidth(0), reader.getHeight(0));
break;
} catch (IOException ignored) {
}
}
if (dimension == null) {
BufferedImage image = ImageIO.read(inputStream);
inputStream.reset();
if (image == null) {
return null;
}
if (type == null) {
type = typeOf(image);
}
dimension = new Dimension(image.getWidth(), image.getHeight());
}
}
}
return new ImageInfo(stream, type, dimension);
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
/**
* 添加图片
*
* @param element HTML元素
* @param context 渲染上下文
* @param inputStream 图片数据流
* @param type 图片类型
* @param widthInPixels 图片宽度(像素)
* @param heightInPixels 图片高度(像素)
* @param svgData SVG数据
*/
protected void addPicture(Element element, HtmlRenderContext context, InputStream inputStream, int type,
int widthInPixels, int heightInPixels,
byte[] svgData) throws InvalidFormatException, IOException {
// 容器限制宽度
int containerWidth = context.getAvailableWidthInEMU();
// int containerHeight = context.getAvailablePageHeight();
// 图片原始宽高
int widthInEMU = Units.pixelToEMU(widthInPixels);
int heightInEMU = Units.pixelToEMU(heightInPixels);
float naturalAspect = 1f * widthInEMU / heightInEMU;
int declaredWidth = widthInEMU;
int declaredHeight = heightInEMU;
int maxWidthInEMU = containerWidth;
int maxHeightInEMU = Integer.MAX_VALUE;
String width = context.getPropertyValue(HtmlConstants.CSS_WIDTH);
if (width.length() > 0) {
CSSLength cssLength = CSSLength.of(width);
if (cssLength.isValid()) {
declaredWidth = context.computeLengthInEMU(cssLength, widthInEMU, containerWidth);
}
} else {
// width attribute is overridden by style, the same to height
// https://css-tricks.com/whats-the-difference-between-width-height-in-css-and-width-height-html-attributes/
width = element.attr(HtmlConstants.ATTR_WIDTH);
if (NumberUtils.isParsable(width)) {
width += HtmlConstants.PX;
CSSLength cssLength = CSSLength.of(width);
declaredWidth = context.computeLengthInEMU(cssLength, widthInEMU, containerWidth);
}
}
String maxWidth = context.getPropertyValue(HtmlConstants.CSS_MAX_WIDTH);
if (maxWidth.length() > 0) {
CSSLength cssLength = CSSLength.of(maxWidth);
if (cssLength.isValid()) {
// restrained by container
maxWidthInEMU = Math.min(context.computeLengthInEMU(cssLength, widthInEMU, containerWidth), containerWidth);
}
}
String height = context.getPropertyValue(HtmlConstants.CSS_HEIGHT);
if (height.length() > 0) {
CSSLength cssLength = CSSLength.of(height);
if (cssLength.isValid()) {
declaredHeight = context.computeLengthInEMU(cssLength, heightInEMU, Integer.MAX_VALUE);
}
} else {
height = element.attr(HtmlConstants.ATTR_HEIGHT);
if (NumberUtils.isParsable(height)) {
height += HtmlConstants.PX;
CSSLength cssLength = CSSLength.of(height);
declaredHeight = context.computeLengthInEMU(cssLength, heightInEMU, Integer.MAX_VALUE);
}
}
String maxHeight = context.getPropertyValue(HtmlConstants.CSS_MAX_HEIGHT);
if (maxHeight.length() > 0) {
CSSLength cssLength = CSSLength.of(maxHeight);
if (cssLength.isValid()) {
maxHeightInEMU = context.computeLengthInEMU(cssLength, heightInEMU, Integer.MAX_VALUE);
}
}
if (declaredWidth == widthInEMU ^ declaredHeight == heightInEMU) {
if (declaredWidth == widthInEMU) {
declaredWidth = (int) (declaredHeight * naturalAspect);
} else {
declaredHeight = (int) (declaredWidth / naturalAspect);
}
}
// 计算尺寸
int calculatedWidth, calculatedHeight;
if (declaredWidth < maxWidthInEMU && declaredHeight <= maxHeightInEMU) {
calculatedWidth = declaredWidth;
calculatedHeight = declaredHeight;
} else if (declaredWidth > maxWidthInEMU && declaredHeight <= maxHeightInEMU) {
calculatedWidth = maxWidthInEMU;
calculatedHeight = (int) (maxWidthInEMU / naturalAspect);
} else if (declaredHeight > maxHeightInEMU && declaredWidth <= maxWidthInEMU) {
calculatedHeight = maxHeightInEMU;
calculatedWidth = (int) (maxHeightInEMU * naturalAspect);
} else {
float widthRatio = 1f * maxWidthInEMU / declaredWidth;
float heightRatio = 1f * maxHeightInEMU / declaredHeight;
float scale = Math.min(widthRatio, heightRatio);
calculatedWidth = (int) (declaredWidth * scale);
calculatedHeight = (int) (declaredHeight * scale);
}
context.renderPicture(inputStream, type, HtmlConstants.TAG_IMG,
calculatedWidth, calculatedHeight, svgData);
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/ItalicRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSStyleUtils;
import org.jsoup.nodes.Element;
/**
* 斜体标签渲染器
*
* @author Draco
* @since 2021-02-23
*/
public class ItalicRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_I, HtmlConstants.TAG_EM};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
context.pushInlineStyle(CSSStyleUtils.parse(HtmlConstants.DEFINED_ITALIC), element.isBlock());
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.popInlineStyle();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/LaTeXRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.apache.commons.lang3.StringUtils;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.latex.LaTeXUtils;
import org.jsoup.nodes.Element;
import uk.ac.ed.ph.snuggletex.SnuggleSession;
/**
* latex标签渲染器(自定义)
*
* @author Draco
* @since 2023-07-17
*/
public class LaTeXRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_LATEX};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
String latex = element.wholeText();
if (StringUtils.isBlank(latex)) {
return false;
}
SnuggleSession session = LaTeXUtils.createSession();
LaTeXUtils.parse(session, latex);
LaTeXUtils.renderTo(context.getClosestParagraph(), null, session, context.getMathRenderConfig());
return false;
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/ListItemRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
/**
* 列表项渲染器
*
* @author Draco
* @since 2021-02-19
*/
public class ListItemRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_LI};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
XWPFParagraph paragraph = context.getClosestParagraph();
context.markDedupe(paragraph);
context.getNumberingContext().add(paragraph);
return true;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.unmarkDedupe();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
return true;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/ListRenderer.java
================================================
/*
* Copyright 2016 - 2021 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import org.apache.commons.lang3.StringUtils;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.ddr.poi.html.util.CSSLength;
import org.ddr.poi.html.util.ListStyle;
import org.ddr.poi.html.util.ListStyleType;
import org.ddr.poi.html.util.RenderUtils;
import org.jsoup.nodes.Element;
/**
* 列表渲染器
*
* @author Draco
* @since 2021-02-18
*/
public class ListRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_UL, HtmlConstants.TAG_OL};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
String listStylePosition = context.currentElementStyle().getPropertyValue(HtmlConstants.CSS_LIST_STYLE_POSITION);
boolean hanging = !HtmlConstants.INSIDE.equals(listStylePosition);
CSSLength marginLeft = CSSLength.of(context.currentElementStyle().getMarginLeft().toLowerCase());
int left = marginLeft.isValid() && !marginLeft.isPercent()
? RenderUtils.emuToTwips(context.lengthToEMU(marginLeft)) : 0;
CSSLength marginRight = CSSLength.of(context.currentElementStyle().getMarginRight().toLowerCase());
int right = marginRight.isValid() && !marginRight.isPercent()
? RenderUtils.emuToTwips(context.lengthToEMU(marginRight)) : 0;
ListStyle listStyle = new ListStyle(determineNumberFormat(context, element), hanging, left, right);
context.getNumberingContext().startLevel(listStyle);
return true;
}
private ListStyleType determineNumberFormat(HtmlRenderContext context, Element element) {
String listStyleType = context.currentElementStyle()
.getPropertyValue(HtmlConstants.CSS_LIST_STYLE_TYPE).toLowerCase();
ListStyleType format;
switch (element.tag().normalName()) {
case HtmlConstants.TAG_OL:
if (StringUtils.isNotBlank(listStyleType)) {
format = ListStyleType.Ordered.of(listStyleType);
} else {
// 支持ol的type属性
String type = element.attr(HtmlConstants.ATTR_TYPE);
format = ListStyleType.Ordered.of(type);
}
break;
case HtmlConstants.TAG_UL:
format = ListStyleType.Unordered.of(listStyleType);
break;
default:
format = ListStyleType.Unordered.NONE;
}
return format;
}
/**
* 元素渲染结束需要执行的逻辑
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.getNumberingContext().endLevel();
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
public boolean renderAsBlock() {
// 列表标签本身不需要作为块状元素渲染,因为每一个列表项都是一个块状元素
return false;
}
}
================================================
FILE: src/main/java/org/ddr/poi/html/tag/MarkRenderer.java
================================================
/*
* Copyright 2016 - 2022 Draco, https://github.com/draco1023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ddr.poi.html.tag;
import com.steadystate.css.dom.CSSStyleDeclarationImpl;
import org.apache.commons.lang3.StringUtils;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlConstants;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
/**
* 标记标签渲染器
*
* @author Draco
* @since 2022-06-11
*/
public class MarkRenderer implements ElementRenderer {
private static final String[] TAGS = {HtmlConstants.TAG_MARK};
/**
* 开始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否继续渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
CSSStyleDeclarationImpl cssStyleDeclaration = context.currentElementStyle();
if (StringUtils.isEmpty(cssStyleDeclaration.getBackgroundColor())) {
cssStyleDeclaration.setBackgroundColor("yellow");
}
return true;
}
@Override
public String[] supportedTags() {
return TAGS;
}
@Override
gitextract_tlgs8gmd/
├── .gitattributes
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── bug_report.md
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── gradle/
│ └── wrapper/
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ ├── apache/
│ │ │ └── poi/
│ │ │ └── xwpf/
│ │ │ └── usermodel/
│ │ │ ├── SVGPictureData.java
│ │ │ └── SVGRelation.java
│ │ ├── ddr/
│ │ │ ├── image/
│ │ │ │ ├── ImageInfo.java
│ │ │ │ ├── ImageInputStreamWrapper.java
│ │ │ │ ├── ImageType.java
│ │ │ │ ├── MetadataReader.java
│ │ │ │ ├── MetadataReaders.java
│ │ │ │ ├── avif/
│ │ │ │ │ ├── AvifImageReader.java
│ │ │ │ │ ├── AvifImageReaderSpi.java
│ │ │ │ │ ├── AvifMetadataReader.java
│ │ │ │ │ └── AvifProviderInfo.java
│ │ │ │ ├── bmp/
│ │ │ │ │ └── BmpMetadataReader.java
│ │ │ │ ├── eps/
│ │ │ │ │ └── EpsMetadataReader.java
│ │ │ │ ├── gif/
│ │ │ │ │ └── GifMetadataReader.java
│ │ │ │ ├── heif/
│ │ │ │ │ ├── HeifImageReader.java
│ │ │ │ │ ├── HeifImageReaderSpi.java
│ │ │ │ │ ├── HeifMetadataReader.java
│ │ │ │ │ └── HeifProviderInfo.java
│ │ │ │ ├── jpeg/
│ │ │ │ │ └── JpegMetadataReader.java
│ │ │ │ ├── png/
│ │ │ │ │ └── PngMetadataReader.java
│ │ │ │ ├── tiff/
│ │ │ │ │ └── TiffMetadataReader.java
│ │ │ │ └── webp/
│ │ │ │ └── WebpMetadataReader.java
│ │ │ └── poi/
│ │ │ ├── html/
│ │ │ │ ├── ElementRenderer.java
│ │ │ │ ├── ElementRendererProvider.java
│ │ │ │ ├── HtmlConstants.java
│ │ │ │ ├── HtmlRenderConfig.java
│ │ │ │ ├── HtmlRenderContext.java
│ │ │ │ ├── HtmlRenderPolicy.java
│ │ │ │ ├── tag/
│ │ │ │ │ ├── ARenderer.java
│ │ │ │ │ ├── BigRenderer.java
│ │ │ │ │ ├── BoldRenderer.java
│ │ │ │ │ ├── BreakRenderer.java
│ │ │ │ │ ├── DeleteRenderer.java
│ │ │ │ │ ├── FigureCaptionRenderer.java
│ │ │ │ │ ├── FigureRenderer.java
│ │ │ │ │ ├── HeaderBreakRenderer.java
│ │ │ │ │ ├── HeaderRenderer.java
│ │ │ │ │ ├── ImageRenderer.java
│ │ │ │ │ ├── ItalicRenderer.java
│ │ │ │ │ ├── LaTeXRenderer.java
│ │ │ │ │ ├── ListItemRenderer.java
│ │ │ │ │ ├── ListRenderer.java
│ │ │ │ │ ├── MarkRenderer.java
│ │ │ │ │ ├── MathRenderer.java
│ │ │ │ │ ├── OmittedRenderer.java
│ │ │ │ │ ├── PreRenderer.java
│ │ │ │ │ ├── RubyRenderer.java
│ │ │ │ │ ├── SmallRenderer.java
│ │ │ │ │ ├── SubscriptRenderer.java
│ │ │ │ │ ├── SuperscriptRenderer.java
│ │ │ │ │ ├── SvgRenderer.java
│ │ │ │ │ ├── TableCellRenderer.java
│ │ │ │ │ ├── TableRenderer.java
│ │ │ │ │ ├── UnderlineRenderer.java
│ │ │ │ │ └── WalkThroughRenderer.java
│ │ │ │ └── util/
│ │ │ │ ├── BoxProperty.java
│ │ │ │ ├── CSSLength.java
│ │ │ │ ├── CSSLengthUnit.java
│ │ │ │ ├── CSSStyleUtils.java
│ │ │ │ ├── Colors.java
│ │ │ │ ├── ColumnStyle.java
│ │ │ │ ├── EmptyCSSStyle.java
│ │ │ │ ├── InlineStyle.java
│ │ │ │ ├── JsoupUtils.java
│ │ │ │ ├── ListStyle.java
│ │ │ │ ├── ListStyleType.java
│ │ │ │ ├── NamedBorderWidth.java
│ │ │ │ ├── NamedFontSize.java
│ │ │ │ ├── NumberingContext.java
│ │ │ │ ├── RenderUtils.java
│ │ │ │ ├── Span.java
│ │ │ │ ├── SpanWidth.java
│ │ │ │ ├── WhiteSpaceRule.java
│ │ │ │ └── XWPFParagraphRuns.java
│ │ │ ├── latex/
│ │ │ │ ├── LaTeXRenderPolicy.java
│ │ │ │ ├── LaTeXUtils.java
│ │ │ │ ├── TagHandler.java
│ │ │ │ └── TextCircledHandler.java
│ │ │ ├── math/
│ │ │ │ ├── EmptyEOfNaryDisplayMode.java
│ │ │ │ ├── MathMLRenderPolicy.java
│ │ │ │ ├── MathMLUtils.java
│ │ │ │ └── MathRenderConfig.java
│ │ │ └── util/
│ │ │ ├── ByteArrayCopyStream.java
│ │ │ ├── HttpURLConnectionUtils.java
│ │ │ └── XmlUtils.java
│ │ └── jsoup/
│ │ └── parser/
│ │ └── CustomHtmlTreeBuilder.java
│ └── resources/
│ ├── META-INF/
│ │ └── services/
│ │ └── javax.imageio.spi.ImageReaderSpi
│ ├── MML2OMML.XSL
│ ├── math-character-aliases.txt
│ └── math-character-circled.txt
└── test/
├── java/
│ └── org/
│ └── ddr/
│ └── poi/
│ ├── FileReader.java
│ ├── html/
│ │ └── HtmlRenderPolicyTest.java
│ ├── latex/
│ │ └── LaTeXRenderPolicyTest.java
│ └── math/
│ └── MathMLRenderPolicyTest.java
└── resources/
├── 0.xml
├── 1.html
├── 1.xml
├── 2.html
├── 2.xml
├── 3.xml
├── math.docx
├── notes.docx
├── poi.docx
└── simplelogger.properties
SYMBOL INDEX (681 symbols across 90 files)
FILE: src/main/java/org/apache/poi/xwpf/usermodel/SVGPictureData.java
class SVGPictureData (line 11) | public class SVGPictureData extends XWPFPictureData {
method SVGPictureData (line 15) | public SVGPictureData() {
method SVGPictureData (line 18) | public SVGPictureData(PackagePart part) {
method initRelation (line 22) | public static void initRelation() {
FILE: src/main/java/org/apache/poi/xwpf/usermodel/SVGRelation.java
class SVGRelation (line 15) | public class SVGRelation extends POIXMLRelation {
method SVGRelation (line 40) | private SVGRelation() {
FILE: src/main/java/org/ddr/image/ImageInfo.java
class ImageInfo (line 6) | public class ImageInfo {
method ImageInfo (line 11) | public ImageInfo(ByteArrayInputStream stream) {
method ImageInfo (line 15) | public ImageInfo(ByteArrayInputStream stream, ImageType type, Dimensio...
method getStream (line 21) | public ByteArrayInputStream getStream() {
method setStream (line 25) | public void setStream(ByteArrayInputStream stream) {
method getType (line 29) | public ImageType getType() {
method setType (line 33) | public void setType(ImageType type) {
method getDimension (line 37) | public Dimension getDimension() {
method setDimension (line 41) | public void setDimension(Dimension dimension) {
method getWidth (line 45) | public int getWidth() {
method getHeight (line 49) | public int getHeight() {
method getRawType (line 53) | public int getRawType() {
FILE: src/main/java/org/ddr/image/ImageInputStreamWrapper.java
class ImageInputStreamWrapper (line 9) | public class ImageInputStreamWrapper extends InputStream implements Imag...
method ImageInputStreamWrapper (line 13) | public ImageInputStreamWrapper(ImageInputStream input) {
method setByteOrder (line 17) | @Override
method getByteOrder (line 22) | @Override
method read (line 27) | @Override
method readBytes (line 32) | @Override
method readBoolean (line 37) | @Override
method readByte (line 42) | @Override
method readUnsignedByte (line 47) | @Override
method readShort (line 52) | @Override
method readUnsignedShort (line 57) | @Override
method readChar (line 62) | @Override
method readInt (line 67) | @Override
method readUnsignedInt (line 72) | @Override
method readLong (line 77) | @Override
method readFloat (line 82) | @Override
method readDouble (line 87) | @Override
method readLine (line 92) | @Override
method readUTF (line 97) | @Override
method readFully (line 102) | @Override
method readFully (line 107) | @Override
method readFully (line 112) | @Override
method readFully (line 117) | @Override
method readFully (line 122) | @Override
method readFully (line 127) | @Override
method readFully (line 132) | @Override
method readFully (line 137) | @Override
method getStreamPosition (line 142) | @Override
method getBitOffset (line 147) | @Override
method setBitOffset (line 152) | @Override
method readBit (line 157) | @Override
method readBits (line 162) | @Override
method length (line 167) | @Override
method skipBytes (line 172) | @Override
method skipBytes (line 177) | @Override
method seek (line 182) | @Override
method mark (line 187) | @Override
method flushBefore (line 192) | @Override
method flush (line 197) | @Override
method getFlushedPosition (line 202) | @Override
method isCached (line 207) | @Override
method isCachedMemory (line 212) | @Override
method isCachedFile (line 217) | @Override
FILE: src/main/java/org/ddr/image/ImageType.java
type ImageType (line 5) | public enum ImageType {
method ImageType (line 22) | ImageType(int type) {
method getExtension (line 26) | public String getExtension() {
method getType (line 30) | public int getType() {
FILE: src/main/java/org/ddr/image/MetadataReader.java
type MetadataReader (line 8) | public interface MetadataReader {
method canRead (line 9) | boolean canRead(FileType type);
method getType (line 11) | ImageType getType(Metadata metadata);
method getDimension (line 13) | Dimension getDimension(Metadata metadata);
FILE: src/main/java/org/ddr/image/MetadataReaders.java
class MetadataReaders (line 13) | public class MetadataReaders {
FILE: src/main/java/org/ddr/image/avif/AvifImageReader.java
class AvifImageReader (line 7) | public class AvifImageReader extends HeifImageReader {
method AvifImageReader (line 9) | public AvifImageReader(ImageReaderSpi provider) {
FILE: src/main/java/org/ddr/image/avif/AvifImageReaderSpi.java
class AvifImageReaderSpi (line 13) | public final class AvifImageReaderSpi extends ImageReaderSpiBase {
method AvifImageReaderSpi (line 25) | public AvifImageReaderSpi() {
method canDecodeInput (line 29) | @Override
method canDecode (line 34) | private boolean canDecode(ImageInputStream input) throws IOException {
method createReaderInstance (line 55) | @Override
method getDescription (line 60) | @Override
FILE: src/main/java/org/ddr/image/avif/AvifMetadataReader.java
class AvifMetadataReader (line 6) | public class AvifMetadataReader extends HeifMetadataReader {
method canRead (line 7) | @Override
FILE: src/main/java/org/ddr/image/avif/AvifProviderInfo.java
class AvifProviderInfo (line 5) | final class AvifProviderInfo extends ReaderWriterProviderInfo {
method AvifProviderInfo (line 6) | AvifProviderInfo() {
FILE: src/main/java/org/ddr/image/bmp/BmpMetadataReader.java
class BmpMetadataReader (line 12) | public class BmpMetadataReader implements MetadataReader {
method canRead (line 13) | @Override
method getType (line 18) | @Override
method getDimension (line 23) | @Override
FILE: src/main/java/org/ddr/image/eps/EpsMetadataReader.java
class EpsMetadataReader (line 12) | public class EpsMetadataReader implements MetadataReader{
method canRead (line 13) | @Override
method getType (line 18) | @Override
method getDimension (line 23) | @Override
FILE: src/main/java/org/ddr/image/gif/GifMetadataReader.java
class GifMetadataReader (line 12) | public class GifMetadataReader implements MetadataReader{
method canRead (line 13) | @Override
method getType (line 18) | @Override
method getDimension (line 23) | @Override
FILE: src/main/java/org/ddr/image/heif/HeifImageReader.java
class HeifImageReader (line 31) | public class HeifImageReader extends ImageReaderBase {
method HeifImageReader (line 40) | public HeifImageReader(ImageReaderSpi provider) {
method HeifImageReader (line 44) | protected HeifImageReader(ImageReaderSpi provider, MetadataReader meta...
method resetMembers (line 50) | @Override
method setInput (line 56) | @Override
method getWidth (line 71) | @Override
method getHeight (line 76) | @Override
method getImageTypes (line 81) | @Override
method read (line 86) | @Override
method convert (line 91) | BufferedImage convert(ImageReadParam param) {
FILE: src/main/java/org/ddr/image/heif/HeifImageReaderSpi.java
class HeifImageReaderSpi (line 13) | public final class HeifImageReaderSpi extends ImageReaderSpiBase {
method HeifImageReaderSpi (line 25) | public HeifImageReaderSpi() {
method canDecodeInput (line 29) | @Override
method canDecode (line 34) | private boolean canDecode(ImageInputStream input) throws IOException {
method createReaderInstance (line 58) | @Override
method getDescription (line 63) | @Override
FILE: src/main/java/org/ddr/image/heif/HeifMetadataReader.java
class HeifMetadataReader (line 16) | public class HeifMetadataReader implements MetadataReader {
method canRead (line 17) | @Override
method getType (line 22) | @Override
method getDimension (line 32) | @Override
FILE: src/main/java/org/ddr/image/heif/HeifProviderInfo.java
class HeifProviderInfo (line 5) | final class HeifProviderInfo extends ReaderWriterProviderInfo {
method HeifProviderInfo (line 6) | HeifProviderInfo() {
FILE: src/main/java/org/ddr/image/jpeg/JpegMetadataReader.java
class JpegMetadataReader (line 12) | public class JpegMetadataReader implements MetadataReader {
method canRead (line 13) | @Override
method getType (line 18) | @Override
method getDimension (line 23) | @Override
FILE: src/main/java/org/ddr/image/png/PngMetadataReader.java
class PngMetadataReader (line 13) | public class PngMetadataReader implements MetadataReader {
method canRead (line 14) | @Override
method getType (line 19) | @Override
method getDimension (line 24) | @Override
FILE: src/main/java/org/ddr/image/tiff/TiffMetadataReader.java
class TiffMetadataReader (line 13) | public class TiffMetadataReader implements MetadataReader {
method canRead (line 16) | @Override
method getType (line 21) | @Override
method getDimension (line 26) | @Override
FILE: src/main/java/org/ddr/image/webp/WebpMetadataReader.java
class WebpMetadataReader (line 12) | public class WebpMetadataReader implements MetadataReader{
method canRead (line 13) | @Override
method getType (line 18) | @Override
method getDimension (line 31) | @Override
FILE: src/main/java/org/ddr/poi/html/ElementRenderer.java
type ElementRenderer (line 28) | public interface ElementRenderer {
method renderStart (line 36) | boolean renderStart(Element element, HtmlRenderContext context);
method renderEnd (line 44) | default void renderEnd(Element element, HtmlRenderContext context) {
method supportedTags (line 50) | String[] supportedTags();
method renderAsBlock (line 55) | boolean renderAsBlock();
FILE: src/main/java/org/ddr/poi/html/ElementRendererProvider.java
type ElementRendererProvider (line 25) | @FunctionalInterface
method get (line 33) | ElementRenderer get(String tagNormalName);
FILE: src/main/java/org/ddr/poi/html/HtmlConstants.java
type HtmlConstants (line 29) | public interface HtmlConstants {
method inlineStyle (line 372) | static String inlineStyle(String key, String value) {
method isMajorFont (line 380) | static boolean isMajorFont(String fontName) {
FILE: src/main/java/org/ddr/poi/html/HtmlRenderConfig.java
class HtmlRenderConfig (line 29) | public class HtmlRenderConfig {
method getGlobalFont (line 47) | public String getGlobalFont() {
method setGlobalFont (line 51) | public void setGlobalFont(String globalFont) {
method getGlobalFontSize (line 58) | public CSSLength getGlobalFontSize() {
method setGlobalFontSize (line 62) | public void setGlobalFontSize(CSSLength globalFontSize) {
method getGlobalFontSizeInHalfPoints (line 67) | public int getGlobalFontSizeInHalfPoints() {
method isShowDefaultTableBorderInTableCell (line 74) | public boolean isShowDefaultTableBorderInTableCell() {
method setShowDefaultTableBorderInTableCell (line 78) | public void setShowDefaultTableBorderInTableCell(boolean showDefaultTa...
method getCustomRenderers (line 85) | public List<ElementRenderer> getCustomRenderers() {
method setCustomRenderers (line 89) | public void setCustomRenderers(List<ElementRenderer> customRenderers) {
method getNumberingIndent (line 96) | public int getNumberingIndent() {
method setNumberingIndent (line 100) | public void setNumberingIndent(int numberingIndent) {
method getNumberingHanging (line 107) | public int getNumberingHanging() {
method setNumberingHanging (line 111) | public void setNumberingHanging(int numberingHanging) {
method getNumberingSpacing (line 118) | public STLevelSuffix.Enum getNumberingSpacing() {
method setNumberingSpacing (line 122) | public void setNumberingSpacing(STLevelSuffix.Enum numberingSpacing) {
method getMathRenderConfig (line 126) | public MathRenderConfig getMathRenderConfig() {
FILE: src/main/java/org/ddr/poi/html/HtmlRenderContext.java
class HtmlRenderContext (line 123) | public class HtmlRenderContext extends RenderContext<String> {
method HtmlRenderContext (line 253) | public HtmlRenderContext(RenderContext<String> context, ElementRendere...
method extractPlaceholderStyle (line 302) | private void extractPlaceholderStyle() {
method getContainer (line 341) | @Override
method pushContainer (line 352) | public void pushContainer(IBody body) {
method popContainer (line 359) | public void popContainer() {
method getClosestParagraph (line 368) | public XWPFParagraph getClosestParagraph() {
method startHyperlink (line 401) | public void startHyperlink(String uri) {
method endHyperlink (line 432) | public void endHyperlink() {
method newParagraph (line 443) | public XWPFParagraph newParagraph(IBody container, XmlCursor cursor) {
method adjustPicture (line 457) | private void adjustPicture() {
method newRun (line 503) | public CTR newRun() {
method initHyperlinkStyle (line 544) | private void initHyperlinkStyle(CTR ctr) {
method getClosestTable (line 558) | public XWPFTable getClosestTable() {
method pushInlineStyle (line 582) | public void pushInlineStyle(CSSStyleDeclarationImpl inlineStyle, boole...
method popInlineStyle (line 649) | public void popInlineStyle() {
method currentElementStyle (line 659) | public CSSStyleDeclarationImpl currentElementStyle() {
method getPropertyValue (line 670) | public String getPropertyValue(String property) {
method getPropertyValue (line 681) | public String getPropertyValue(String property, boolean inlineOnly) {
method getPropertyValue (line 693) | public String getPropertyValue(String property, boolean caseSensitive,...
method getDefaultFontSize (line 709) | public CSSLength getDefaultFontSize() {
method getInheritedFontSizeInHalfPoints (line 716) | public int getInheritedFontSizeInHalfPoints() {
method getAvailableWidthInEMU (line 728) | public int getAvailableWidthInEMU() {
method computeLengthInEMU (line 746) | public int computeLengthInEMU(String length, String maxLength, int nat...
method computeLengthInEMU (line 775) | public int computeLengthInEMU(CSSLength cssLength, int naturalEMU, int...
method renderText (line 794) | public void renderText(String text) {
method addText (line 947) | private void addText(CTR ctr, StringBuilder sb, boolean endTrimmed) {
method applyTextStyle (line 965) | private void applyTextStyle(CTR ctr) {
method renderPicture (line 1078) | public void renderPicture(InputStream pictureData, int pictureType, St...
method attachSvgData (line 1155) | private void attachSvgData(XWPFPicture xwpfPicture, byte[] svgData) th...
method lengthToEMU (line 1178) | public int lengthToEMU(CSSLength length) {
method getNumberingContext (line 1212) | public NumberingContext getNumberingContext() {
method getPageWidth (line 1216) | public CSSLength getPageWidth() {
method getPageHeight (line 1220) | public CSSLength getPageHeight() {
method getMarginTop (line 1224) | public CSSLength getMarginTop() {
method getMarginRight (line 1228) | public CSSLength getMarginRight() {
method getMarginBottom (line 1232) | public CSSLength getMarginBottom() {
method getMarginLeft (line 1236) | public CSSLength getMarginLeft() {
method getAvailablePageWidth (line 1240) | public int getAvailablePageWidth() {
method getAvailablePageHeight (line 1244) | public int getAvailablePageHeight() {
method getMathRenderConfig (line 1248) | public MathRenderConfig getMathRenderConfig() {
method setMathRenderConfig (line 1252) | public void setMathRenderConfig(MathRenderConfig mathRenderConfig) {
method getCurrentRun (line 1256) | public XWPFRun getCurrentRun() {
method getGlobalFont (line 1260) | public String getGlobalFont() {
method getGlobalFontSize (line 1264) | public BigInteger getGlobalFontSize() {
method isShowDefaultTableBorderInTableCell (line 1268) | public boolean isShowDefaultTableBorderInTableCell() {
method setShowDefaultTableBorderInTableCell (line 1272) | public void setShowDefaultTableBorderInTableCell(boolean showDefaultTa...
method setGlobalFont (line 1276) | public void setGlobalFont(String globalFont) {
method setGlobalFontSize (line 1280) | public void setGlobalFontSize(BigInteger globalFontSize) {
method isBlocked (line 1284) | public boolean isBlocked() {
method incrementBlockLevel (line 1288) | public void incrementBlockLevel() {
method decrementBlockLevel (line 1292) | public void decrementBlockLevel() {
method renderDocument (line 1296) | public void renderDocument(Document document) {
method renderNode (line 1311) | public void renderNode(Node node) {
method renderElement (line 1322) | public void renderElement(Element element) {
method isEmptyParagraph (line 1410) | private boolean isEmptyParagraph(XWPFParagraph paragraph) {
method removeParagraph (line 1423) | private void removeParagraph(IBody container, XWPFParagraph paragraph) {
method renderAsBlock (line 1457) | public boolean renderAsBlock(Element element) {
method renderAsBlock (line 1468) | private boolean renderAsBlock(Element element, ElementRenderer element...
method shouldNewParagraph (line 1472) | private boolean shouldNewParagraph(Element element) {
method removeLastBrRun (line 1495) | private boolean removeLastBrRun(XmlCursor xmlCursor) {
method adjustCursor (line 1513) | private void adjustCursor(IBody container, boolean isTableTag) {
method renderElementEnd (line 1533) | private void renderElementEnd(Element element, HtmlRenderContext conte...
method moveContentToNewPrevParagraph (line 1543) | private void moveContentToNewPrevParagraph(CTR ctr) {
method getCssStyleDeclaration (line 1608) | public CSSStyleDeclarationImpl getCssStyleDeclaration(Element element) {
method pushCursor (line 1620) | public void pushCursor(XmlCursor targetCursor) {
method popCursor (line 1630) | public boolean popCursor() {
method currentCursorObject (line 1637) | public XmlObject currentCursorObject() {
method markDedupe (line 1646) | public void markDedupe(XWPFParagraph paragraph) {
method unmarkDedupe (line 1653) | public void unmarkDedupe() {
class TextWrapper (line 1660) | private static class TextWrapper {
method TextWrapper (line 1664) | public TextWrapper(CTText text, boolean endTrimmed) {
method getText (line 1669) | public CTText getText() {
method isEndTrimmed (line 1673) | public boolean isEndTrimmed() {
FILE: src/main/java/org/ddr/poi/html/HtmlRenderPolicy.java
class HtmlRenderPolicy (line 73) | public class HtmlRenderPolicy extends AbstractRenderPolicy<String> {
method HtmlRenderPolicy (line 77) | public HtmlRenderPolicy() {
method HtmlRenderPolicy (line 81) | @Deprecated
method HtmlRenderPolicy (line 88) | public HtmlRenderPolicy(HtmlRenderConfig config) {
method getConfig (line 135) | public HtmlRenderConfig getConfig() {
method validate (line 139) | @Override
method doRender (line 144) | @Override
method afterRender (line 163) | @Override
method hasSibling (line 178) | private boolean hasSibling(XWPFRun run) {
method isValidSibling (line 202) | private boolean isValidSibling(XmlCursor cursor) {
FILE: src/main/java/org/ddr/poi/html/tag/ARenderer.java
class ARenderer (line 30) | public class ARenderer implements ElementRenderer {
method renderStart (line 40) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/BigRenderer.java
class BigRenderer (line 31) | @Deprecated
method renderStart (line 42) | @Override
method renderEnd (line 54) | @Override
method supportedTags (line 59) | @Override
method renderAsBlock (line 64) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/BoldRenderer.java
class BoldRenderer (line 31) | public class BoldRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/BreakRenderer.java
class BreakRenderer (line 33) | public class BreakRenderer implements ElementRenderer {
method renderStart (line 43) | @Override
method supportedTags (line 54) | @Override
method renderAsBlock (line 59) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/DeleteRenderer.java
class DeleteRenderer (line 31) | public class DeleteRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/FigureCaptionRenderer.java
class FigureCaptionRenderer (line 14) | public class FigureCaptionRenderer implements ElementRenderer {
method renderStart (line 24) | @Override
method renderEnd (line 36) | @Override
method supportedTags (line 44) | @Override
method renderAsBlock (line 52) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/FigureRenderer.java
class FigureRenderer (line 19) | public class FigureRenderer implements ElementRenderer {
method renderStart (line 29) | @Override
method renderEnd (line 63) | @Override
method supportedTags (line 71) | @Override
method renderAsBlock (line 79) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/HeaderBreakRenderer.java
class HeaderBreakRenderer (line 37) | public class HeaderBreakRenderer implements ElementRenderer {
method renderStart (line 55) | @Override
method renderEnd (line 71) | @Override
method supportedTags (line 76) | @Override
method renderAsBlock (line 81) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/HeaderRenderer.java
class HeaderRenderer (line 36) | public class HeaderRenderer implements ElementRenderer {
method renderStart (line 54) | @Override
method renderEnd (line 73) | @Override
method supportedTags (line 78) | @Override
method renderAsBlock (line 83) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/ImageRenderer.java
class ImageRenderer (line 65) | public class ImageRenderer implements ElementRenderer {
method renderStart (line 85) | @Override
method handleData (line 106) | private void handleData(Element element, HtmlRenderContext context, St...
method typeOf (line 170) | protected ImageType typeOf(BufferedImage image) {
method handleRemoteImage (line 181) | private void handleRemoteImage(Element element, HtmlRenderContext cont...
method analyzeImage (line 210) | private ImageInfo analyzeImage(ByteArrayInputStream inputStream, boole...
method supportedTags (line 273) | @Override
method renderAsBlock (line 278) | @Override
method addPicture (line 294) | protected void addPicture(Element element, HtmlRenderContext context, ...
FILE: src/main/java/org/ddr/poi/html/tag/ItalicRenderer.java
class ItalicRenderer (line 31) | public class ItalicRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/LaTeXRenderer.java
class LaTeXRenderer (line 33) | public class LaTeXRenderer implements ElementRenderer {
method renderStart (line 43) | @Override
method supportedTags (line 57) | @Override
method renderAsBlock (line 62) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/ListItemRenderer.java
class ListItemRenderer (line 31) | public class ListItemRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 55) | @Override
method supportedTags (line 60) | @Override
method renderAsBlock (line 65) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/ListRenderer.java
class ListRenderer (line 35) | public class ListRenderer implements ElementRenderer {
method renderStart (line 45) | @Override
method determineNumberFormat (line 60) | private ListStyleType determineNumberFormat(HtmlRenderContext context,...
method renderEnd (line 89) | @Override
method supportedTags (line 94) | @Override
method renderAsBlock (line 99) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/MarkRenderer.java
class MarkRenderer (line 32) | public class MarkRenderer implements ElementRenderer {
method renderStart (line 42) | @Override
method supportedTags (line 52) | @Override
method renderAsBlock (line 57) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/MathRenderer.java
class MathRenderer (line 33) | public class MathRenderer implements ElementRenderer {
method renderStart (line 43) | @Override
method supportedTags (line 55) | @Override
method renderAsBlock (line 60) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/OmittedRenderer.java
class OmittedRenderer (line 30) | public class OmittedRenderer implements ElementRenderer {
method renderStart (line 52) | @Override
method supportedTags (line 57) | @Override
method renderAsBlock (line 62) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/PreRenderer.java
class PreRenderer (line 15) | public class PreRenderer implements ElementRenderer {
method renderStart (line 18) | @Override
method renderEnd (line 24) | @Override
method supportedTags (line 29) | @Override
method renderAsBlock (line 34) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/RubyRenderer.java
class RubyRenderer (line 40) | public class RubyRenderer implements ElementRenderer {
method renderStart (line 45) | @Override
method supportedTags (line 92) | @Override
method renderAsBlock (line 97) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/SmallRenderer.java
class SmallRenderer (line 31) | public class SmallRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/SubscriptRenderer.java
class SubscriptRenderer (line 31) | public class SubscriptRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/SuperscriptRenderer.java
class SuperscriptRenderer (line 31) | public class SuperscriptRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/SvgRenderer.java
class SvgRenderer (line 25) | public class SvgRenderer extends ImageRenderer {
method renderStart (line 41) | @Override
method supportedTags (line 72) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/TableCellRenderer.java
class TableCellRenderer (line 40) | public class TableCellRenderer implements ElementRenderer {
method renderStart (line 50) | @Override
method renderEnd (line 75) | @Override
method supportedTags (line 91) | @Override
method renderAsBlock (line 96) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/TableRenderer.java
class TableRenderer (line 72) | public class TableRenderer implements ElementRenderer {
method TableRenderer (line 77) | public TableRenderer() {
method renderStart (line 94) | @Override
method reorderTableChildren (line 382) | private void reorderTableChildren(Element element) {
method handleTableProperties (line 399) | private void handleTableProperties(Element element, CTTbl ctTbl) {
method extractColumnStyles (line 520) | private List<ColumnStyle> extractColumnStyles(Element colgroup) {
method sumColumnWidths (line 552) | private CSSLength sumColumnWidths(List<ColumnStyle> columnWidths, int ...
method renderCaption (line 583) | private void renderCaption(HtmlRenderContext context, XWPFTable table,...
method renderEnd (line 627) | @Override
method addVMergeCell (line 641) | private void addVMergeCell(HtmlRenderContext context, XWPFTableRow row...
method createCell (line 658) | private XWPFTableCell createCell(XWPFTableRow row, int c) {
method createRow (line 669) | private XWPFTableRow createRow(XWPFTable table, int r) {
method supportedTags (line 674) | @Override
method renderAsBlock (line 679) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/UnderlineRenderer.java
class UnderlineRenderer (line 31) | public class UnderlineRenderer implements ElementRenderer {
method renderStart (line 41) | @Override
method renderEnd (line 53) | @Override
method supportedTags (line 58) | @Override
method renderAsBlock (line 63) | @Override
FILE: src/main/java/org/ddr/poi/html/tag/WalkThroughRenderer.java
class WalkThroughRenderer (line 30) | public class WalkThroughRenderer implements ElementRenderer {
method renderStart (line 47) | @Override
method supportedTags (line 52) | @Override
method renderAsBlock (line 57) | @Override
FILE: src/main/java/org/ddr/poi/html/util/BoxProperty.java
type BoxProperty (line 30) | public enum BoxProperty {
method BoxProperty (line 47) | BoxProperty(String top, String right, String bottom, String left) {
method setValues (line 54) | public void setValues(CSSStyleDeclarationImpl cssStyleDeclaration, int i,
method setValues (line 62) | public void setValues(CSSStyleDeclarationImpl cssStyleDeclaration, int...
method setValues (line 66) | public void setValues(CSSStyleDeclarationImpl cssStyleDeclaration, int...
method setValues (line 70) | public void setValues(CSSStyleDeclarationImpl cssStyleDeclaration, int...
FILE: src/main/java/org/ddr/poi/html/util/CSSLength.java
class CSSLength (line 31) | public class CSSLength {
method CSSLength (line 44) | public CSSLength(double value, CSSLengthUnit unit) {
method getValue (line 49) | public double getValue() {
method getUnit (line 53) | public CSSLengthUnit getUnit() {
method toString (line 57) | @Override
method toEMU (line 65) | public int toEMU() {
method unitValue (line 71) | public double unitValue() {
method requireAbsoluteUnit (line 75) | private void requireAbsoluteUnit() {
method validate (line 81) | private void validate() {
method to (line 87) | public CSSLength to(CSSLengthUnit other) {
method toHalfPoints (line 92) | public int toHalfPoints() {
method isValid (line 97) | public boolean isValid() {
method isPercent (line 101) | public boolean isPercent() {
method isValidPercent (line 105) | public boolean isValidPercent() {
method of (line 109) | public static CSSLength of(String text) {
method equals (line 120) | @Override
method hashCode (line 130) | @Override
FILE: src/main/java/org/ddr/poi/html/util/CSSLengthUnit.java
type CSSLengthUnit (line 34) | public enum CSSLengthUnit {
method CSSLengthUnit (line 79) | CSSLengthUnit(String literal, boolean system, boolean relative, boolea...
method getLiteral (line 89) | public String getLiteral() {
method isSystem (line 93) | public boolean isSystem() {
method isRelative (line 97) | public boolean isRelative() {
method isRelativeToParent (line 101) | public boolean isRelativeToParent() {
method toString (line 105) | @Override
method absoluteFactor (line 110) | public double absoluteFactor() {
method to (line 114) | public double to(CSSLengthUnit other) {
method of (line 131) | public static CSSLengthUnit of(String literal) {
FILE: src/main/java/org/ddr/poi/html/util/CSSStyleUtils.java
class CSSStyleUtils (line 25) | public class CSSStyleUtils {
method isEmpty (line 39) | public static boolean isEmpty(CSSStyleDeclarationImpl style) {
method newParser (line 46) | public static CSSOMParser newParser() {
method parse (line 56) | public static CSSStyleDeclarationImpl parse(String inlineStyle) {
method parseValue (line 74) | public static CSSValue parseValue(String value) {
method newProperty (line 90) | public static Property newProperty(String key, String value) {
method split (line 99) | public static void split(CSSStyleDeclarationImpl style) {
method splitBackground (line 159) | private static void splitBackground(CSSValueImpl valueList, int length...
method splitBorder (line 179) | private static void splitBorder(CSSValueImpl valueList, int length, CS...
method splitBorder (line 194) | private static void splitBorder(CSSValueImpl valueList, int length, CS...
method handleBorderValue (line 210) | private static void handleBorderValue(CSSStyleDeclarationImpl style, i...
method handleBorderValue (line 226) | private static void handleBorderValue(CSSStyleDeclarationImpl style, i...
method splitFont (line 243) | private static void splitFont(CSSValueImpl valueList, int length, CSSS...
method splitBox (line 302) | private static void splitBox(CSSValueImpl valueList, int length, CSSSt...
method splitListStyle (line 324) | private static void splitListStyle(CSSValueImpl valueList, int length,...
method handleListStyleValue (line 342) | private static void handleListStyleValue(CSSStyleDeclarationImpl style...
method splitTextDecoration (line 350) | private static void splitTextDecoration(CSSValueImpl valueList, int le...
FILE: src/main/java/org/ddr/poi/html/util/Colors.java
class Colors (line 33) | public class Colors {
method getColorByName (line 199) | public static String getColorByName(String name) {
method getColorByName (line 210) | public static String getColorByName(String name, String defaultColor) {
method fromHSL (line 222) | public static String fromHSL(float h, float s, float l) {
method hsl2rgb (line 227) | private static float[] hsl2rgb(float h, float s, float l) {
method hue2rgb (line 241) | private static float hue2rgb(float h, float s, float l, int n) {
method fromHWB (line 255) | public static String fromHWB(float h, float w, float b) {
method toInt (line 270) | private static int toInt(float f) {
method degrees (line 274) | private static float degrees(String s) {
method toHexString (line 296) | public static String toHexString(int r, int g, int b) {
method fromStyle (line 307) | public static String fromStyle(String style, String defaultColor) {
method fromStyle (line 392) | public static String fromStyle(String style) {
method warn (line 396) | private static void warn(String style) {
method maybe (line 406) | public static boolean maybe(String style) {
FILE: src/main/java/org/ddr/poi/html/util/ColumnStyle.java
class ColumnStyle (line 11) | public class ColumnStyle {
method ColumnStyle (line 15) | public ColumnStyle(CSSStyleDeclarationImpl style, CSSLength width) {
method getStyle (line 20) | public CSSStyleDeclarationImpl getStyle() {
method getWidth (line 24) | public CSSLength getWidth() {
FILE: src/main/java/org/ddr/poi/html/util/EmptyCSSStyle.java
class EmptyCSSStyle (line 16) | public class EmptyCSSStyle extends CSSStyleDeclarationImpl {
method setProperties (line 17) | @Override
method getProperties (line 22) | @Override
method setProperty (line 27) | @Override
method setCssText (line 32) | @Override
FILE: src/main/java/org/ddr/poi/html/util/InlineStyle.java
class InlineStyle (line 27) | public final class InlineStyle {
method InlineStyle (line 37) | public InlineStyle(CSSStyleDeclarationImpl declaration, boolean block) {
method getDeclaration (line 42) | public CSSStyleDeclarationImpl getDeclaration() {
method isBlock (line 46) | public boolean isBlock() {
FILE: src/main/java/org/ddr/poi/html/util/JsoupUtils.java
class JsoupUtils (line 39) | public class JsoupUtils {
method selectChildren (line 47) | public static void selectChildren(Elements collection, Element parent,...
method children (line 65) | public static Elements children(Element parent, String tag) {
method children (line 78) | public static Elements children(Element parent, String... tags) {
method firstChild (line 92) | public static Element firstChild(Element parent, String tag) {
method childRows (line 110) | public static Elements childRows(Element parent) {
method parse (line 131) | public static Document parse(String html) {
FILE: src/main/java/org/ddr/poi/html/util/ListStyle.java
class ListStyle (line 9) | public class ListStyle {
method ListStyle (line 18) | public ListStyle(ListStyleType numberFormat, boolean hanging, int left...
method getNumberFormat (line 25) | public ListStyleType getNumberFormat() {
method isHanging (line 29) | public boolean isHanging() {
method getLeft (line 33) | public int getLeft() {
method getRight (line 37) | public int getRight() {
method toString (line 41) | @Override
FILE: src/main/java/org/ddr/poi/html/util/ListStyleType.java
type ListStyleType (line 16) | public interface ListStyleType {
method getName (line 26) | String getName();
method getFormat (line 28) | STNumberFormat.Enum getFormat();
method getText (line 33) | String getText();
method getFont (line 35) | String getFont();
type Unordered (line 37) | enum Unordered implements ListStyleType {
method Unordered (line 54) | Unordered(String name, STNumberFormat.Enum format, String text, Stri...
method getName (line 61) | @Override
method getFormat (line 66) | @Override
method getText (line 71) | @Override
method getFont (line 76) | @Override
method of (line 81) | public static ListStyleType of(String type) {
type Ordered (line 89) | enum Ordered implements ListStyleType {
method Ordered (line 143) | Ordered(String name, STNumberFormat.Enum format, String text, String...
method getName (line 150) | @Override
method getFormat (line 155) | @Override
method getText (line 160) | @Override
method getFont (line 165) | @Override
method of (line 170) | public static ListStyleType of(String type) {
FILE: src/main/java/org/ddr/poi/html/util/NamedBorderWidth.java
type NamedBorderWidth (line 32) | public enum NamedBorderWidth {
method NamedBorderWidth (line 43) | NamedBorderWidth(String name, int px) {
method getName (line 48) | public String getName() {
method getWidth (line 52) | public CSSLength getWidth() {
method of (line 56) | public static NamedBorderWidth of(String name) {
method contains (line 60) | public static boolean contains(String name) {
FILE: src/main/java/org/ddr/poi/html/util/NamedFontSize.java
type NamedFontSize (line 32) | public enum NamedFontSize {
method NamedFontSize (line 48) | NamedFontSize(String name, double pt) {
method getName (line 53) | public String getName() {
method getSize (line 57) | public CSSLength getSize() {
method of (line 61) | public static NamedFontSize of(String name) {
FILE: src/main/java/org/ddr/poi/html/util/NumberingContext.java
class NumberingContext (line 48) | public class NumberingContext {
method NumberingContext (line 65) | public NumberingContext(XWPFDocument document) {
method startLevel (line 74) | public void startLevel(ListStyle listStyle) {
method endLevel (line 86) | public void endLevel() {
method add (line 118) | public void add(XWPFParagraph paragraph) {
method getNumberId (line 139) | private BigInteger getNumberId(String key) {
method getLevelText (line 202) | private String getLevelText(ListStyleType listStyleType, int i) {
method getOrderedLevelText (line 218) | private String getOrderedLevelText(int i) {
method getFormatKey (line 222) | private String getFormatKey() {
method setIndent (line 230) | public void setIndent(int indent) {
method setHanging (line 236) | public void setHanging(int hanging) {
method setSpacing (line 242) | public void setSpacing(STLevelSuffix.Enum spacing) {
method contains (line 246) | public boolean contains(XWPFParagraph paragraph) {
FILE: src/main/java/org/ddr/poi/html/util/RenderUtils.java
class RenderUtils (line 74) | public class RenderUtils {
method align (line 133) | public static ParagraphAlignment align(String textAlign) {
method underline (line 160) | public static STUnderline.Enum underline(String textDecorationStyle) {
method getPPr (line 177) | public static CTPPr getPPr(CTStyle ctStyle) {
method getPPr (line 181) | public static CTPPr getPPr(CTP ctp) {
method getPBdr (line 185) | public static CTPBdr getPBdr(CTPPr pr) {
method getJc (line 189) | public static CTJc getJc(CTPPr pr) {
method getRPr (line 193) | public static CTRPr getRPr(CTR ctr) {
method getTcPr (line 197) | public static CTTcPr getTcPr(CTTc tc) {
method getTcMar (line 201) | public static CTTcMar getTcMar(CTTcPr tcPr) {
method getTcMar (line 205) | public static CTTcMar getTcMar(XWPFTableCell cell) {
method getShd (line 210) | public static CTShd getShd(CTPPr pPr) {
method getInd (line 214) | public static CTInd getInd(CTPPr pPr) {
method getInd (line 218) | public static CTInd getInd(XWPFParagraph paragraph) {
method getSpacing (line 223) | public static CTSpacing getSpacing(CTPPr pPr) {
method getSpacing (line 227) | public static CTSpacing getSpacing(XWPFParagraph paragraph) {
method getColor (line 232) | public static CTColor getColor(CTRPr rPr) {
method getUnderline (line 236) | public static CTUnderline getUnderline(CTRPr rPr) {
method getAvailableWidthInEMU (line 246) | public static int getAvailableWidthInEMU(IBody body) {
method paragraphStyle (line 288) | public static void paragraphStyle(HtmlRenderContext context, XWPFParag...
method setSpacing (line 328) | private static void setSpacing(HtmlRenderContext context, XWPFParagrap...
method setIndentation (line 374) | private static void setIndentation(HtmlRenderContext context, XWPFPara...
method indent (line 423) | private static boolean indent(HtmlRenderContext context, XWPFParagraph...
method setBorder (line 452) | private static boolean setBorder(HtmlRenderContext context, Object xwp...
method borderStyle (line 488) | private static STBorder.Enum borderStyle(String style) {
method getTblBorders (line 515) | public static CTTblBorders getTblBorders(CTTbl tbl) {
method getTop (line 520) | private static CTBorder getTop(Object e) {
method getParagraphTop (line 535) | public static CTBorder getParagraphTop(CTP paragraph) {
method getTableTop (line 541) | public static CTBorder getTableTop(CTTbl table) {
method getTableCellTop (line 546) | public static CTBorder getTableCellTop(CTTc cell) {
method getRight (line 552) | private static CTBorder getRight(Object e) {
method getParagraphRight (line 567) | public static CTBorder getParagraphRight(CTP paragraph) {
method getTableRight (line 573) | public static CTBorder getTableRight(CTTbl tbl) {
method getTableCellRight (line 578) | public static CTBorder getTableCellRight(CTTc cell) {
method getBottom (line 584) | private static CTBorder getBottom(Object e) {
method getParagraphBottom (line 599) | public static CTBorder getParagraphBottom(CTP paragraph) {
method getTableBottom (line 605) | public static CTBorder getTableBottom(CTTbl table) {
method getTableCellBottom (line 610) | public static CTBorder getTableCellBottom(CTTc cell) {
method getLeft (line 616) | private static CTBorder getLeft(Object e) {
method getParagraphLeft (line 631) | public static CTBorder getParagraphLeft(CTP paragraph) {
method getTableLeft (line 637) | public static CTBorder getTableLeft(CTTbl table) {
method getTableCellLeft (line 642) | public static CTBorder getTableCellLeft(CTTc cell) {
method smallerFontSizeInHalfPoints (line 654) | public static int smallerFontSizeInHalfPoints(int inheritedSizeInHalfP...
method largerFontSizeInHalfPoints (line 670) | public static int largerFontSizeInHalfPoints(int inheritedSizeInHalfPo...
method emuToTwips (line 684) | public static int emuToTwips(int emu) {
method tableStyle (line 695) | public static void tableStyle(HtmlRenderContext context, XWPFTable tab...
method getTblInsideV (line 735) | public static CTBorder getTblInsideV(CTTblBorders tblBorders) {
method getTblInsideH (line 739) | public static CTBorder getTblInsideH(CTTblBorders tblBorders) {
method cellStyle (line 751) | public static void cellStyle(HtmlRenderContext context, XWPFTableCell ...
method setCellPadding (line 787) | private static void setCellPadding(HtmlRenderContext context, XWPFTabl...
method setBorder (line 829) | public static boolean setBorder(HtmlRenderContext context, Object xwpf...
method indent (line 841) | private static boolean indent(HtmlRenderContext context, XWPFTable tab...
method getInd (line 859) | public static CTTblWidth getInd(CTTblPr tblPr) {
method getTblBorders (line 863) | public static CTTblBorders getTblBorders(CTTblPr tblPr) {
method getShd (line 867) | public static CTShd getShd(CTTblPr tblPr) {
method getTblPr (line 871) | public static CTTblPr getTblPr(CTTbl ctTbl) {
method getTcBorders (line 879) | public static CTTcBorders getTcBorders(CTTcPr tcPr) {
method getShd (line 883) | public static CTShd getShd(CTTcPr tcPr) {
method alignTable (line 893) | public static TableRowAlign alignTable(String cssFloat) {
method alignTableCell (line 913) | public static XWPFTableCell.XWPFVertAlign alignTableCell(String vertic...
method inlineToAnchor (line 933) | public static CTAnchor inlineToAnchor(CTDrawing drawing) {
FILE: src/main/java/org/ddr/poi/html/util/Span.java
class Span (line 27) | public class Span {
method Span (line 33) | public Span(int row, int column, boolean enabled, CSSStyleDeclarationI...
method getRow (line 40) | public int getRow() {
method getColumn (line 44) | public int getColumn() {
method isEnabled (line 48) | public boolean isEnabled() {
method setRow (line 52) | public void setRow(int row) {
method setColumn (line 56) | public void setColumn(int column) {
method setEnabled (line 60) | public void setEnabled(boolean enabled) {
method getStyle (line 64) | public CSSStyleDeclarationImpl getStyle() {
method toString (line 68) | public String toString() {
FILE: src/main/java/org/ddr/poi/html/util/SpanWidth.java
class SpanWidth (line 28) | public class SpanWidth extends CSSLength {
method SpanWidth (line 34) | public SpanWidth(CSSLength length, int column, int span, boolean expli...
method setLength (line 42) | public void setLength(Map<Integer, CSSLength> map) {
method getSpan (line 87) | public int getSpan() {
method getColumn (line 91) | public int getColumn() {
method getLengths (line 95) | public CSSLength[] getLengths() {
method equals (line 99) | @Override
method hashCode (line 110) | @Override
FILE: src/main/java/org/ddr/poi/html/util/WhiteSpaceRule.java
type WhiteSpaceRule (line 32) | public enum WhiteSpaceRule {
method WhiteSpaceRule (line 45) | WhiteSpaceRule(String value, boolean keepLineBreak, boolean keepSpaceA...
method getValue (line 52) | public String getValue() {
method isKeepLineBreak (line 56) | public boolean isKeepLineBreak() {
method isKeepSpaceAndTab (line 60) | public boolean isKeepSpaceAndTab() {
method isKeepTrailingSpace (line 64) | public boolean isKeepTrailingSpace() {
method isNormal (line 68) | public boolean isNormal() {
method of (line 76) | public static WhiteSpaceRule of(String value) {
method of (line 80) | public static WhiteSpaceRule of(String value, WhiteSpaceRule defaultRu...
FILE: src/main/java/org/ddr/poi/html/util/XWPFParagraphRuns.java
class XWPFParagraphRuns (line 16) | public class XWPFParagraphRuns {
method XWPFParagraphRuns (line 33) | @SuppressWarnings("unchecked")
method remove (line 48) | public void remove(int pos) {
method runCount (line 56) | public int runCount() {
FILE: src/main/java/org/ddr/poi/latex/LaTeXRenderPolicy.java
class LaTeXRenderPolicy (line 17) | public class LaTeXRenderPolicy extends AbstractRenderPolicy<String> {
method LaTeXRenderPolicy (line 21) | public LaTeXRenderPolicy() {
method LaTeXRenderPolicy (line 25) | public LaTeXRenderPolicy(MathRenderConfig config) {
method getConfig (line 29) | public MathRenderConfig getConfig() {
method validate (line 33) | @Override
method doRender (line 44) | @Override
method afterRender (line 51) | @Override
FILE: src/main/java/org/ddr/poi/latex/LaTeXUtils.java
class LaTeXUtils (line 46) | public class LaTeXUtils {
method createSession (line 57) | public static SnuggleSession createSession() {
method parse (line 68) | public static boolean parse(SnuggleSession session, String data) {
method renderTo (line 93) | public static void renderTo(XWPFParagraph paragraph, CTR ctr, SnuggleS...
method renderTag (line 113) | private static void renderTag(XWPFParagraph paragraph, CTR ctr, Node n...
class Initializer (line 168) | private static class Initializer {
FILE: src/main/java/org/ddr/poi/latex/TagHandler.java
class TagHandler (line 18) | public class TagHandler implements CommandHandler {
method handleCommand (line 19) | @Override
FILE: src/main/java/org/ddr/poi/latex/TextCircledHandler.java
class TextCircledHandler (line 11) | public class TextCircledHandler implements CommandHandler {
method handleCommand (line 15) | @Override
FILE: src/main/java/org/ddr/poi/math/EmptyEOfNaryDisplayMode.java
type EmptyEOfNaryDisplayMode (line 9) | public enum EmptyEOfNaryDisplayMode {
method EmptyEOfNaryDisplayMode (line 29) | EmptyEOfNaryDisplayMode(int value) {
method getValue (line 33) | public int getValue() {
method isZeroWidth (line 37) | public boolean isZeroWidth() {
method isHidden (line 41) | public boolean isHidden() {
FILE: src/main/java/org/ddr/poi/math/MathMLRenderPolicy.java
class MathMLRenderPolicy (line 32) | public class MathMLRenderPolicy extends AbstractRenderPolicy<String> {
method MathMLRenderPolicy (line 36) | public MathMLRenderPolicy() {
method MathMLRenderPolicy (line 40) | public MathMLRenderPolicy(MathRenderConfig config) {
method getConfig (line 44) | public MathRenderConfig getConfig() {
method validate (line 48) | @Override
method doRender (line 57) | @Override
method afterRender (line 66) | @Override
FILE: src/main/java/org/ddr/poi/math/MathMLUtils.java
class MathMLUtils (line 55) | public class MathMLUtils {
method renderTo (line 79) | public static void renderTo(XWPFParagraph paragraph, CTR ctr, String m...
method normalize (line 102) | public static String normalize(String math) {
class Initializer (line 121) | private static class Initializer {
method createTransformer (line 125) | private static Xslt30Transformer createTransformer() {
method newSerializer (line 137) | private static Serializer newSerializer(StringWriter sw) {
method addMath (line 152) | private static void addMath(XWPFParagraph paragraph, CTR ctr, String o...
method insertMathAfterRun (line 202) | private static void insertMathAfterRun(CTP ctp, CTR ctr, CTOMath ctoMa...
FILE: src/main/java/org/ddr/poi/math/MathRenderConfig.java
class MathRenderConfig (line 8) | public class MathRenderConfig {
method getEmptyEOfNaryOption (line 11) | public EmptyEOfNaryDisplayMode getEmptyEOfNaryOption() {
method setEmptyEOfNaryOption (line 15) | public void setEmptyEOfNaryOption(EmptyEOfNaryDisplayMode emptyEOfNary...
FILE: src/main/java/org/ddr/poi/util/ByteArrayCopyStream.java
class ByteArrayCopyStream (line 28) | public class ByteArrayCopyStream extends ByteArrayOutputStream {
method ByteArrayCopyStream (line 29) | public ByteArrayCopyStream() {
method ByteArrayCopyStream (line 32) | public ByteArrayCopyStream(int size) {
method toInput (line 39) | public ByteArrayInputStream toInput() {
FILE: src/main/java/org/ddr/poi/util/HttpURLConnectionUtils.java
class HttpURLConnectionUtils (line 49) | public class HttpURLConnectionUtils {
class X509TrustAllManager (line 53) | public static class X509TrustAllManager implements X509TrustManager {
method checkClientTrusted (line 54) | @Override
method checkServerTrusted (line 58) | @Override
method getAcceptedIssuers (line 62) | @Override
class TrustAllHostname (line 68) | public static class TrustAllHostname implements HostnameVerifier {
method verify (line 69) | @Override
method connect (line 81) | public static HttpURLConnection connect(String urlSpec) throws IOExcep...
method connect (line 93) | public static HttpURLConnection connect(String urlSpec, String user, S...
method trustAllSslSocketFactory (line 121) | public static SSLSocketFactory trustAllSslSocketFactory() {
method initFormData (line 138) | public static String initFormData(HttpURLConnection connect) throws Pr...
method initUserAgent (line 149) | public static void initUserAgent(HttpURLConnection connect) {
method addFormData (line 159) | public static void addFormData(OutputStream outputStream, byte[] bound...
FILE: src/main/java/org/ddr/poi/util/XmlUtils.java
class XmlUtils (line 30) | public class XmlUtils {
method removeNamespaces (line 55) | public static void removeNamespaces(XmlObject xmlObject) {
FILE: src/main/java/org/jsoup/parser/CustomHtmlTreeBuilder.java
class CustomHtmlTreeBuilder (line 15) | public class CustomHtmlTreeBuilder extends HtmlTreeBuilder {
method reconstructFormattingElements (line 16) | @Override
method insert (line 30) | @Override
method parseFragment (line 45) | @Override
method isSvgElement (line 50) | private boolean isSvgElement() {
FILE: src/test/java/org/ddr/poi/FileReader.java
class FileReader (line 29) | public class FileReader {
method readFile (line 30) | public static String readFile(String resourceFile) throws IOException {
FILE: src/test/java/org/ddr/poi/html/HtmlRenderPolicyTest.java
class HtmlRenderPolicyTest (line 29) | class HtmlRenderPolicyTest {
method doRender (line 31) | @Test
FILE: src/test/java/org/ddr/poi/latex/LaTeXRenderPolicyTest.java
class LaTeXRenderPolicyTest (line 13) | class LaTeXRenderPolicyTest {
method doRender (line 15) | @Test
FILE: src/test/java/org/ddr/poi/math/MathMLRenderPolicyTest.java
class MathMLRenderPolicyTest (line 36) | class MathMLRenderPolicyTest {
method doRender (line 38) | @Test
Condensed preview — 114 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (678K chars).
[
{
"path": ".gitattributes",
"chars": 154,
"preview": "#\n# https://help.github.com/articles/dealing-with-line-endings/\n#\n# These are explicitly windows files and should use cr"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 552,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".gitignore",
"chars": 673,
"preview": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Packa"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 1945,
"preview": "# poi-tl-ext\n 
About this extraction
This page contains the full source code of the draco1023/poi-tl-ext GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 114 files (616.6 KB), approximately 157.4k tokens, and a symbol index with 681 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.