Compare commits

...

53 Commits

Author SHA1 Message Date
dependabot[bot]
2b8c4261b7 Bump poi from 3.17 to 4.1.1 in /server
Bumps poi from 3.17 to 4.1.1.

---
updated-dependencies:
- dependency-name: org.apache.poi:poi
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-29 18:30:27 +00:00
chenkailing
82f6d3565f 优化 file:// 协议访问授信目录的代码结构 2022-05-25 19:37:29 +08:00
gitchenjh
8c68834e17 Merge pull request #342 from gitchenjh/master
修复使用http文件流下载时文件名编码异常Gitee-#I4W0TQ
2022-03-09 19:48:22 -06:00
陈精华
ba7b6eed4a 修复使用http文件流下载时文件名编码异常Gitee-#I4W0TQ 2022-03-10 09:47:05 +08:00
gitchenjh
051ad0f4ea Merge pull request #334 from NullXin/wzxdev
ppt显示问题
2022-02-21 00:10:17 -06:00
1192247166@qq.com
05935c29e1 1、左边侧边栏显示总页码显示为0
2、修改左屏幕分辨率过高,显示图片过大。
2022-01-26 11:38:12 +08:00
zhangzhen1979
6a2735ec3d tif图片预览。因无法下载jai_core包,所以去掉pom中geotoolkit仓库的设置 2021-12-20 17:31:18 +08:00
zhangzhen1979
8bc53f76eb tif图片预览。更新本地jai_core包的版本至1.1.3 2021-12-20 17:31:18 +08:00
zhangzhen1979
48f6a507dc tif图片预览。根据 @gaoxingzaq 反馈和帮助,修改tif文件的jpg、pdf模式预览功能,兼容多页tif的pdf转换、jpg转换,以及jpg在线多页预览功能。 2021-12-20 17:31:18 +08:00
BearBen
20f328906c 解压方案新版测试修复乱码Linux问题 2021-12-17 17:40:23 +08:00
gitchenjh
4d1e2eb9c6 Merge pull request #326 from gitchenjh/master
修复 #311
2021-12-17 17:38:58 +08:00
gitchenjh
97542b06fa Create maven.yml
add Github Action
2021-12-17 17:31:32 +08:00
陈精华
0c93c7e4b6 update README 2021-12-17 17:22:31 +08:00
陈精华
56d9906c74 修复ppt预览在改过context-path后出现异常 (#311) 2021-12-17 17:18:58 +08:00
gitchenjh
5cfe37433f Merge pull request #321 from gitchenjh/master
启动脚本版本更新为4.1.0-SNAPSHOT
2021-12-14 13:38:00 +08:00
陈精华
45ebef3b74 启动脚本版本更新为4.1.0-SNAPSHOT 2021-12-14 13:37:03 +08:00
gitchenjh
2ed294bd65 Merge pull request #317 from zzzhouuu/master
feat: 通过配置控制PDF.js viewer toolbar & secondaryToolbar功能按钮
2021-12-10 17:31:25 +08:00
zhangzhen1979
91f3348a2f * tif图片预览,根据反馈的意见,已经全部修改 2021-12-10 17:13:57 +08:00
zhangzhen1979
d424de5e9a * tif图片预览,根据反馈的意见,已经全部修改 2021-12-10 17:13:57 +08:00
zhangzhen1979
6387ac21c9 * tif图片预览,根据反馈的意见,已经全部修改
1、配置文件、配置项默认值已改回image、tif
2、去掉了静态工具类上的@component 注解
3、修改OnlinePreview处理,在切换image方式预览时url中加入参数previewType=image,加入对应的处理,兼容在pdf预览模式下切换到JPG方式预览
2021-12-10 17:13:57 +08:00
zhangzhen1979
6dce47e47f * tif图片预览,在application.properties中加入tif.preview.type = ${KK_TIF_PREVIEW_TYPE:tif},可以控制使用tif、jpg、pdf方式预览。
修改相应代码,加入必要的判断处理。
2021-12-10 17:13:57 +08:00
zhangzhen1979
b7760ab42a * tif图片预览,改为支持转换为jpg后预览(JAI支持);加入tif->jpg->pdf转换后,以pdf格式预览,此种模式可完美支持打印纸张自适应。 2021-12-10 17:13:57 +08:00
周游
e0405bc5f6 Merge remote-tracking branch 'upstream/master' 2021-12-09 09:32:34 +08:00
gitchenjh
14151a6c46 Merge pull request #315 from gitchenjh/master
4.1.0迭代开启
2021-12-07 16:13:01 +08:00
陈精华
00555d3544 4.1.0迭代开启 2021-12-07 16:11:50 +08:00
Nevan Chow
7b2adc3979 Merge branch 'master' into master 2021-11-30 17:53:24 +08:00
gitchenjh
0a5fc1e4a8 Merge pull request #301 from fangzhengjin/patch-1
fix: PDF/word图片模式预览时, 下翻图片时频繁请求图片资源
2021-11-28 17:05:39 +08:00
gitchenjh
304d974b38 Merge pull request #268 from kekingcn/dependabot/maven/server/org.apache.commons-commons-compress-1.21
Bump commons-compress from 1.19 to 1.21 in /server
2021-11-25 16:19:52 +08:00
gitchenjh
6792a7afa7 Merge pull request #282 from kekingcn/dependabot/maven/server/com.thoughtworks.xstream-xstream-1.4.18
Bump xstream from 1.4.17 to 1.4.18 in /server
2021-11-25 16:19:39 +08:00
gitchenjh
ded297de59 Merge pull request #310 from gitchenjh/master
修复文件名包含空格时不能预览 #294
2021-11-25 16:11:35 +08:00
陈精华
f616678d83 修复文件名包含空格时不能预览 #294 2021-11-25 16:10:33 +08:00
gitchenjh
e10273dcdd Merge pull request #289 from sanxiHsu/master
Update startup.sh
2021-11-25 15:48:06 +08:00
gitchenjh
343269670c Merge pull request #288 from sanxiHsu/patch-2
Update shutdown.sh
2021-11-25 15:47:53 +08:00
gitchenjh
8af157b848 Merge pull request #309 from gitchenjh/master
处理Issues
2021-11-25 14:07:33 +08:00
陈精华
9d65c999e5 增加配置,限制允许预览的本地文件夹 #304 2021-11-25 13:47:51 +08:00
陈精华
4fe0d0edc9 PDF.js版本更新,解决 #264 2021-11-25 10:48:56 +08:00
周游
b1aab27338 feat: 通过配置控制PDF.js viewer toolbar & secondaryToolbar功能按钮 2021-11-16 11:50:12 +08:00
ZhengJin Fang
727e9ae9ed fix: PDF/word图片模式预览时, 下翻图片时频繁请求图片资源 2021-11-10 16:27:55 +08:00
sanxiHsu
dc9df5d760 Update startup.sh
更新根据pid文件来识别后台进程是否处于运行状态,避免同时启动多个后台进程。
2021-09-18 06:10:57 +00:00
sanxiHsu
b7de791658 Update shutdown.sh
原脚本使用grep,但grep只能用于人工操作无法用于自动脚本,原因在于无论是否搜索到正确结果,都会返回该grep进程的ID号,这时候kill就报错了。
更改为pid文件方式也是业界较为稳妥的方式。
2021-09-18 05:13:30 +00:00
Yiding He
82c7b59650 在主页上添加输入URL地址来预览的表单 2021-09-13 16:00:38 +08:00
dependabot[bot]
e0b9d8f476 Bump xstream from 1.4.17 to 1.4.18 in /server
Bumps [xstream](https://github.com/x-stream/xstream) from 1.4.17 to 1.4.18.
- [Release notes](https://github.com/x-stream/xstream/releases)
- [Commits](https://github.com/x-stream/xstream/commits)

---
updated-dependencies:
- dependency-name: com.thoughtworks.xstream:xstream
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-25 15:56:05 +00:00
陈精华
db2ed3a555 修复某些版本LibreOffice本地安装了,但检测不到问题 2021-08-25 15:27:36 +08:00
xiaxiaxiaxia
6efc375066 Fix:修复install.sh安装脚本路径错误 2021-08-24 16:05:41 +08:00
Yiding He
9a4c864490 重命名 env() 2021-08-24 13:56:07 +08:00
Yiding He
a3081ef4a9 修复单元测试失败的问题 2021-08-24 13:56:07 +08:00
Yiding He
43374e02bd 让 office-plugin 项目的语言级别与父项目保持一致 2021-08-24 11:05:12 +08:00
gitchenjh
fb8a19469b Merge pull request #272 from jerrykcode/master
修复: 文件名含有特殊字符时无法预览
2021-08-21 14:19:40 +08:00
jerrykcode
f2d5f4a86c 为WebUtils.encodeUrlFileName方法添加测试用例 2021-08-20 14:15:42 +08:00
jerrykcode
2177aed64f 修复: 文件名含有特殊字符时无法预览 2021-08-19 20:26:37 +08:00
dependabot[bot]
d20ac8fafc Bump commons-compress from 1.19 to 1.21 in /server
Bumps commons-compress from 1.19 to 1.21.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-compress
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-02 17:25:07 +00:00
捏造的信仰
2395a489a3 Update startup.sh
Add `/usr/lib64/libreoffice` to search path list.
2021-07-21 15:42:11 +08:00
ypgsh
0b8eedf935 feat: add dxf file type in cad scope 2021-07-21 15:41:35 +08:00
303 changed files with 91516 additions and 84908 deletions

24
.github/workflows/maven.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'adopt'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml

View File

@@ -6,6 +6,7 @@ RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe m
apt-get clean && apt-get update &&\
apt-get install -y locales && apt-get install -y language-pack-zh-hans &&\
localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 && locale-gen zh_CN.UTF-8 &&\
export DEBIAN_FRONTEND=noninteractive &&\
apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
apt-get install -y libxrender1 && apt-get install -y libxt6 && apt-get install -y libxext-dev && apt-get install -y libfreetype6-dev &&\
apt-get install -y wget && apt-get install -y ttf-mscorefonts-installer && apt-get install -y fontconfig &&\
@@ -37,5 +38,5 @@ ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-4.0.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.0.0/config/application.properties","-jar","/opt/kkFileView-4.0.0/bin/kkFileView-4.0.0.jar"]
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-4.1.0-SNAPSHOT/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.1.0-SNAPSHOT/config/application.properties","-jar","/opt/kkFileView-4.1.0-SNAPSHOT/bin/kkFileView-4.1.0-SNAPSHOT.jar"]

View File

@@ -1,4 +1,4 @@
# file-online-preview
# kkFileView
[![GitHub license](https://img.shields.io/github/license/kekingcn/kkFileView.svg?style=flat-square)](https://github.com/kekingcn/kkFileView/blob/master/LICENSE)

View File

@@ -1,4 +1,4 @@
# file-online-preview
# kkFileView
此项目为文件文档在线预览项目解决方案对标业内付费产品有[永中office](http://dcs.yozosoft.com/)】【[office365](http://www.officeweb365.com/)】【[idocv](https://www.idocv.com/)】等在取得公司高层同意后以Apache协议开源出来反哺社区在此特别感谢@唐老大的支持以及@端木详笑的贡献。该项目使用流行的spring boot搭建易上手和部署基本支持主流办公文档的在线预览如doc,docx,Excel,pdf,txt,zip,rar,图片等等
### 项目特性

View File

@@ -5,9 +5,9 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>filepreview</artifactId>
<artifactId>kkFileView-parent</artifactId>
<groupId>cn.keking</groupId>
<version>4.0.0</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>office-plugin</artifactId>
@@ -86,16 +86,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>

View File

@@ -104,9 +104,14 @@ public class OfficeUtils {
"/opt/openoffice.org3",
"/opt/openoffice",
"/opt/libreoffice",
"/opt/libreoffice6.0",
"/opt/libreoffice6.1",
"/opt/libreoffice6.2",
"/opt/libreoffice6.3",
"/opt/libreoffice6.4",
"/opt/libreoffice7.0",
"/opt/libreoffice7.1",
"/opt/libreoffice7.2",
"/opt/openoffice4",
"/usr/lib/openoffice",
"/usr/lib/libreoffice"

View File

@@ -29,23 +29,45 @@ public class ConfigUtils {
return userDir;
}
// 获取环境变量,如果找不到则返回默认值
@SuppressWarnings("SameParameterValue")
private static String getEnvOrDefault(String key, String def) {
String value = System.getenv(key);
return value == null ? def : value;
}
public static String getOfficePluginPath() {
String userDir = System.getenv("KKFILEVIEW_BIN_FOLDER");
if (userDir == null) {
userDir = System.getProperty("user.dir");
}
if (userDir.endsWith("bin")) {
userDir = userDir.substring(0, userDir.length() - 4);
} else {
String separator = File.separator;
if (!userDir.contains(OFFICE_PLUGIN_NAME)) {
userDir = userDir + separator + OFFICE_PLUGIN_NAME;
// 返回参数列表中第一个真实存在的路径,或者 null
private static String firstExists(File... paths) {
for (File path : paths) {
if (path.exists()) {
return path.getAbsolutePath();
}
}
return userDir;
return null;
}
public static String getOfficePluginPath() {
String userDir = System.getProperty("user.dir");
String binFolder = getEnvOrDefault("KKFILEVIEW_BIN_FOLDER", userDir);
File pluginPath = new File(binFolder);
// 如果指定了 bin 或其父目录,则返回父目录
// 否则在当前目录和父目录中寻找 office-plugin
if (new File(pluginPath, "bin").exists()) {
return pluginPath.getAbsolutePath();
} else if (pluginPath.exists() && pluginPath.getName().equals("bin")) {
return pluginPath.getParentFile().getAbsolutePath();
} else {
return firstExists(
new File(pluginPath, OFFICE_PLUGIN_NAME),
new File(pluginPath.getParentFile(), OFFICE_PLUGIN_NAME)
);
}
}
public static String getCustomizedConfigPath() {
String homePath = getHomePath();
String separator = java.io.File.separator;

View File

@@ -5,8 +5,8 @@
<packaging>pom</packaging>
<groupId>cn.keking</groupId>
<artifactId>filepreview</artifactId>
<version>4.0.0</version>
<artifactId>kkFileView-parent</artifactId>
<version>4.1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
@@ -22,7 +22,7 @@
<module>server</module>
</modules>
<name>file-online-preview</name>
<name>kkFileView-parent</name>
<description>专注文件在线预览服务</description>
<url>https://github.com/kekingcn/kkFileView</url>

Binary file not shown.

Binary file not shown.

View File

@@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>filepreview</artifactId>
<artifactId>kkFileView-parent</artifactId>
<groupId>cn.keking</groupId>
<version>4.0.0</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>kkFileView</artifactId>
@@ -28,6 +28,17 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- rar5 的支持 和其他众多压缩支持 可参考 package net.sf.sevenzipjbinding.ArchiveFormat; -->
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -67,7 +78,7 @@
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
@@ -99,7 +110,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.19</version>
<version>1.21</version>
</dependency>
<!-- 解压(rar)-->
<dependency>
@@ -149,7 +160,7 @@
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.17</version>
<version>1.4.18</version>
</dependency>
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
@@ -250,6 +261,28 @@
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_core</artifactId>
<version>1.1.3</version>
<scope>system</scope>
<systemPath>${basedir}/lib/jai_core-1.1.3.jar</systemPath>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_codec</artifactId>
<version>1.1.3</version>
<scope>system</scope>
<systemPath>${basedir}/lib/jai_codec-1.1.3.jar</systemPath>
</dependency>
</dependencies>
<build>

View File

@@ -7,7 +7,7 @@ install_redhat() {
if [ $? -eq 0 ];then
yum install -y libXext.x86_64
yum groupinstall -y "X Window System"
yum installlocalho
yum localinstall *.rpm
echo 'install finshed...'
else
echo 'download package error...'
@@ -15,7 +15,7 @@ install_redhat() {
}
install_ubuntu() {
wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_deb.tar.gz -cO LibreOffice_7_deb.gz && tar -zxf /tmp/LibreOffice_7_deb.tar.gz && cd /tmp/LibreOffice_7.1.4.2_Linux_x86-deb/RPMS
wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_deb.tar.gz -cO LibreOffice_7_deb.tar.gz && tar -zxf /tmp/LibreOffice_7_deb.tar.gz && cd /tmp/LibreOffice_7.1.4.2_Linux_x86-64_deb/DEBS
echo $?
if [ $? -eq 0 ];then
apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1

View File

@@ -1,2 +1,36 @@
#!/bin/bash
kill -15 `ps -ef|grep kkFileView|awk 'NR==1{print $2}'`
#
#
#############################
# Author: sanxi
# Version: 1.0
# Date: 2021/09/17
# Description: v1.0修改kkFileView关闭进程机制
#############################
#
KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")" || exit 1 ;pwd)
PID_FILE_NAME="kkFileView.pid"
PID_FILE="${KKFILEVIEW_BIN_FOLDER}/${PID_FILE_NAME}"
export KKFILEVIEW_BIN_FOLDER=$KKFILEVIEW_BIN_FOLDER
#
## pid文件是否存在
if [ ! -e "$PID_FILE" ]; then
echo "kkFileView.pid文件不存在"
exit 1
else
## 文件不为空代表程序正在运行则循环关闭进程
if [ -s "$PID_FILE" ]; then
# 读取pid文件内容开启while循环读取每一行文本赋予给变量PID_FILE
cat "${PID_FILE}" | while read PID;do
## 如已读取完毕则退出脚本
[ -z "$PID" ] && exit 2
echo "正在停止进程:${PID}..."
## 正常停止进程
kill -15 "${PID}" && echo "进程:${PID}停止成功!"
done
# 关闭所有进程后重置pid
cat /dev/null > "$PID_FILE"
else
echo "kkFileView进程尚未运行"
fi
fi

View File

@@ -6,4 +6,4 @@ echo Starting kkFileView...
echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official homesite: https://kkFileView.keking.cn
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.0.0.jar -> ..\log\kkFileView.log
java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.1.0-SNAPSHOT.jar -> ..\log\kkFileView.log

View File

@@ -1,32 +1,61 @@
#!/bin/bash
#
#
#############################
# First_Author: 凯京科技
# Second_Author: sanxi
# Version: 1.1
# Date: 2021/09/17
# Description: v1.1修改进程启动机制为pid形式
#############################
#
DIR_HOME=("/opt/openoffice.org3" "/opt/libreoffice" "/opt/libreoffice6.1" "/opt/libreoffice7.0" "/opt/libreoffice7.1" "/opt/openoffice4" "/usr/lib/openoffice" "/usr/lib/libreoffice")
FLAG=
OFFICE_HOME=
KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")";pwd)
KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")" || exit 1 ;pwd)
PID_FILE_NAME="kkFileView.pid"
PID_FILE="${KKFILEVIEW_BIN_FOLDER}/${PID_FILE_NAME}"
export KKFILEVIEW_BIN_FOLDER=$KKFILEVIEW_BIN_FOLDER
cd $KKFILEVIEW_BIN_FOLDER
echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER"
grep 'office\.home' ../config/application.properties | grep '!^#'
if [ $? -eq 0 ]; then
echo "Using customized office.home"
else
for i in ${DIR_HOME[@]}
do
if [ -f $i"/program/soffice.bin" ]; then
FLAG=true
OFFICE_HOME=${i}
break
fi
done
if [ ! -n "${FLAG}" ]; then
echo "Installing OpenOffice"
sh ./install.sh
else
echo "Detected office component has been installed in $OFFICE_HOME"
fi
#
## 如pid文件不存在则自动创建
if [ ! -f ${PID_FILE_NAME} ]; then
touch "${KKFILEVIEW_BIN_FOLDER}/${PID_FILE_NAME}"
fi
## 判断当前是否有进程处于运行状态
if [ -s "${PID_FILE}" ]; then
PID=$(cat "${PID_FILE}")
echo "进程已处于运行状态,进程号为:${PID}"
exit 1
else
cd "$KKFILEVIEW_BIN_FOLDER" || exit 1
echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER"
grep 'office\.home' ../config/application.properties | grep '!^#'
if [ $? -eq 0 ]; then
echo "Using customized office.home"
else
for i in ${DIR_HOME[@]}
do
if [ -f "$i/program/soffice.bin" ]; then
FLAG=true
OFFICE_HOME=${i}
break
fi
done
if [ ! -n "${FLAG}" ]; then
echo "Installing OpenOffice"
sh ./install.sh
else
echo "Detected office component has been installed in $OFFICE_HOME"
fi
fi
## 启动kkFileView
echo "Starting kkFileView..."
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.1.0-SNAPSHOT.jar > ../log/kkFileView.log 2>&1 &
echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn"
echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
PROCESS=$(ps -ef | grep kkFileView | awk 'NR==1{print $2}')
# 启动成功后将进程号写入pid文件
echo "$PROCESS" > "$PID_FILE"
fi
echo "Starting kkFileView..."
echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn"
echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.0.0.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -22,9 +22,15 @@ office.plugin.server.ports = 2001,2002
## office 转换服务 task 超时时间默认五分钟
office.plugin.task.timeout = 5m
#文件资源路径默认为打包根路径下的file目录下
#预览生成资源路径默认为打包根路径下的file目录下
#file.dir = D:\\kkFileview\\
file.dir = ${KK_FILE_DIR:default}
#允许预览的本地文件夹 默认不允许任何本地文件被预览
#file.dir = D:\\kkFileview\\
local.preview.dir = ${KK_LOCAL_PREVIEW_DIR:default}
#openoffice home路径
#office.home = C:\\Program Files (x86)\\OpenOffice 4
office.home = ${KK_OFFICE_HOME:default}
@@ -65,8 +71,16 @@ office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#是否关闭office预览切换开关默认为false可配置为true关闭
office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}
#是否禁止演示模式
pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true}
#是否禁止打开文件
pdf.openFile.disable = ${KK_PDF_OPEN_FILE_DISABLE:true}
#是否禁止打印转换生成的pdf文件
pdf.print.disable = ${KK_PDF_PRINT_DISABLE:true}
#是否禁止下载转换生成的pdf文件
pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
#是否禁止bookmark
pdf.bookmark.disable = ${KK_PDF_BOOKMARK_DISABLE:true}
#是否禁用首页文件上传
file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false}
@@ -100,3 +114,6 @@ watermark.height = ${WATERMARK_HEIGHT:80}
#水印倾斜度数要求设置在大于等于0小于90
watermark.angle = ${WATERMARK_ANGLE:10}
#Tif类型图片浏览模式tif利用前端js插件浏览jpg转换为jpg后前端显示pdf转换为pdf后显示便于打印
tif.preview.type = ${KK_TIF_PREVIEW_TYPE:tif}

View File

@@ -20,8 +20,8 @@ public class AppBanner implements Banner {
" | < | < | | | | | | | __/ \\ / | | | __/ \\ V V / \n" +
" |_|\\_\\ |_|\\_\\ |_| |_| |_| \\___| \\/ |_| \\___| \\_/\\_/ \n" +
" \n" +
" => Spring Boot :: (v2.4.2) QQ1 :: 613025121\n" +
" => kkFileView :: (v4.0.0) QQ2 :: 484680571\n" +
" => Spring Boot :: (v2.4.2) QQ1 :: 613025121\n" +
" => kkFileView :: (v4.1.0-SNAPSHOT) QQ2 :: 484680571\n" +
" => github :: https://github.com/kekingcn/kkFileView\n" +
" => gitee :: https://gitee.com/kekingcn/file-online-preview\n");
}

View File

@@ -33,9 +33,15 @@ public class ConfigConstants {
private static String ftpControlEncoding;
private static String baseUrl;
private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
private static String localPreviewDir;
private static CopyOnWriteArraySet<String> trustHostSet;
private static String pdfPresentationModeDisable;
private static String pdfOpenFileDisable;
private static String pdfPrintDisable;
private static String pdfDownloadDisable;
private static String pdfBookmarkDisable;
private static Boolean fileUploadDisable;
private static String tifPreviewType;
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
@@ -47,10 +53,15 @@ public class ConfigConstants {
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_BASE_URL = "default";
public static final String DEFAULT_FILE_DIR_VALUE = "default";
public static final String DEFAULT_LOCAL_PREVIEW_DIR_VALUE = "default";
public static final String DEFAULT_TRUST_HOST = "default";
public static final String DEFAULT_PDF_PRESENTATION_MODE_DISABLE = "true";
public static final String DEFAULT_PDF_OPEN_FILE_DISABLE = "true";
public static final String DEFAULT_PDF_PRINT_DISABLE = "true";
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
public static final String DEFAULT_PDF_BOOKMARK_DISABLE = "true";
public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false";
public static final String DEFAULT_TIF_PREVIEW_TYPE = "tif";
public static Boolean isCacheEnabled() {
return cacheEnabled;
@@ -203,6 +214,24 @@ public class ConfigConstants {
}
}
public static String getLocalPreviewDir() {
return localPreviewDir;
}
@Value("${local.preview.dir:default}")
public void setLocalPreviewDir(String localPreviewDir) {
setLocalPreviewDirValue(localPreviewDir);
}
public static void setLocalPreviewDirValue(String localPreviewDir) {
if (!DEFAULT_LOCAL_PREVIEW_DIR_VALUE.equals(localPreviewDir)) {
if (!localPreviewDir.endsWith(File.separator)) {
localPreviewDir = localPreviewDir + File.separator;
}
}
ConfigConstants.localPreviewDir = localPreviewDir;
}
@Value("${trust.host:default}")
public void setTrustHost(String trustHost) {
setTrustHostValue(trustHost);
@@ -228,11 +257,46 @@ public class ConfigConstants {
ConfigConstants.trustHostSet = trustHostSet;
}
public static String getPdfPresentationModeDisable() {
return pdfPresentationModeDisable;
}
@Value("${pdf.presentationMode.disable:true}")
public void setPdfPresentationModeDisable(String pdfPresentationModeDisable) {
setPdfPresentationModeDisableValue(pdfPresentationModeDisable);
}
public static void setPdfPresentationModeDisableValue(String pdfPresentationModeDisable) {
ConfigConstants.pdfPresentationModeDisable = pdfPresentationModeDisable;
}
public static String getPdfOpenFileDisable() {
return pdfOpenFileDisable;
}
@Value("${pdf.openFile.disable:true}")
public static void setPdfOpenFileDisable(String pdfOpenFileDisable) {
setPdfOpenFileDisableValue(pdfOpenFileDisable);
}
public static void setPdfOpenFileDisableValue(String pdfOpenFileDisable) {
ConfigConstants.pdfOpenFileDisable = pdfOpenFileDisable;
}
public static String getPdfPrintDisable() {
return pdfPrintDisable;
}
@Value("${pdf.print.disable:true}")
public void setPdfPrintDisable(String pdfPrintDisable) {
setPdfPrintDisableValue(pdfPrintDisable);
}
public static void setPdfPrintDisableValue(String pdfPrintDisable) {
ConfigConstants.pdfPrintDisable = pdfPrintDisable;
}
public static String getPdfDownloadDisable() {
return pdfDownloadDisable;
}
@Value("${pdf.download.disable:true}")
public void setPdfDownloadDisable(String pdfDownloadDisable) {
setPdfDownloadDisableValue(pdfDownloadDisable);
@@ -241,6 +305,17 @@ public class ConfigConstants {
ConfigConstants.pdfDownloadDisable = pdfDownloadDisable;
}
public static String getPdfBookmarkDisable() {
return pdfBookmarkDisable;
}
@Value("${pdf.bookmark.disable:true}")
public void setPdfBookmarkDisable(String pdfBookmarkDisable) {
setPdfBookmarkDisableValue(pdfBookmarkDisable);
}
public static void setPdfBookmarkDisableValue(String pdfBookmarkDisable) {
ConfigConstants.pdfBookmarkDisable = pdfBookmarkDisable;
}
public static String getOfficePreviewSwitchDisabled() {
return officePreviewSwitchDisabled;
}
@@ -264,4 +339,18 @@ public class ConfigConstants {
public static void setFileUploadDisableValue(Boolean fileUploadDisable) {
ConfigConstants.fileUploadDisable = fileUploadDisable;
}
public static String getTifPreviewType() {
return tifPreviewType;
}
@Value("${tif.preview.type:tif}")
public void setTifPreviewType(String tifPreviewType) {
setTifPreviewTypeValue(tifPreviewType);
}
public static void setTifPreviewTypeValue(String tifPreviewType) {
ConfigConstants.tifPreviewType = tifPreviewType;
}
}

View File

@@ -47,8 +47,14 @@ public class ConfigRefreshComponent {
String configFilePath = ConfigUtils.getCustomizedConfigPath();
String baseUrl;
String trustHost;
String pdfPresentationModeDisable;
String pdfOpenFileDisable;
String pdfPrintDisable;
String pdfDownloadDisable;
String pdfBookmarkDisable;
boolean fileUploadDisable;
String tifPreviewType;
while (true) {
FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
@@ -66,8 +72,14 @@ public class ConfigRefreshComponent {
mediaArray = media.split(",");
baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_BASE_URL);
trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_TRUST_HOST);
pdfPresentationModeDisable = properties.getProperty("pdf.presentationMode.disable", ConfigConstants.DEFAULT_PDF_PRESENTATION_MODE_DISABLE);
pdfOpenFileDisable = properties.getProperty("pdf.openFile.disable", ConfigConstants.DEFAULT_PDF_OPEN_FILE_DISABLE);
pdfPrintDisable = properties.getProperty("pdf.print.disable", ConfigConstants.DEFAULT_PDF_PRINT_DISABLE);
pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE);
pdfBookmarkDisable = properties.getProperty("pdf.bookmark.disable", ConfigConstants.DEFAULT_PDF_BOOKMARK_DISABLE);
fileUploadDisable = Boolean.parseBoolean(properties.getProperty("file.upload.disable", ConfigConstants.DEFAULT_FILE_UPLOAD_DISABLE));
tifPreviewType = properties.getProperty("tif.preview.type", ConfigConstants.DEFAULT_TIF_PREVIEW_TYPE);
ConfigConstants.setCacheEnabledValueValue(cacheEnabled);
ConfigConstants.setSimTextValue(textArray);
ConfigConstants.setMediaValue(mediaArray);
@@ -78,8 +90,13 @@ public class ConfigRefreshComponent {
ConfigConstants.setBaseUrlValue(baseUrl);
ConfigConstants.setTrustHostValue(trustHost);
ConfigConstants.setOfficePreviewSwitchDisabledValue(officePreviewSwitchDisabled);
ConfigConstants.setPdfPresentationModeDisableValue(pdfPresentationModeDisable);
ConfigConstants.setPdfOpenFileDisableValue(pdfOpenFileDisable);
ConfigConstants.setPdfPrintDisableValue(pdfPrintDisable);
ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable);
ConfigConstants.setPdfBookmarkDisableValue(pdfBookmarkDisable);
ConfigConstants.setFileUploadDisableValue(fileUploadDisable);
ConfigConstants.setTifPreviewTypeValue(tifPreviewType);
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();

View File

@@ -1,11 +1,17 @@
package cn.keking.config;
import cn.keking.web.filter.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.HashSet;
import java.util.Set;
/**
* @author: chenjh
* @since: 2019/4/16 20:04
@@ -23,4 +29,63 @@ public class WebConfig implements WebMvcConfigurer {
LOGGER.info("Add resource locations: {}", filePath);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/","file:" + filePath);
}
@Bean
public FilterRegistrationBean<ChinesePathFilter> getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean<ChinesePathFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
return registrationBean;
}
@Bean
public FilterRegistrationBean<TrustHostFilter> getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean<TrustHostFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean<TrustDirFilter> getTrustDirFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
TrustDirFilter filter = new TrustDirFilter();
FilterRegistrationBean<TrustDirFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean<BaseUrlFilter> getBaseUrlFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
BaseUrlFilter filter = new BaseUrlFilter();
FilterRegistrationBean<BaseUrlFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean<AttributeSetFilter> getWatermarkConfigFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
AttributeSetFilter filter = new AttributeSetFilter();
FilterRegistrationBean<AttributeSetFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
}

View File

@@ -14,6 +14,8 @@ public class FileAttribute {
private String url;
private String fileKey;
private String officePreviewType = ConfigConstants.getOfficePreviewType();
private String tifPreviewType;
private Boolean skipDownLoad = false;
public FileAttribute() {
}
@@ -80,4 +82,18 @@ public class FileAttribute {
public void setUrl(String url) {
this.url = url;
}
public Boolean getSkipDownLoad() {
return skipDownLoad;
}
public void setSkipDownLoad(Boolean skipDownLoad) {
this.skipDownLoad = skipDownLoad;
}
public String getTifPreviewType() {
return tifPreviewType;
}
public void setTifPreviewType(String previewType) {
this.tifPreviewType = previewType;
}
}

View File

@@ -32,6 +32,7 @@ public enum FileType {
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
private static final String[] TIFF_TYPES = {"tif", "tiff"};
private static final String[] OFD_TYPES = {"ofd"};
private static final String[] CAD_TYPES = {"dwg", "dxf"};
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
private static final String[] CODES = {"java", "c", "php", "go", "python", "py", "js", "html", "ftl", "css", "lua", "sh", "rb", "yaml", "yml", "json", "h", "cpp", "cs", "aspx", "jsp"};
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
@@ -66,10 +67,12 @@ public enum FileType {
for (String ofd : OFD_TYPES) {
FILE_TYPE_MAPPER.put(ofd, FileType.OFD);
}
for (String cad : CAD_TYPES) {
FILE_TYPE_MAPPER.put(cad, FileType.CAD);
}
FILE_TYPE_MAPPER.put("md", FileType.MARKDOWN);
FILE_TYPE_MAPPER.put("xml", FileType.XML);
FILE_TYPE_MAPPER.put("pdf", FileType.PDF);
FILE_TYPE_MAPPER.put("dwg", FileType.CAD);
FILE_TYPE_MAPPER.put("flv", FileType.FLV);
}

View File

@@ -2,6 +2,7 @@ package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType;
import cn.keking.utils.FileHeaderRar;
import cn.keking.utils.KkFileUtils;
import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -9,10 +10,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.junrar.Archive;
import com.github.junrar.exception.RarException;
import com.github.junrar.rarfile.FileHeader;
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
import java.io.*;
@@ -24,6 +29,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @author yudian-it
@@ -99,44 +105,84 @@ public class CompressFileReader {
List<String> imgUrls = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl();
try {
Archive archive = new Archive(new FileInputStream(filePath));
List<FileHeader> headers = archive.getFileHeaders();
headers = sortedHeaders(headers);
List<FileHeaderRar> items = getRar4Paths(filePath);
String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
List<Map<String, FileHeader>> headersToBeExtracted = new ArrayList<>();
for (FileHeader header : headers) {
String fullName;
if (header.isUnicode()) {
fullName = header.getFileNameW();
} else {
fullName = header.getFileNameString();
}
// 展示名
String originName = getLastFileName(fullName, "\\");
List<Map<String, FileHeaderRar>> headersToBeExtract = new ArrayList<>();
for (FileHeaderRar header : items) {
String fullName = header.getFileNameW();
String originName = getLastFileName(fullName, File.separator);
String childName = originName;
boolean directory = header.isDirectory();
boolean directory = header.getDirectory();
if (!directory) {
childName = archiveFileName + "_" + originName;
headersToBeExtracted.add(Collections.singletonMap(childName, header));
headersToBeExtract.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
String parentName = getLast2FileName(fullName, File.separator, archiveFileName);
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.PICTURE)) {//添加图片文件到图片列表
if (type.equals(FileType.PICTURE)) {
imgUrls.add(baseUrl + childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
FileNode node =
new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
fileHandlerService.putImgCache(fileKey, imgUrls);
executors.submit(new RarExtractorWorker(headersToBeExtract, filePath));
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (RarException | IOException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public List<FileHeaderRar> getRar4Paths(String paths) {
RandomAccessFile randomAccessFile = null;
IInArchive inArchive = null;
List<FileHeaderRar> itemPath = null;
try {
randomAccessFile = new RandomAccessFile(paths, "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
String folderName = paths.substring(paths.lastIndexOf(File.separator) + 1);
String extractPath = paths.substring(0, paths.lastIndexOf(folderName));
inArchive.extract(null, false, new ExtractCallback(inArchive, extractPath, folderName + "_"));
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
itemPath =
Arrays.stream(simpleInArchive.getArchiveItems())
.map(
o -> {
try {
return new FileHeaderRar(o.getPath(), o.isFolder());
} catch (SevenZipException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList())
.stream()
.sorted(Comparator.comparing(FileHeaderRar::getFileNameW))
.collect(Collectors.toList());
} catch (Exception e) {
System.err.println("Error occurs: " + e);
} finally {
if (inArchive != null) {
try {
inArchive.close();
} catch (SevenZipException e) {
System.err.println("Error closing archive: " + e);
}
}
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
System.err.println("Error closing file: " + e);
}
}
}
return itemPath;
}
public String read7zFile(String filePath, String fileKey) {
String archiveSeparator = "/";
Map<String, FileNode> appender = new HashMap<>();
@@ -206,10 +252,21 @@ public class CompressFileReader {
private List<FileHeader> sortedHeaders(List<FileHeader> headers) {
List<FileHeader> sortedHeaders = new ArrayList<>();
Map<Integer, FileHeader> mapHeaders = new TreeMap<>();
headers.forEach(header -> mapHeaders.put(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length(), header));
headers.forEach(
header ->
mapHeaders.put(
new Integer(0).equals(header.getFileNameW().length())
? header.getFileNameString().length()
: header.getFileNameW().length(),
header));
for (Map.Entry<Integer, FileHeader> entry : mapHeaders.entrySet()) {
for (FileHeader header : headers) {
if (entry.getKey().equals(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length())) {
if (entry
.getKey()
.equals(
new Integer(0).equals(header.getFileNameW().length())
? header.getFileNameString().length()
: header.getFileNameW().length())) {
sortedHeaders.add(header);
}
}
@@ -445,16 +502,29 @@ public class CompressFileReader {
class RarExtractorWorker implements Runnable {
private final List<Map<String, FileHeader>> headersToBeExtracted;
private final List<Map<String, FileHeaderRar>> headersToBeExtract;
private final Archive archive;
/**
* 用以删除源文件
*/
private final String filePath;
public RarExtractorWorker(List<Map<String, FileHeader>> headersToBeExtracted, Archive archive, String filePath) {
public RarExtractorWorker(
List<Map<String, FileHeader>> headersToBeExtracted, Archive archive, String filePath) {
this.headersToBeExtracted = headersToBeExtracted;
this.archive = archive;
this.filePath = filePath;
headersToBeExtract = null;
}
public RarExtractorWorker(
List<Map<String, FileHeaderRar>> headersToBeExtract, String filePath) {
this.headersToBeExtract = headersToBeExtract;
this.filePath = filePath;
archive = null;
headersToBeExtracted = null;
}
@Override
@@ -480,4 +550,74 @@ public class CompressFileReader {
}
}
}
private static class ExtractCallback implements IArchiveExtractCallback {
private final IInArchive inArchive;
private final String extractPath;
private final String folderName;
public ExtractCallback(IInArchive inArchive, String extractPath, String folderName) {
this.inArchive = inArchive;
if (!extractPath.endsWith("/") && !extractPath.endsWith("\\")) {
extractPath += File.separator;
}
this.extractPath = extractPath;
this.folderName = folderName;
}
@Override
public void setTotal(long total) {
}
@Override
public void setCompleted(long complete) {
}
@Override
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
String filePath = inArchive.getStringProperty(index, PropID.PATH);
String real = folderName + filePath.substring(filePath.lastIndexOf(File.separator) + 1);
File f = new File(extractPath + real);
f.delete();
return data -> {
FileOutputStream fos = null;
try {
File path = new File(extractPath + real);
if (!path.getParentFile().exists()) {
path.getParentFile().mkdirs();
}
if (!path.exists()) {
path.createNewFile();
}
fos = new FileOutputStream(path, true);
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.flush();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return data.length;
};
}
@Override
public void prepareOperation(ExtractAskMode extractAskMode) {
}
@Override
public void setOperationResult(ExtractOperationResult extractOperationResult) {
}
}
}

View File

@@ -270,9 +270,13 @@ public class FileHandlerService {
type = FileType.typeFromUrl(url);
suffix = WebUtils.suffixFromUrl(url);
}
if (url.contains("?fileKey=")) {
attribute.setSkipDownLoad(true);
}
attribute.setType(type);
attribute.setName(fileName);
attribute.setSuffix(suffix);
url = WebUtils.encodeUrlFileName(url);
attribute.setUrl(url);
if (req != null) {
String officePreviewType = req.getParameter("officePreviewType");
@@ -283,6 +287,11 @@ public class FileHandlerService {
if (StringUtils.hasText(fileKey)) {
attribute.setFileKey(fileKey);
}
String tifPreviewType = req.getParameter("tifPreviewType");
if (StringUtils.hasText(tifPreviewType)) {
attribute.setTifPreviewType(tifPreviewType);
}
}
return attribute;
}

View File

@@ -40,16 +40,7 @@ public class CompressFilePreviewImpl implements FilePreview {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
fileTree = compressFileReader.readZipFile(filePath, fileName);
} else if ("rar".equalsIgnoreCase(suffix)) {
fileTree = compressFileReader.unRar(filePath, fileName);
} else if ("7z".equalsIgnoreCase(suffix)) {
fileTree = compressFileReader.read7zFile(filePath, fileName);
}
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
fileHandlerService.addConvertedFile(fileName, fileTree);
}
fileTree = compressFileReader.unRar(filePath, fileName);
} else {
fileTree = fileHandlerService.getConvertedFile(fileName);
}
@@ -57,7 +48,7 @@ public class CompressFilePreviewImpl implements FilePreview {
model.addAttribute("fileTree", fileTree);
return COMPRESS_FILE_PREVIEW_PAGE;
} else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式");
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件类型不受支持");
}
}
}

View File

@@ -1,12 +1,21 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.ConvertPicUtil;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.WebUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* tiff 图片文件处理
* @author kl (http://kailing.pub)
@@ -20,19 +29,85 @@ public class TiffFilePreviewImpl implements FilePreview {
//默认初始化 50MB 内存
private static final long INITIALIZE_MEMORY_SIZE_VALUE_DEFAULT = 1024L * 1024 * 50;
private final String fileDir = ConfigConstants.getFileDir();
public TiffFilePreviewImpl(PictureFilePreviewImpl pictureFilePreview) {
this.pictureFilePreview = pictureFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
pictureFilePreview.filePreviewHandle(url,model,fileAttribute);
String fileSize = WebUtils.getUrlParameterReg(url,INITIALIZE_MEMORY_SIZE);
if(StringUtils.hasText(fileSize)){
model.addAttribute(INITIALIZE_MEMORY_SIZE,fileSize);
}else {
model.addAttribute(INITIALIZE_MEMORY_SIZE,Long.toString(INITIALIZE_MEMORY_SIZE_VALUE_DEFAULT));
}
return TIFF_FILE_PREVIEW_PAGE;
String tifPreviewType = ConfigConstants.getTifPreviewType();
String tifOnLinePreviewType = fileAttribute.getTifPreviewType();
if (StringUtils.hasText(tifOnLinePreviewType)) {
tifPreviewType = tifOnLinePreviewType;
}
if("tif".equalsIgnoreCase(tifPreviewType)){
pictureFilePreview.filePreviewHandle(url,model,fileAttribute);
String fileSize = WebUtils.getUrlParameterReg(url,INITIALIZE_MEMORY_SIZE);
if(StringUtils.hasText(fileSize)){
model.addAttribute(INITIALIZE_MEMORY_SIZE,fileSize);
}else {
model.addAttribute(INITIALIZE_MEMORY_SIZE,Long.toString(INITIALIZE_MEMORY_SIZE_VALUE_DEFAULT));
}
return TIFF_FILE_PREVIEW_PAGE;
}else if("jpg".equalsIgnoreCase(tifPreviewType) || "pdf".equalsIgnoreCase(tifPreviewType)){
String inputFileName = url.substring(url.lastIndexOf("/") + 1);
String inputFileNamePrefix = inputFileName.substring(0, inputFileName.lastIndexOf("."));
String strLocalTif = fileDir + inputFileName;
File fileTiff = new File(strLocalTif);
// 如果本地不存在这个tif文件则下载
if(!fileTiff.exists()){
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, inputFileName);
if (response.isFailure()) {
return NOT_SUPPORTED_FILE_PAGE;
}
}
String baseUrl = BaseUrlFilter.getBaseUrl();
if("pdf".equalsIgnoreCase(tifPreviewType)){
// 以PDF模式预览的过程
File filePdf = new File(fileDir + inputFileNamePrefix + ".pdf");
// 如果本地不存在对应的pdf则调用转换过程。否则直接用现有的pdf文件
if(!filePdf.exists()){
filePdf = ConvertPicUtil.convertTif2Pdf(strLocalTif, fileDir + inputFileNamePrefix + ".pdf");
}
// 如果pdf已经存在则将url路径加入到对象中返回给页面
if(filePdf.exists()){
String pdfUrl = baseUrl + inputFileNamePrefix + ".pdf";
model.addAttribute("pdfUrl", pdfUrl);
return PDF_FILE_PREVIEW_PAGE;
}
}else{
// 以JPG模式预览的过程
String strJpgFilePathName = fileDir + inputFileNamePrefix + ".jpg";
// 将tif转换为jpg返回转换后的文件路径、文件名的list
List<String> listPic2Jpg = ConvertPicUtil.convertTif2Jpg(strLocalTif, strJpgFilePathName);
// 将返回页面的图片url的list对象
List<String> listImageUrls = new ArrayList<>();
// 循环拼装url的list对象
for(String strJpg : listPic2Jpg){
listImageUrls.add(baseUrl + strJpg);
}
model.addAttribute("imgUrls", listImageUrls);
model.addAttribute("currentUrl", listImageUrls.get(0));
}
// 转换后的tif没用了可以删掉了
if(fileTiff.exists()){
fileTiff.delete();
}
return PICTURE_FILE_PREVIEW_PAGE;
}
return NOT_SUPPORTED_FILE_PAGE;
}
}

View File

@@ -0,0 +1,237 @@
package cn.keking.utils;
import com.lowagie.text.Document;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
import com.sun.media.jai.codec.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ConvertPicUtil {
private final static Logger logger = LoggerFactory.getLogger(ConvertPicUtil.class);
/**
* Tif 转 JPG。
* @param strInputFile 输入文件的路径和文件名
* @param strOutputFile 输出文件的路径和文件名
* @return boolean 是否转换成功
*/
public static List<String> convertTif2Jpg(String strInputFile, String strOutputFile) {
List<String> listImageFiles = new ArrayList<>();
if (strInputFile == null || "".equals(strInputFile.trim())) {
return null;
}
if (!new File(strInputFile).exists()) {
logger.info("找不到文件【" + strInputFile + "");
return null;
}
strInputFile = strInputFile.replaceAll("\\\\", "/");
strOutputFile = strOutputFile.replaceAll("\\\\", "/");
FileSeekableStream fileSeekStream = null;
try {
JPEGEncodeParam jpegEncodeParam = new JPEGEncodeParam();
TIFFEncodeParam tiffEncodeParam = new TIFFEncodeParam();
tiffEncodeParam.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
tiffEncodeParam.setLittleEndian(false);
String strFilePrefix = strInputFile.substring(strInputFile.lastIndexOf("/") + 1, strInputFile.lastIndexOf("."));
fileSeekStream = new FileSeekableStream(strInputFile);
ImageDecoder imageDecoder = ImageCodec.createImageDecoder("TIFF", fileSeekStream, null);
int intTifCount = imageDecoder.getNumPages();
logger.info("该tif文件共有【" + intTifCount + "】页");
String strJpgPath = "";
String strJpgUrl = "";
if (intTifCount == 1) {
// 如果是单页tif文件则转换的目标文件夹就在指定的位置
strJpgPath = strOutputFile.substring(0, strOutputFile.lastIndexOf("/"));
} else {
// 如果是多页tif文件则在目标文件夹下按照文件名再创建子目录将转换后的文件放入此新建的子目录中
strJpgPath = strOutputFile.substring(0, strOutputFile.lastIndexOf("."));
}
// 处理目标文件夹,如果不存在则自动创建
File fileJpgPath = new File(strJpgPath);
if (!fileJpgPath.exists()) {
fileJpgPath.mkdirs();
}
// 循环处理每页tif文件转换为jpg
for (int i = 0; i < intTifCount; i++) {
String strJpg = "";
if(intTifCount == 1){
strJpg = strJpgPath + "/" + strFilePrefix + ".jpg";
strJpgUrl = strFilePrefix + ".jpg";
}else{
strJpg = strJpgPath + "/" + i + ".jpg";
strJpgUrl = strFilePrefix + "/" + i + ".jpg";
}
File fileJpg = new File(strJpg);
// 如果文件不存在,则生成
if(!fileJpg.exists()){
RenderedImage renderedImage = imageDecoder.decodeAsRenderedImage(i);
ParameterBlock pb = new ParameterBlock();
pb.addSource(renderedImage);
pb.add(fileJpg.toString());
pb.add("JPEG");
pb.add(jpegEncodeParam);
RenderedOp renderedOp = JAI.create("filestore", pb);
renderedOp.dispose();
logger.info("每页分别保存至: " + fileJpg.getCanonicalPath());
}else{
logger.info("JPG文件已存在 " + fileJpg.getCanonicalPath());
}
listImageFiles.add(strJpgUrl);
}
return listImageFiles;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (fileSeekStream != null) {
try {
fileSeekStream.close();
} catch (IOException e) {
}
fileSeekStream = null;
}
}
}
/**
* 将Jpg图片转换为Pdf文件
*
* @param strJpgFile 输入的jpg的路径和文件名
* @param strPdfFile 输出的pdf的路径和文件名
* @return
*/
public static File convertJpg2Pdf(String strJpgFile, String strPdfFile) {
Document document = new Document();
// 设置文档页边距
document.setMargins(0, 0, 0, 0);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(strPdfFile);
PdfWriter.getInstance(document, fos);
// 打开文档
document.open();
// 获取图片的宽高
Image image = Image.getInstance(strJpgFile);
float floatImageHeight = image.getScaledHeight();
float floatImageWidth = image.getScaledWidth();
// 设置页面宽高与图片一致
Rectangle rectangle = new Rectangle(floatImageWidth, floatImageHeight);
document.setPageSize(rectangle);
// 图片居中
image.setAlignment(Image.ALIGN_CENTER);
//新建一页添加图片
document.newPage();
document.add(image);
} catch (Exception ioe) {
ioe.printStackTrace();
return null;
} finally {
//关闭文档
document.close();
try {
fos.flush();
fos.close();
File filePDF = new File(strPdfFile);
return filePDF;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 将Tif图片转换为Pdf文件支持多页Tif
*
* @param strTifFile 输入的tif的路径和文件名
* @param strPdfFile 输出的pdf的路径和文件名
* @return
*/
public static File convertTif2Pdf(String strTifFile, String strPdfFile) {
try {
RandomAccessFileOrArray rafa = new RandomAccessFileOrArray(strTifFile);
Document document = new Document();
// 设置文档页边距
document.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(document, new FileOutputStream(strPdfFile));
document.open();
int intPages = TiffImage.getNumberOfPages(rafa);
Image image;
File filePDF;
if(intPages == 1){
String strJpg = strTifFile.substring(0, strTifFile.lastIndexOf(".")) + ".jpg";
File fileJpg = new File(strJpg);
List<String> listPic2Jpg = convertTif2Jpg(strTifFile, strJpg);
if(listPic2Jpg != null && fileJpg.exists()){
filePDF = convertJpg2Pdf(strJpg, strPdfFile);
}
}else{
for (int i = 1; i <= intPages; i++) {
image = TiffImage.getTiffImage(rafa, i);
// 设置页面宽高与图片一致
Rectangle pageSize = new Rectangle(image.getScaledWidth(), image.getScaledHeight());
document.setPageSize(pageSize);
// 图片居中
image.setAlignment(Image.ALIGN_CENTER);
//新建一页添加图片
document.newPage();
document.add(image);
}
document.close();
}
rafa.close();
filePDF = new File(strPdfFile);
return filePDF;
} catch (Exception e) {
System.out.println(e.toString());
}
return null;
}
}

View File

@@ -32,22 +32,24 @@ public class DownloadUtils {
* @return 本地文件绝对路径
*/
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl();
String urlStr = fileAttribute.getUrl().replaceAll("\\+", "%20");
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
String realPath = DownloadUtils.getRelFilePath(fileName, fileAttribute);
try {
URL url = WebUtils.normalizedURL(urlStr);
if (isHttpUrl(url)) {
File realFile = new File(realPath);
FileUtils.copyURLToFile(url,realFile);
} else if (isFtpUrl(url)) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
response.setMsg("url不能识别url" + urlStr);
if (!fileAttribute.getSkipDownLoad()) {
if (isHttpUrl(url)) {
File realFile = new File(realPath);
FileUtils.copyURLToFile(url, realFile);
} else if (isFtpUrl(url)) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
response.setMsg("url不能识别url" + urlStr);
}
}
response.setContent(realPath);
response.setMsg(fileName);

View File

@@ -0,0 +1,28 @@
package cn.keking.utils;
public class FileHeaderRar {
private String fileNameW;
private Boolean isDirectory;
public FileHeaderRar(String fileNameW, Boolean isDirectory) {
this.fileNameW = fileNameW;
this.isDirectory = isDirectory;
}
public String getFileNameW() {
return fileNameW;
}
public void setFileNameW(String fileNameW) {
this.fileNameW = fileNameW;
}
public Boolean getDirectory() {
return isDirectory;
}
public void setDirectory(Boolean directory) {
isDirectory = directory;
}
}

View File

@@ -1,9 +1,15 @@
package cn.keking.utils;
import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Base64Utils;
import javax.servlet.ServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -15,6 +21,7 @@ public class WebUtils {
/**
* 获取标准的URL
*
* @param urlStr url
* @return 标准的URL
*/
@@ -79,6 +86,14 @@ public class WebUtils {
* @return 文件名
*/
public static String getFileNameFromURL(String url) {
if (url.toLowerCase().startsWith("file:")) {
try {
URL urlObj = new URL(url);
url = urlObj.getPath().substring(1);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
@@ -97,4 +112,77 @@ public class WebUtils {
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return KkFileUtils.suffixFromFileName(fileName);
}
/**
* 对url中的文件名进行UTF-8编码
*
* @param url url
* @return 文件名编码后的url
*/
public static String encodeUrlFileName(String url) {
String encodedFileName;
String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
if (fullFileName != null && fullFileName.length() > 0) {
try {
encodedFileName = URLEncoder.encode(fullFileName, "UTF-8");
} catch (UnsupportedEncodingException e) {
return null;
}
String noQueryUrl = url.substring(0, url.indexOf("?"));
String parameterStr = url.substring(url.indexOf("?"));
parameterStr = parameterStr.replaceFirst(fullFileName, encodedFileName);
return noQueryUrl + parameterStr;
}
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
int fileNameStartIndex = noQueryUrl.lastIndexOf('/') + 1;
int fileNameEndIndex = noQueryUrl.lastIndexOf('.');
try {
encodedFileName = URLEncoder.encode(noQueryUrl.substring(fileNameStartIndex, fileNameEndIndex), "UTF-8");
} catch (UnsupportedEncodingException e) {
return null;
}
return url.substring(0, fileNameStartIndex) + encodedFileName + url.substring(fileNameEndIndex);
}
/**
* 从 ServletRequest 获取预览的源 url , 已 base64 解码
*
* @param request 请求 request
* @return url
*/
public static String getSourceUrl(ServletRequest request) {
String url = request.getParameter("url");
String urls = request.getParameter("urls");
String currentUrl = request.getParameter("currentUrl");
String urlPath = request.getParameter("urlPath");
if (StringUtils.isNotBlank(url)) {
return new String(Base64Utils.decodeFromString(url), StandardCharsets.UTF_8);
}
if (StringUtils.isNotBlank(currentUrl)) {
return new String(Base64Utils.decodeFromString(currentUrl), StandardCharsets.UTF_8);
}
if (StringUtils.isNotBlank(urlPath)) {
return new String(Base64Utils.decodeFromString(urlPath), StandardCharsets.UTF_8);
}
if (StringUtils.isNotBlank(urls)) {
urls = new String(Base64Utils.decodeFromString(urls), StandardCharsets.UTF_8);
String[] images = urls.split("\\|");
return images[0];
}
return null;
}
/**
* 获取 url 的 host
* @param urlStr url
* @return host
*/
public static String getHost(String urlStr) {
try {
URL url = new URL(urlStr);
return url.getHost().toLowerCase();
} catch (MalformedURLException ignored) {
}
return null;
}
}

View File

@@ -1,12 +1,11 @@
package cn.keking.web.controller;
import cn.keking.model.FileAttribute;
import cn.keking.service.FileHandlerService;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.service.impl.OtherFilePreviewImpl;
import cn.keking.service.FileHandlerService;
import cn.keking.utils.WebUtils;
import fr.opensagres.xdocreport.core.io.IOUtils;
import io.mola.galimatias.GalimatiasParseException;
@@ -23,7 +22,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -125,4 +125,6 @@ public class OnlinePreviewController {
return "success";
}
}

View File

@@ -30,8 +30,12 @@ public class AttributeSetFilter implements Filter {
* @param request request
*/
private void setFileAttribute(ServletRequest request){
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletRequest httpRequest = (HttpServletRequest) request;
request.setAttribute("pdfPresentationModeDisable", ConfigConstants.getPdfPresentationModeDisable());
request.setAttribute("pdfOpenFileDisable", ConfigConstants.getPdfOpenFileDisable());
request.setAttribute("pdfPrintDisable", ConfigConstants.getPdfPrintDisable());
request.setAttribute("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
request.setAttribute("pdfBookmarkDisable", ConfigConstants.getPdfBookmarkDisable());
request.setAttribute("fileKey", httpRequest.getParameter("fileKey"));
request.setAttribute("switchDisabled", ConfigConstants.getOfficePreviewSwitchDisabled());
request.setAttribute("fileUploadDisable", ConfigConstants.getFileUploadDisable());

View File

@@ -1,65 +0,0 @@
package cn.keking.web.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author yudian-it
* @date 2017/11/30
*/
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
return registrationBean;
}
@Bean
public FilterRegistrationBean getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getBaseUrlFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
BaseUrlFilter filter = new BaseUrlFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getWatermarkConfigFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
AttributeSetFilter filter = new AttributeSetFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
}

View File

@@ -0,0 +1,73 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import cn.keking.utils.WebUtils;
import io.mola.galimatias.GalimatiasParseException;
import org.artofsolving.jodconverter.util.PlatformUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import javax.servlet.*;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
/**
* @author : kl (http://kailing.pub)
* @since : 2022-05-25 17:45
*/
public class TrustDirFilter implements Filter {
private String notTrustDirView;
private final Logger logger = LoggerFactory.getLogger(TrustDirFilter.class);
@Override
public void init(FilterConfig filterConfig) {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustDir.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustDirView = new String(bytes, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = WebUtils.getSourceUrl(request);
if (!allowPreview(url)) {
response.getWriter().write(this.notTrustDirView);
response.getWriter().close();
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
private boolean allowPreview(String urlPath) {
try {
URL url = WebUtils.normalizedURL(urlPath);
if ("file".equals(url.getProtocol().toLowerCase(Locale.ROOT))) {
String filePath = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name());
if (PlatformUtils.isWindows()) {
filePath = filePath.replaceAll("/", "\\\\");
}
return filePath.startsWith(ConfigConstants.getFileDir()) || filePath.startsWith(ConfigConstants.getLocalPreviewDir());
}
return true;
} catch (IOException | GalimatiasParseException e) {
logger.error("解析URL异常url{}", urlPath, e);
return false;
}
}
}

View File

@@ -1,6 +1,7 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import cn.keking.utils.WebUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Base64Utils;
@@ -34,11 +35,8 @@ public class TrustHostFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = getSourceUrl(request);
if(url != null){
url = new String(Base64Utils.decodeFromString(url), StandardCharsets.UTF_8);
}
String host = getHost(url);
String url = WebUtils.getSourceUrl(request);
String host = WebUtils.getHost(url);
if (host != null &&!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) {
String html = this.notTrustHost.replace("${current_host}", host);
response.getWriter().write(html);
@@ -52,28 +50,4 @@ public class TrustHostFilter implements Filter {
}
private String getSourceUrl(ServletRequest request) {
String url = request.getParameter("url");
String currentUrl = request.getParameter("currentUrl");
String urlPath = request.getParameter("urlPath");
if (StringUtils.isNotBlank(url)) {
return url;
}
if (StringUtils.isNotBlank(currentUrl)) {
return currentUrl;
}
if (StringUtils.isNotBlank(urlPath)) {
return urlPath;
}
return null;
}
private String getHost(String urlStr) {
try {
URL url = new URL(urlStr);
return url.getHost().toLowerCase();
} catch (MalformedURLException ignored) {
}
return null;
}
}

View File

@@ -19,8 +19,12 @@ function checkImgs() {
}
function loadImg(el) {
var source = el.getAttribute("data-src");
el.src = source;
var loaded = el.getAttribute("loaded");
if (!Boolean(loaded)) {
var source = el.getAttribute("data-src");
el.setAttribute("loaded", true);
el.src = source;
}
}
// var mustRun = 500
// function throttle(fn, mustRun) {

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -12,14 +12,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-var */
"use strict";
// eslint-disable-next-line no-var
var FontInspector = (function FontInspectorClosure() {
var fonts;
var active = false;
var fontAttribute = "data-font-name";
let fonts;
let active = false;
const fontAttribute = "data-font-name";
function removeSelection() {
const divs = document.querySelectorAll(`span[${fontAttribute}]`);
for (const div of divs) {
@@ -47,10 +47,10 @@ var FontInspector = (function FontInspectorClosure() {
) {
return;
}
var fontName = e.target.dataset.fontName;
var selects = document.getElementsByTagName("input");
for (var i = 0; i < selects.length; ++i) {
var select = selects[i];
const fontName = e.target.dataset.fontName;
const selects = document.getElementsByTagName("input");
for (let i = 0; i < selects.length; ++i) {
const select = selects[i];
if (select.dataset.fontName !== fontName) {
continue;
}
@@ -66,8 +66,8 @@ var FontInspector = (function FontInspectorClosure() {
panel: null,
manager: null,
init: function init(pdfjsLib) {
var panel = this.panel;
var tmp = document.createElement("button");
const panel = this.panel;
const tmp = document.createElement("button");
tmp.addEventListener("click", resetSelection);
tmp.textContent = "Refresh";
panel.appendChild(tmp);
@@ -95,27 +95,27 @@ var FontInspector = (function FontInspectorClosure() {
// FontInspector specific functions.
fontAdded: function fontAdded(fontObj, url) {
function properties(obj, list) {
var moreInfo = document.createElement("table");
for (var i = 0; i < list.length; i++) {
var tr = document.createElement("tr");
var td1 = document.createElement("td");
const moreInfo = document.createElement("table");
for (let i = 0; i < list.length; i++) {
const tr = document.createElement("tr");
const td1 = document.createElement("td");
td1.textContent = list[i];
tr.appendChild(td1);
var td2 = document.createElement("td");
const td2 = document.createElement("td");
td2.textContent = obj[list[i]].toString();
tr.appendChild(td2);
moreInfo.appendChild(tr);
}
return moreInfo;
}
var moreInfo = properties(fontObj, ["name", "type"]);
const moreInfo = properties(fontObj, ["name", "type"]);
const fontName = fontObj.loadedName;
var font = document.createElement("div");
var name = document.createElement("span");
const font = document.createElement("div");
const name = document.createElement("span");
name.textContent = fontName;
var download = document.createElement("a");
const download = document.createElement("a");
if (url) {
url = /url\(['"]?([^\)"']+)/.exec(url);
url = /url\(['"]?([^)"']+)/.exec(url);
download.href = url[1];
} else if (fontObj.data) {
download.href = URL.createObjectURL(
@@ -123,17 +123,17 @@ var FontInspector = (function FontInspectorClosure() {
);
}
download.textContent = "Download";
var logIt = document.createElement("a");
const logIt = document.createElement("a");
logIt.href = "";
logIt.textContent = "Log";
logIt.addEventListener("click", function(event) {
logIt.addEventListener("click", function (event) {
event.preventDefault();
console.log(fontObj);
});
const select = document.createElement("input");
select.setAttribute("type", "checkbox");
select.dataset.fontName = fontName;
select.addEventListener("click", function() {
select.addEventListener("click", function () {
selectFont(fontName, select.checked);
});
font.appendChild(select);
@@ -155,15 +155,17 @@ var FontInspector = (function FontInspectorClosure() {
};
})();
var opMap;
let opMap;
// Manages all the page steppers.
//
// eslint-disable-next-line no-var
var StepperManager = (function StepperManagerClosure() {
var steppers = [];
var stepperDiv = null;
var stepperControls = null;
var stepperChooser = null;
var breakPoints = Object.create(null);
let steppers = [];
let stepperDiv = null;
let stepperControls = null;
let stepperChooser = null;
let breakPoints = Object.create(null);
return {
// Properties/functions needed by PDFBug.
id: "Stepper",
@@ -171,10 +173,10 @@ var StepperManager = (function StepperManagerClosure() {
panel: null,
manager: null,
init: function init(pdfjsLib) {
var self = this;
const self = this;
stepperControls = document.createElement("div");
stepperChooser = document.createElement("select");
stepperChooser.addEventListener("change", function(event) {
stepperChooser.addEventListener("change", function (event) {
self.selectStepper(this.value);
});
stepperControls.appendChild(stepperChooser);
@@ -186,7 +188,7 @@ var StepperManager = (function StepperManagerClosure() {
}
opMap = Object.create(null);
for (var key in pdfjsLib.OPS) {
for (const key in pdfjsLib.OPS) {
opMap[pdfjsLib.OPS[key]] = key;
}
},
@@ -199,17 +201,17 @@ var StepperManager = (function StepperManagerClosure() {
active: false,
// Stepper specific functions.
create: function create(pageIndex) {
var debug = document.createElement("div");
const debug = document.createElement("div");
debug.id = "stepper" + pageIndex;
debug.setAttribute("hidden", true);
debug.hidden = true;
debug.className = "stepper";
stepperDiv.appendChild(debug);
var b = document.createElement("option");
const b = document.createElement("option");
b.textContent = "Page " + (pageIndex + 1);
b.value = pageIndex;
stepperChooser.appendChild(b);
var initBreakPoints = breakPoints[pageIndex] || [];
var stepper = new Stepper(debug, pageIndex, initBreakPoints);
const initBreakPoints = breakPoints[pageIndex] || [];
const stepper = new Stepper(debug, pageIndex, initBreakPoints);
steppers.push(stepper);
if (steppers.length === 1) {
this.selectStepper(pageIndex, false);
@@ -217,22 +219,18 @@ var StepperManager = (function StepperManagerClosure() {
return stepper;
},
selectStepper: function selectStepper(pageIndex, selectPanel) {
var i;
pageIndex = pageIndex | 0;
let i;
pageIndex |= 0;
if (selectPanel) {
this.manager.selectPanel(this);
}
for (i = 0; i < steppers.length; ++i) {
var stepper = steppers[i];
if (stepper.pageIndex === pageIndex) {
stepper.panel.removeAttribute("hidden");
} else {
stepper.panel.setAttribute("hidden", true);
}
const stepper = steppers[i];
stepper.panel.hidden = stepper.pageIndex !== pageIndex;
}
var options = stepperChooser.options;
const options = stepperChooser.options;
for (i = 0; i < options.length; ++i) {
var option = options[i];
const option = options[i];
option.selected = (option.value | 0) === pageIndex;
}
},
@@ -243,11 +241,11 @@ var StepperManager = (function StepperManagerClosure() {
};
})();
// The stepper for each page's IRQueue.
var Stepper = (function StepperClosure() {
// The stepper for each page's operatorList.
const Stepper = (function StepperClosure() {
// Shorter way to create element and optionally set textContent.
function c(tag, textContent) {
var d = document.createElement(tag);
const d = document.createElement(tag);
if (textContent) {
d.textContent = textContent;
}
@@ -256,7 +254,7 @@ var Stepper = (function StepperClosure() {
function simplifyArgs(args) {
if (typeof args === "string") {
var MAX_STRING_LENGTH = 75;
const MAX_STRING_LENGTH = 75;
return args.length <= MAX_STRING_LENGTH
? args
: args.substring(0, MAX_STRING_LENGTH) + "...";
@@ -266,10 +264,9 @@ var Stepper = (function StepperClosure() {
}
if ("length" in args) {
// array
var simpleArgs = [],
i,
ii;
var MAX_ITEMS = 10;
const MAX_ITEMS = 10,
simpleArgs = [];
let i, ii;
for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
simpleArgs.push(simplifyArgs(args[i]));
}
@@ -278,30 +275,32 @@ var Stepper = (function StepperClosure() {
}
return simpleArgs;
}
var simpleObj = {};
for (var key in args) {
const simpleObj = {};
for (const key in args) {
simpleObj[key] = simplifyArgs(args[key]);
}
return simpleObj;
}
function Stepper(panel, pageIndex, initialBreakPoints) {
this.panel = panel;
this.breakPoint = 0;
this.nextBreakPoint = null;
this.pageIndex = pageIndex;
this.breakPoints = initialBreakPoints;
this.currentIdx = -1;
this.operatorListIdx = 0;
}
Stepper.prototype = {
init: function init(operatorList) {
var panel = this.panel;
var content = c("div", "c=continue, s=step");
var table = c("table");
// eslint-disable-next-line no-shadow
class Stepper {
constructor(panel, pageIndex, initialBreakPoints) {
this.panel = panel;
this.breakPoint = 0;
this.nextBreakPoint = null;
this.pageIndex = pageIndex;
this.breakPoints = initialBreakPoints;
this.currentIdx = -1;
this.operatorListIdx = 0;
}
init(operatorList) {
const panel = this.panel;
const content = c("div", "c=continue, s=step");
const table = c("table");
content.appendChild(table);
table.cellSpacing = 0;
var headerRow = c("tr");
const headerRow = c("tr");
table.appendChild(headerRow);
headerRow.appendChild(c("th", "Break"));
headerRow.appendChild(c("th", "Idx"));
@@ -310,12 +309,13 @@ var Stepper = (function StepperClosure() {
panel.appendChild(content);
this.table = table;
this.updateOperatorList(operatorList);
},
updateOperatorList: function updateOperatorList(operatorList) {
var self = this;
}
updateOperatorList(operatorList) {
const self = this;
function cboxOnClick() {
var x = +this.dataset.idx;
const x = +this.dataset.idx;
if (this.checked) {
self.breakPoints.push(x);
} else {
@@ -324,26 +324,26 @@ var Stepper = (function StepperClosure() {
StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
}
var MAX_OPERATORS_COUNT = 15000;
const MAX_OPERATORS_COUNT = 15000;
if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
return;
}
var chunk = document.createDocumentFragment();
var operatorsToDisplay = Math.min(
const chunk = document.createDocumentFragment();
const operatorsToDisplay = Math.min(
MAX_OPERATORS_COUNT,
operatorList.fnArray.length
);
for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
var line = c("tr");
for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) {
const line = c("tr");
line.className = "line";
line.dataset.idx = i;
chunk.appendChild(line);
var checked = this.breakPoints.includes(i);
var args = operatorList.argsArray[i] || [];
const checked = this.breakPoints.includes(i);
const args = operatorList.argsArray[i] || [];
var breakCell = c("td");
var cbox = c("input");
const breakCell = c("td");
const cbox = c("input");
cbox.type = "checkbox";
cbox.className = "points";
cbox.checked = checked;
@@ -353,81 +353,92 @@ var Stepper = (function StepperClosure() {
breakCell.appendChild(cbox);
line.appendChild(breakCell);
line.appendChild(c("td", i.toString()));
var fn = opMap[operatorList.fnArray[i]];
var decArgs = args;
const fn = opMap[operatorList.fnArray[i]];
let decArgs = args;
if (fn === "showText") {
var glyphs = args[0];
var newArgs = [];
var str = [];
for (var j = 0; j < glyphs.length; j++) {
var glyph = glyphs[j];
const glyphs = args[0];
const charCodeRow = c("tr");
const fontCharRow = c("tr");
const unicodeRow = c("tr");
for (let j = 0; j < glyphs.length; j++) {
const glyph = glyphs[j];
if (typeof glyph === "object" && glyph !== null) {
str.push(glyph.fontChar);
charCodeRow.appendChild(c("td", glyph.originalCharCode));
fontCharRow.appendChild(c("td", glyph.fontChar));
unicodeRow.appendChild(c("td", glyph.unicode));
} else {
if (str.length > 0) {
newArgs.push(str.join(""));
str = [];
}
newArgs.push(glyph); // null or number
// null or number
const advanceEl = c("td", glyph);
advanceEl.classList.add("advance");
charCodeRow.appendChild(advanceEl);
fontCharRow.appendChild(c("td"));
unicodeRow.appendChild(c("td"));
}
}
if (str.length > 0) {
newArgs.push(str.join(""));
}
decArgs = [newArgs];
decArgs = c("td");
const table = c("table");
table.classList.add("showText");
decArgs.appendChild(table);
table.appendChild(charCodeRow);
table.appendChild(fontCharRow);
table.appendChild(unicodeRow);
}
line.appendChild(c("td", fn));
line.appendChild(c("td", JSON.stringify(simplifyArgs(decArgs))));
if (decArgs instanceof HTMLElement) {
line.appendChild(decArgs);
} else {
line.appendChild(c("td", JSON.stringify(simplifyArgs(decArgs))));
}
}
if (operatorsToDisplay < operatorList.fnArray.length) {
line = c("tr");
var lastCell = c("td", "...");
const lastCell = c("td", "...");
lastCell.colspan = 4;
chunk.appendChild(lastCell);
}
this.operatorListIdx = operatorList.fnArray.length;
this.table.appendChild(chunk);
},
getNextBreakPoint: function getNextBreakPoint() {
this.breakPoints.sort(function(a, b) {
}
getNextBreakPoint() {
this.breakPoints.sort(function (a, b) {
return a - b;
});
for (var i = 0; i < this.breakPoints.length; i++) {
for (let i = 0; i < this.breakPoints.length; i++) {
if (this.breakPoints[i] > this.currentIdx) {
return this.breakPoints[i];
}
}
return null;
},
breakIt: function breakIt(idx, callback) {
}
breakIt(idx, callback) {
StepperManager.selectStepper(this.pageIndex, true);
var self = this;
var dom = document;
self.currentIdx = idx;
var listener = function(e) {
switch (e.keyCode) {
this.currentIdx = idx;
const listener = evt => {
switch (evt.keyCode) {
case 83: // step
dom.removeEventListener("keydown", listener);
self.nextBreakPoint = self.currentIdx + 1;
self.goTo(-1);
document.removeEventListener("keydown", listener);
this.nextBreakPoint = this.currentIdx + 1;
this.goTo(-1);
callback();
break;
case 67: // continue
dom.removeEventListener("keydown", listener);
var breakPoint = self.getNextBreakPoint();
self.nextBreakPoint = breakPoint;
self.goTo(-1);
document.removeEventListener("keydown", listener);
this.nextBreakPoint = this.getNextBreakPoint();
this.goTo(-1);
callback();
break;
}
};
dom.addEventListener("keydown", listener);
self.goTo(idx);
},
goTo: function goTo(idx) {
var allRows = this.panel.getElementsByClassName("line");
for (var x = 0, xx = allRows.length; x < xx; ++x) {
var row = allRows[x];
document.addEventListener("keydown", listener);
this.goTo(idx);
}
goTo(idx) {
const allRows = this.panel.getElementsByClassName("line");
for (let x = 0, xx = allRows.length; x < xx; ++x) {
const row = allRows[x];
if ((row.dataset.idx | 0) === idx) {
row.style.backgroundColor = "rgb(251,250,207)";
row.scrollIntoView();
@@ -435,20 +446,21 @@ var Stepper = (function StepperClosure() {
row.style.backgroundColor = null;
}
}
},
};
}
}
return Stepper;
})();
// eslint-disable-next-line no-var
var Stats = (function Stats() {
var stats = [];
let stats = [];
function clear(node) {
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
}
function getStatIndex(pageNumber) {
for (var i = 0, ii = stats.length; i < ii; ++i) {
for (let i = 0, ii = stats.length; i < ii; ++i) {
if (stats[i].pageNumber === pageNumber) {
return i;
}
@@ -469,27 +481,27 @@ var Stats = (function Stats() {
if (!stat) {
return;
}
var statsIndex = getStatIndex(pageNumber);
const statsIndex = getStatIndex(pageNumber);
if (statsIndex !== false) {
const b = stats[statsIndex];
this.panel.removeChild(b.div);
stats.splice(statsIndex, 1);
}
var wrapper = document.createElement("div");
const wrapper = document.createElement("div");
wrapper.className = "stats";
var title = document.createElement("div");
const title = document.createElement("div");
title.className = "title";
title.textContent = "Page: " + pageNumber;
var statsDiv = document.createElement("div");
const statsDiv = document.createElement("div");
statsDiv.textContent = stat.toString();
wrapper.appendChild(title);
wrapper.appendChild(statsDiv);
stats.push({ pageNumber, div: wrapper });
stats.sort(function(a, b) {
stats.sort(function (a, b) {
return a.pageNumber - b.pageNumber;
});
clear(this.panel);
for (var i = 0, ii = stats.length; i < ii; ++i) {
for (let i = 0, ii = stats.length; i < ii; ++i) {
this.panel.appendChild(stats[i].div);
}
},
@@ -502,30 +514,27 @@ var Stats = (function Stats() {
// Manages all the debugging tools.
window.PDFBug = (function PDFBugClosure() {
var panelWidth = 300;
var buttons = [];
var activePanel = null;
const panelWidth = 300;
const buttons = [];
let activePanel = null;
return {
tools: [FontInspector, StepperManager, Stats],
enable(ids) {
var all = false,
tools = this.tools;
if (ids.length === 1 && ids[0] === "all") {
all = true;
}
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
const all = ids.length === 1 && ids[0] === "all";
const tools = this.tools;
for (let i = 0; i < tools.length; ++i) {
const tool = tools[i];
if (all || ids.includes(tool.id)) {
tool.enabled = true;
}
}
if (!all) {
// Sort the tools by the order they are enabled.
tools.sort(function(a, b) {
var indexA = ids.indexOf(a.id);
tools.sort(function (a, b) {
let indexA = ids.indexOf(a.id);
indexA = indexA < 0 ? tools.length : indexA;
var indexB = ids.indexOf(b.id);
let indexB = ids.indexOf(b.id);
indexB = indexB < 0 ? tools.length : indexB;
return indexA - indexB;
});
@@ -541,14 +550,14 @@ window.PDFBug = (function PDFBugClosure() {
* Panel
* ...
*/
var ui = document.createElement("div");
const ui = document.createElement("div");
ui.id = "PDFBug";
var controls = document.createElement("div");
const controls = document.createElement("div");
controls.setAttribute("class", "controls");
ui.appendChild(controls);
var panels = document.createElement("div");
const panels = document.createElement("div");
panels.setAttribute("class", "panels");
ui.appendChild(panels);
@@ -556,17 +565,17 @@ window.PDFBug = (function PDFBugClosure() {
container.style.right = panelWidth + "px";
// Initialize all the debugging tools.
var tools = this.tools;
var self = this;
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
var panel = document.createElement("div");
var panelButton = document.createElement("button");
const tools = this.tools;
const self = this;
for (let i = 0; i < tools.length; ++i) {
const tool = tools[i];
const panel = document.createElement("div");
const panelButton = document.createElement("button");
panelButton.textContent = tool.name;
panelButton.addEventListener(
"click",
(function(selected) {
return function(event) {
(function (selected) {
return function (event) {
event.preventDefault();
self.selectPanel(selected);
};
@@ -592,7 +601,7 @@ window.PDFBug = (function PDFBugClosure() {
this.selectPanel(0);
},
cleanup() {
for (var i = 0, ii = this.tools.length; i < ii; i++) {
for (let i = 0, ii = this.tools.length; i < ii; i++) {
if (this.tools[i].enabled) {
this.tools[i].cleanup();
}
@@ -606,17 +615,12 @@ window.PDFBug = (function PDFBugClosure() {
return;
}
activePanel = index;
var tools = this.tools;
for (var j = 0; j < tools.length; ++j) {
if (j === index) {
buttons[j].setAttribute("class", "active");
tools[j].active = true;
tools[j].panel.removeAttribute("hidden");
} else {
buttons[j].setAttribute("class", "");
tools[j].active = false;
tools[j].panel.setAttribute("hidden", "true");
}
const tools = this.tools;
for (let j = 0; j < tools.length; ++j) {
const isActive = j === index;
buttons[j].classList.toggle("active", isActive);
tools[j].active = isActive;
tools[j].panel.hidden = !isActive;
}
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

View File

@@ -0,0 +1,4 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 12a1 1 0 0 1-.707-.293l-5-5a1 1 0 0 1 1.414-1.414L8 9.586l4.293-4.293a1 1 0 0 1 1.414 1.414l-5 5A1 1 0 0 1 8 12z"></path></svg>

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,4 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M13 11a1 1 0 0 1-.707-.293L8 6.414l-4.293 4.293a1 1 0 0 1-1.414-1.414l5-5a1 1 0 0 1 1.414 0l5 5A1 1 0 0 1 13 11z"></path></svg>

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"
fill="rgba(255,255,255,1)" style="animation:spinLoadingIcon 1s steps(12,end)
infinite"><style>@keyframes
spinLoadingIcon{to{transform:rotate(360deg)}}</style><path
d="M7 3V1s0-1 1-1 1 1 1 1v2s0 1-1 1-1-1-1-1z"/><path d="M4.63
4.1l-1-1.73S3.13 1.5 4 1c.87-.5 1.37.37 1.37.37l1 1.73s.5.87-.37
1.37c-.87.57-1.37-.37-1.37-.37z" fill-opacity=".93"/><path
d="M3.1 6.37l-1.73-1S.5 4.87 1 4c.5-.87 1.37-.37 1.37-.37l1.73 1s.87.5.37
1.37c-.5.87-1.37.37-1.37.37z" fill-opacity=".86"/><path d="M3
9H1S0 9 0 8s1-1 1-1h2s1 0 1 1-1 1-1 1z" fill-opacity=".79"/><path d="M4.1 11.37l-1.73 1S1.5 12.87 1
12c-.5-.87.37-1.37.37-1.37l1.73-1s.87-.5 1.37.37c.5.87-.37 1.37-.37 1.37z"
fill-opacity=".72"/><path d="M3.63 13.56l1-1.73s.5-.87
1.37-.37c.87.5.37 1.37.37 1.37l-1 1.73s-.5.87-1.37.37c-.87-.5-.37-1.37-.37-1.37z"
fill-opacity=".65"/><path d="M7 15v-2s0-1 1-1 1 1 1 1v2s0 1-1
1-1-1-1-1z" fill-opacity=".58"/><path d="M10.63
14.56l-1-1.73s-.5-.87.37-1.37c.87-.5 1.37.37 1.37.37l1 1.73s.5.87-.37
1.37c-.87.5-1.37-.37-1.37-.37z" fill-opacity=".51"/><path
d="M13.56 12.37l-1.73-1s-.87-.5-.37-1.37c.5-.87 1.37-.37 1.37-.37l1.73 1s.87.5.37
1.37c-.5.87-1.37.37-1.37.37z" fill-opacity=".44"/><path d="M15
9h-2s-1 0-1-1 1-1 1-1h2s1 0 1 1-1 1-1 1z" fill-opacity=".37"/><path d="M14.56 5.37l-1.73
1s-.87.5-1.37-.37c-.5-.87.37-1.37.37-1.37l1.73-1s.87-.5 1.37.37c.5.87-.37 1.37-.37
1.37z" fill-opacity=".3"/><path d="M9.64 3.1l.98-1.66s.5-.874
1.37-.37c.87.5.37 1.37.37 1.37l-1 1.73s-.5.87-1.37.37c-.87-.5-.37-1.37-.37-1.37z"
fill-opacity=".23"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" style="animation:spinLoadingIcon 1s steps(12,end) infinite"><style>@keyframes spinLoadingIcon{to{transform:rotate(360deg)}}</style><path d="M7 3V1s0-1 1-1 1 1 1 1v2s0 1-1 1-1-1-1-1z"/><path d="M4.63 4.1l-1-1.73S3.13 1.5 4 1c.87-.5 1.37.37 1.37.37l1 1.73s.5.87-.37 1.37c-.87.57-1.37-.37-1.37-.37z" fill-opacity=".93"/><path d="M3.1 6.37l-1.73-1S.5 4.87 1 4c.5-.87 1.37-.37 1.37-.37l1.73 1s.87.5.37 1.37c-.5.87-1.37.37-1.37.37z" fill-opacity=".86"/><path d="M3 9H1S0 9 0 8s1-1 1-1h2s1 0 1 1-1 1-1 1z" fill-opacity=".79"/><path d="M4.1 11.37l-1.73 1S1.5 12.87 1 12c-.5-.87.37-1.37.37-1.37l1.73-1s.87-.5 1.37.37c.5.87-.37 1.37-.37 1.37z" fill-opacity=".72"/><path d="M3.63 13.56l1-1.73s.5-.87 1.37-.37c.87.5.37 1.37.37 1.37l-1 1.73s-.5.87-1.37.37c-.87-.5-.37-1.37-.37-1.37z" fill-opacity=".65"/><path d="M7 15v-2s0-1 1-1 1 1 1 1v2s0 1-1 1-1-1-1-1z" fill-opacity=".58"/><path d="M10.63 14.56l-1-1.73s-.5-.87.37-1.37c.87-.5 1.37.37 1.37.37l1 1.73s.5.87-.37 1.37c-.87.5-1.37-.37-1.37-.37z" fill-opacity=".51"/><path d="M13.56 12.37l-1.73-1s-.87-.5-.37-1.37c.5-.87 1.37-.37 1.37-.37l1.73 1s.87.5.37 1.37c-.5.87-1.37.37-1.37.37z" fill-opacity=".44"/><path d="M15 9h-2s-1 0-1-1 1-1 1-1h2s1 0 1 1-1 1-1 1z" fill-opacity=".37"/><path d="M14.56 5.37l-1.73 1s-.87.5-1.37-.37c-.5-.87.37-1.37.37-1.37l1.73-1s.87-.5 1.37.37c.5.87-.37 1.37-.37 1.37z" fill-opacity=".3"/><path d="M9.64 3.1l.98-1.66s.5-.874 1.37-.37c.87.5.37 1.37.37 1.37l-1 1.73s-.5.87-1.37.37c-.87-.5-.37-1.37-.37-1.37z" fill-opacity=".23"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16
16">
<path
d="M8 16a8 8 0 1 1 8-8 8.009 8.009 0 0 1-8 8zM8 2a6 6 0 1 0 6 6 6.006 6.006 0 0 0-6-6z">
</path>
<path
d="M8 7a1 1 0 0 0-1 1v3a1 1 0 0 0 2 0V8a1 1 0 0 0-1-1z">
</path>
<circle
cx="8" cy="5" r="1.188">
</circle>
</svg>

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13 13c-.3 0-.5-.1-.7-.3L8 8.4l-4.3 4.3c-.9.9-2.3-.5-1.4-1.4l5-5c.4-.4 1-.4 1.4 0l5 5c.6.6.2 1.7-.7 1.7zm0-11H3C1.7 2 1.7 4 3 4h10c1.3 0 1.3-2 0-2z"/></svg>

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M15 3.7V13c0 1.5-1.53 3-3 3H7.13c-.72 0-1.63-.5-2.13-1l-5-5s.84-1 .87-1c.13-.1.33-.2.53-.2.1 0 .3.1.4.2L4 10.6V2.7c0-.6.4-1 1-1s1 .4 1 1v4.6h1V1c0-.6.4-1 1-1s1 .4 1 1v6.3h1V1.7c0-.6.4-1 1-1s1 .4 1 1v5.7h1V3.7c0-.6.4-1 1-1s1 .4 1 1z"/></svg>

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M8 10c-.3 0-.5-.1-.7-.3l-5-5c-.9-.9.5-2.3 1.4-1.4L8 7.6l4.3-4.3c.9-.9 2.3.5 1.4 1.4l-5 5c-.2.2-.4.3-.7.3zm5 2H3c-1.3 0-1.3 2 0 2h10c1.3 0 1.3-2 0-2z"/></svg>

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M1 1a1 1 0 011 1v2.4A7 7 0 118 15a7 7 0 01-4.9-2 1 1 0 011.4-1.5 5 5 0 10-1-5.5H6a1 1 0 010 2H1a1 1 0 01-1-1V2a1 1 0 011-1z"/></svg>

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

View File

@@ -0,0 +1,4 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M15 1a1 1 0 0 0-1 1v2.418A6.995 6.995 0 1 0 8 15a6.954 6.954 0 0 0 4.95-2.05 1 1 0 0 0-1.414-1.414A5.019 5.019 0 1 1 12.549 6H10a1 1 0 0 0 0 2h5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"></path></svg>

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M0 4h1.5c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5H0zM9.5 4c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5h-3c-1 0-1.5-.5-1.5-1.5v-5C5 4.5 5.5 4 6.5 4zM16 4h-1.5c-1 0-1.5.5-1.5 1.5v5c0 1 .5 1.5 1.5 1.5H16z"/></svg>

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M9.5 4c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5h-3c-1 0-1.5-.5-1.5-1.5v-5C5 4.5 5.5 4 6.5 4zM11 0v.5c0 1-.5 1.5-1.5 1.5h-3C5.5 2 5 1.5 5 .5V0h6zM11 16v-.5c0-1-.5-1.5-1.5-1.5h-3c-1 0-1.5.5-1.5 1.5v.5h6z"/></svg>

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M5.5 4c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5h-3c-1 0-1.5-.5-1.5-1.5v-5C1 4.5 1.5 4 2.5 4zM7 0v.5C7 1.5 6.5 2 5.5 2h-3C1.5 2 1 1.5 1 .5V0h6zM7 16v-.5c0-1-.5-1.5-1.5-1.5h-3c-1 0-1.5.5-1.5 1.5v.5h6zM13.5 4c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5h-3c-1 0-1.5-.5-1.5-1.5v-5c0-1 .5-1.5 1.5-1.5zM15 0v.5c0 1-.5 1.5-1.5 1.5h-3C9.5 2 9 1.5 9 .5V0h6zM15 16v-.507c0-1-.5-1.5-1.5-1.5h-3C9.5 14 9 14.5 9 15.5v.5h6z"/></svg>

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

View File

@@ -0,0 +1,4 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M12.408 8.217l-8.083-6.7A.2.2 0 0 0 4 1.672V12.3a.2.2 0 0 0 .333.146l2.56-2.372 1.857 3.9A1.125 1.125 0 1 0 10.782 13L8.913 9.075l3.4-.51a.2.2 0 0 0 .095-.348z"></path></svg>

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M1.5 3.5C.5 3.5 0 4 0 5v6.5c0 1 .5 1.5 1.5 1.5h4c1 0 1.5-.5 1.5-1.5V5c0-1-.5-1.5-1.5-1.5zm2 1.2c.8 0 1.4.2 1.8.6.5.4.7 1 .7 1.7 0 .5-.2 1-.5 1.4-.2.3-.5.7-1 1l-.6.4c-.4.3-.6.4-.75.56-.15.14-.25.24-.35.44H6v1.3H1c0-.6.1-1.1.3-1.5.3-.6.7-1 1.5-1.6.7-.4 1.1-.8 1.28-1 .32-.3.42-.6.42-1 0-.3-.1-.6-.23-.8-.17-.2-.37-.3-.77-.3s-.7.1-.9.5c-.04.2-.1.5-.1.9H1.1c0-.6.1-1.1.3-1.5.4-.7 1.1-1.1 2.1-1.1zM10.54 3.54C9.5 3.54 9 4 9 5v6.5c0 1 .5 1.5 1.54 1.5h4c.96 0 1.46-.5 1.46-1.5V5c0-1-.5-1.46-1.5-1.46zm1.9.95c.7 0 1.3.2 1.7.5.4.4.6.8.6 1.4 0 .4-.1.8-.4 1.1-.2.2-.3.3-.5.4.1 0 .3.1.6.3.4.3.5.8.5 1.4 0 .6-.2 1.2-.6 1.6-.4.5-1.1.7-1.9.7-1 0-1.8-.3-2.2-1-.14-.29-.24-.69-.24-1.29h1.4c0 .3 0 .5.1.7.2.4.5.5 1 .5.3 0 .5-.1.7-.3.2-.2.3-.5.3-.8 0-.5-.2-.8-.6-.95-.2-.05-.5-.15-1-.15v-1c.5 0 .8-.1 1-.14.3-.1.5-.4.5-.9 0-.3-.1-.5-.2-.7-.2-.2-.4-.3-.7-.3-.3 0-.6.1-.75.3-.2.2-.2.5-.2.86h-1.34c0-.4.1-.7.19-1.1 0-.12.2-.32.4-.62.2-.2.4-.3.7-.4.3-.1.6-.1 1-.1z"/></svg>

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M6 3c-1 0-1.5.5-1.5 1.5v7c0 1 .5 1.5 1.5 1.5h4c1 0 1.5-.5 1.5-1.5v-7c0-1-.5-1.5-1.5-1.5z"/></svg>

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M10.56 3.5C9.56 3.5 9 4 9 5v6.5c0 1 .5 1.5 1.5 1.5h4c1 0 1.5-.5 1.5-1.5V5c0-1-.5-1.5-1.5-1.5zm1.93 1.2c.8 0 1.4.2 1.8.64.5.4.7 1 .7 1.7 0 .5-.2 1-.5 1.44-.2.3-.6.6-1 .93l-.6.4c-.4.3-.6.4-.7.55-.1.1-.2.2-.3.4h3.2v1.27h-5c0-.5.1-1 .3-1.43.2-.49.7-1 1.5-1.54.7-.5 1.1-.8 1.3-1.02.3-.3.4-.7.4-1.05 0-.3-.1-.6-.3-.77-.2-.2-.4-.3-.7-.3-.4 0-.7.2-.9.5-.1.2-.1.5-.2.9h-1.4c0-.6.2-1.1.3-1.5.4-.7 1.1-1.1 2-1.1zM1.54 3.5C.54 3.5 0 4 0 5v6.5c0 1 .5 1.5 1.54 1.5h4c1 0 1.5-.5 1.5-1.5V5c0-1-.5-1.5-1.5-1.5zm1.8 1.125H4.5V12H3V6.9H1.3v-1c.5 0 .8 0 .97-.03.33-.07.53-.17.73-.37.1-.2.2-.3.25-.5.05-.2.05-.3.05-.3z"/></svg>

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M4 16V2s0-1 1-1h6s1 0 1 1v14l-4-5z"/></svg>

After

Width:  |  Height:  |  Size: 115 B

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