최초 커밋
commit
74a20dfd0c
@ -0,0 +1,182 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
|
||||
|
||||
<groupId>cokr.xit.base</groupId>
|
||||
<artifactId>xit-docs</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>xit-docs</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
|
||||
<spring.maven.artifact.version>5.3.20</spring.maven.artifact.version>
|
||||
<org.egovframe.rte.version>4.1.0</org.egovframe.rte.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mvn2s</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>maven-public</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>cokr.xit.base</groupId>
|
||||
<artifactId>xit-foundation</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
<!-- 한컴 한글 라이브러리 -->
|
||||
<dependency>
|
||||
<groupId>kr.dogfoot</groupId>
|
||||
<artifactId>hwplib</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
|
||||
<resources>
|
||||
<resource><directory>${basedir}/src/main/resources</directory></resource>
|
||||
</resources>
|
||||
|
||||
<testResources>
|
||||
<testResource><directory>${basedir}/src/test/resources</directory></testResource>
|
||||
<testResource><directory>${basedir}/src/main/resources</directory></testResource>
|
||||
</testResources>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- EMMA -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>emma-maven-plugin</artifactId>
|
||||
<version>1.0-alpha-3</version>
|
||||
</plugin>
|
||||
<!-- PMD manven plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*.class</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- EMMA -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
<reportFormat>xml</reportFormat>
|
||||
<excludes>
|
||||
<exclude>**/Abstract*.java</exclude>
|
||||
<exclude>**/*Suite.java</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>emma-maven-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!-- Nexus deploy -->
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshot</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
|
||||
<repository>
|
||||
<id>maven-release</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<!-- Nexus deploy -->
|
||||
|
||||
</project>
|
@ -0,0 +1,81 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import kr.dogfoot.hwplib.object.HWPFile;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.Control;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlType;
|
||||
import kr.dogfoot.hwplib.reader.HWPReader;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.ControlFinder;
|
||||
|
||||
/**아래아 한글 파일 처리를 지원하는 베이스 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class HWP {
|
||||
public static final String MIME_TYPE = "application/vnd.hancom.hwp";
|
||||
|
||||
/**지정하는 파일경로의 한글 파일을 반환한다.
|
||||
* @param path 파일경로
|
||||
* @return 한글 파일
|
||||
*/
|
||||
public static HWPFile filepath(String path) {
|
||||
try {
|
||||
return HWPReader.fromFile(path);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**클래스패스 상에서 지정하는 파일경로의 한글 파일을 반환한다.
|
||||
* @param path 파일경로
|
||||
* @return 한글 파일
|
||||
*/
|
||||
public static HWPFile classpath(String path) {
|
||||
try (InputStream input = new ClassPathResource(path).getInputStream()) {
|
||||
return filestream(input);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**지정하는 InputStream에서 한글 파일을 로드하여 반환한다.
|
||||
* @param input InputStream
|
||||
* @return 한글 파일
|
||||
*/
|
||||
public static HWPFile filestream(InputStream input) {
|
||||
try {
|
||||
return HWPReader.fromInputStream(input);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**한글 파일*/
|
||||
protected HWPFile file;
|
||||
|
||||
/**새 HWP를 생성한다.
|
||||
* @param file 한글 파일
|
||||
*/
|
||||
public HWP(HWPFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**한글 파일을 반환한다.
|
||||
* @return
|
||||
*/
|
||||
public HWPFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**지정하는 유형의 컨트롤들을 반환한다.
|
||||
* @param type 컨트롤 유형
|
||||
* @return 지정하는 유형의 컨트롤
|
||||
*/
|
||||
public List<Control> getControls(ControlType type) {
|
||||
return ControlFinder.find(file, (control, paragraph, section) -> type == control.getType());
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import kr.dogfoot.hwplib.object.HWPFile;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.Control;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlField;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.Paragraph;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.ParagraphList;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.text.ParaText;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.FieldFinder;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.TextBuffer;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.forField.ForParagraphList;
|
||||
|
||||
/**아래아 한글 파일 작성 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class HWPWriter extends HWP {
|
||||
private Image image;
|
||||
private Table table;
|
||||
private Header header;
|
||||
|
||||
/**새 HWPWriter를 생성한다.
|
||||
* @param file 한글 파일
|
||||
*/
|
||||
public HWPWriter(HWPFile file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
/**지정하는 이름의 필드/누름틀에 값을 설정한다.
|
||||
* @param name 필드/누름틀 이름
|
||||
* @param objs 설정하려는 값
|
||||
* @return 현재 HWPWriter
|
||||
*/
|
||||
public HWPWriter setValue(String name, Iterable<?> objs) {
|
||||
try {
|
||||
if (objs instanceof ArrayList) {
|
||||
FieldFinder.setClickHereText(file, name, (ArrayList<String>)objs);
|
||||
return this;
|
||||
}
|
||||
|
||||
ArrayList<String> vals = new ArrayList<>();
|
||||
for (Object val: objs) {
|
||||
vals.add(val != null ? val.toString() : "");
|
||||
}
|
||||
FieldFinder.setClickHereText(file, name, vals);
|
||||
return this;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**지정하는 이름의 필드/누름틀에 값을 설정한다.
|
||||
* @param name 필드/누름틀 이름
|
||||
* @param vals 설정하려는 값
|
||||
* @return 현재 HWPWriter
|
||||
*/
|
||||
public HWPWriter setValue(String name, Object... vals) {
|
||||
return setValue(name, List.of(vals));
|
||||
}
|
||||
|
||||
/**지정하는 객체의 값을 이름의 필드/누름틀에 설정한다.
|
||||
* @param <T> 객체유형
|
||||
* @param t 객체
|
||||
* @param mappers 필드/누름틀 이름을 키로하고, 해당 이름에 해당하는 값을 반환하는 함수를 값으로 하는 맵
|
||||
* @return 현재 HWPWriter
|
||||
*/
|
||||
public <T> HWPWriter setValues(T t, Map<String, Function<T, Object>> mappers) {
|
||||
if (!Assert.isEmpty(t)) {
|
||||
mappers.forEach((k, mapper) -> setValue(k, mapper.apply(t)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정하는 맵의 값들을 키에 해당하는 필드/누름틀에 설정한다.
|
||||
* @param map 맵
|
||||
* @return 현재 HWPWriter
|
||||
*/
|
||||
public HWPWriter setValues(Map<?, ?> map) {
|
||||
if (!Assert.isEmpty(map)) {
|
||||
setValues(map, getMappers(map));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
<T extends Map<?, ?>> Map<String, Function<T, Object>> getMappers(T map) {
|
||||
return map.keySet().stream().collect(Collectors.toMap(
|
||||
k -> k.toString(),
|
||||
k -> m -> m.get(k)
|
||||
));
|
||||
}
|
||||
|
||||
ParaText getTextIn(Paragraph paragraph) {
|
||||
ParaText paraText = paragraph.getText();
|
||||
if (paraText == null) {
|
||||
paragraph.createText();
|
||||
paraText = paragraph.getText();
|
||||
}
|
||||
return paraText;
|
||||
}
|
||||
|
||||
void setValue(Paragraph paragraph, Object val) throws Exception {
|
||||
ParaText paraText = getTextIn(paragraph);
|
||||
paraText.addString(val != null ? val.toString() : "");
|
||||
}
|
||||
|
||||
<T extends Map<?, ?>> void setValues(ParagraphList paragraphs, T obj, Map<String, Function<T, Object>> mappers) throws Exception {
|
||||
for (Paragraph paragraph: paragraphs) {
|
||||
ArrayList<Control> controls = paragraph.getControlList();
|
||||
Control control = !Assert.isEmpty(controls) ? controls.get(0) : null;
|
||||
if (!(control instanceof ControlField)) continue;
|
||||
|
||||
ControlField controlField = (ControlField)control;
|
||||
String fieldName = controlField.getName();
|
||||
Function<T, Object> mapper = !Assert.isEmpty(fieldName) ? mappers.get(fieldName) : null;
|
||||
if (mapper == null) continue;
|
||||
|
||||
Object val = mapper.apply(obj);
|
||||
ArrayList<String> strs = new ArrayList<>();
|
||||
strs.add(val != null ? val.toString() : "");
|
||||
|
||||
ForParagraphList.setFieldText(paragraphs, controlField.getType(), fieldName, new TextBuffer(strs));
|
||||
}
|
||||
}
|
||||
|
||||
/**한글 파일에 이미지를 추가하기 위한 Image를 반환한다.
|
||||
* @return Image
|
||||
*/
|
||||
public Image image() {
|
||||
return Assert.ifEmpty(image, () -> image = new Image(this));
|
||||
}
|
||||
|
||||
/**한글 파일의 테이블에 데이터를 설정하기 위한 Table을 반환한다.
|
||||
* @param index 테이블 인덱스
|
||||
* @param rowstart 데이터 설정 시작 행의 인덱스
|
||||
* @param rowspan 데이터 행의 줄수
|
||||
* @return Table
|
||||
*/
|
||||
public Table table(int index, int rowstart, int rowspan) {
|
||||
return Assert.ifEmpty(table, () -> table = new Table(this)).of(index, rowstart, rowspan);
|
||||
}
|
||||
|
||||
public Header header() {
|
||||
return Assert.ifEmpty(header, () -> header = new Header(this));
|
||||
}
|
||||
|
||||
/**설정된 데이터와 내용을 지정한 OutputStream에 저장한다.
|
||||
* @param out OutputStream
|
||||
*/
|
||||
public void write(OutputStream out) {
|
||||
try {
|
||||
kr.dogfoot.hwplib.writer.HWPWriter.toStream(file, out);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**설정된 데이터와 내용을 지정한 경로의 파일에 저장한다.
|
||||
* @param filepath 파일경로
|
||||
* @return 저장한 한글 파일
|
||||
*/
|
||||
public File write(String filepath) {
|
||||
File file = new File(filepath);
|
||||
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||
write(out);
|
||||
return file;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.Control;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlHeader;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlType;
|
||||
|
||||
/**한글 파일의 머릿글에 데이터 설정을 지원하는 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Header {
|
||||
private HWPWriter writer;
|
||||
|
||||
Header(HWPWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
private List<Control> headers() {
|
||||
return writer.getControls(ControlType.Header);
|
||||
}
|
||||
|
||||
private ControlHeader header() {
|
||||
List<Control> controls = writer.getControls(ControlType.Header);
|
||||
if (controls.isEmpty())
|
||||
throw new RuntimeException("ControlHeader not found");
|
||||
|
||||
return (ControlHeader)controls.get(0);
|
||||
}
|
||||
|
||||
/**지정하는 인덱스의 머릿글에 지정하는 객체를 데이터로 설정한다.
|
||||
* @param index 머릿글 인덱스
|
||||
* @param obj 객체
|
||||
* @return 현재 Header와 연결된 HWPWriter
|
||||
*/
|
||||
public HWPWriter setValue(int index, Object obj) {
|
||||
try {
|
||||
List<Control> headers = headers();
|
||||
if (headers.isEmpty())
|
||||
throw new RuntimeException("ControlHeader not found");
|
||||
|
||||
ControlHeader header = (ControlHeader)headers.get(index);
|
||||
writer.setValue(header.getParagraphList().getParagraph(0), obj);
|
||||
|
||||
return writer;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**머릿글에 지정하는 객체를 데이터로 설정한다.
|
||||
* @param obj 객체
|
||||
* @return 현재 Header와 연결된 HWPWriter
|
||||
*/
|
||||
public HWPWriter setValue(Object obj) {
|
||||
return setValue(0, obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import kr.dogfoot.hwplib.object.bindata.BinData;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.CtrlHeaderGso;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.GsoHeaderProperty;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.HeightCriterion;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.HorzRelTo;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.ObjectNumberSort;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.RelativeArrange;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.TextFlowMethod;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.TextHorzArrange;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.VertRelTo;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ctrlheader.gso.WidthCriterion;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.ControlRectangle;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.GsoControlType;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.ShapeComponentNormal;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.LineArrowShape;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.LineArrowSize;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.LineEndShape;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.LineInfo;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.LineType;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.lineinfo.OutlineStyle;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.shadowinfo.ShadowInfo;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponent.shadowinfo.ShadowType;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.gso.shapecomponenteach.ShapeComponentRectangle;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.Paragraph;
|
||||
import kr.dogfoot.hwplib.object.docinfo.bindata.BinDataCompress;
|
||||
import kr.dogfoot.hwplib.object.docinfo.bindata.BinDataProperty;
|
||||
import kr.dogfoot.hwplib.object.docinfo.bindata.BinDataState;
|
||||
import kr.dogfoot.hwplib.object.docinfo.bindata.BinDataType;
|
||||
import kr.dogfoot.hwplib.object.docinfo.borderfill.fillinfo.FillInfo;
|
||||
import kr.dogfoot.hwplib.object.docinfo.borderfill.fillinfo.ImageFill;
|
||||
import kr.dogfoot.hwplib.object.docinfo.borderfill.fillinfo.ImageFillType;
|
||||
import kr.dogfoot.hwplib.object.docinfo.borderfill.fillinfo.PictureEffect;
|
||||
|
||||
/**한글 파일의 이미지 처리를 지원하는 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Image {
|
||||
private HWPWriter writer;
|
||||
private int instanceID = 0x5bb840e1;
|
||||
|
||||
Image(HWPWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
/**지정한 경로의 이미지 파일을 지정한 문단의 영역에 추가한다.
|
||||
* @param path 이미지 파일 경로
|
||||
* @param paragraph 문단
|
||||
* @param rect 이미지 영역
|
||||
* @return 현재 Image
|
||||
*/
|
||||
public Image add(String path, Paragraph paragraph, Rectangle rect) {
|
||||
try {
|
||||
int index = addData(path),
|
||||
dataID = addToDocInfo(index, getExt(path));
|
||||
|
||||
ControlRectangle ctrlRect = newRect(paragraph);
|
||||
|
||||
setCtrlHeaderGso(ctrlRect, rect);
|
||||
setShapeComponent(ctrlRect, rect, dataID);
|
||||
setShapeComponentRectangle(ctrlRect, rect);
|
||||
|
||||
return this;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**지정한 경로의 이미지 파일을 지정한 문단에 추가한다.
|
||||
* @param path 이미지 파일 경로
|
||||
* @param paragraph 문단
|
||||
* @return 현재 Image
|
||||
*/
|
||||
public Image add(String path, Paragraph paragraph) {
|
||||
return add(path, paragraph, new Rectangle(50, 50, 100, 100));
|
||||
}
|
||||
|
||||
/**지정한 경로의 이미지 파일을 추가하는 함수를 반환한다.
|
||||
* @param path 이미지 파일 경로
|
||||
* @return 이미지 파일을 추가하는 함수
|
||||
*/
|
||||
public BiConsumer<Paragraph, Rectangle> add(String path) {
|
||||
return !Assert.isEmpty(path) ? (paragraph, rect) -> add(path, paragraph, rect) : null;
|
||||
}
|
||||
|
||||
private int addData(String path) throws Exception {
|
||||
BinData binData = writer.file.getBinData();
|
||||
int index = binData.getEmbeddedBinaryDataList().size() + 1;
|
||||
String ext = getExt(path),
|
||||
name = String.format("Bin%04X", index) + (!ext.isEmpty() ? "." + ext : ext);
|
||||
|
||||
try (FileInputStream input = new FileInputStream(path)) {
|
||||
byte[] bytes = input.readAllBytes();
|
||||
binData.addNewEmbeddedBinaryData(name, bytes, BinDataCompress.ByStorageDefault);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getExt(String path) {
|
||||
int pos = path.lastIndexOf(".");
|
||||
return pos < 0 ? "" : path.substring(pos + 1);
|
||||
}
|
||||
|
||||
private int addToDocInfo(int index, String ext) {
|
||||
kr.dogfoot.hwplib.object.docinfo.BinData binData = new kr.dogfoot.hwplib.object.docinfo.BinData();
|
||||
BinDataProperty property = binData.getProperty();
|
||||
|
||||
property.setType(BinDataType.Embedding);
|
||||
property.setCompress(BinDataCompress.ByStorageDefault);
|
||||
property.setState(BinDataState.NotAccess);
|
||||
|
||||
binData.setBinDataID(index);
|
||||
binData.setExtensionForEmbedding(ext);
|
||||
|
||||
ArrayList<kr.dogfoot.hwplib.object.docinfo.BinData> dataList = writer.file.getDocInfo().getBinDataList();
|
||||
dataList.add(binData);
|
||||
|
||||
return dataList.size();
|
||||
}
|
||||
|
||||
private ControlRectangle newRect(Paragraph paragraph) {
|
||||
writer.getTextIn(paragraph)
|
||||
.addExtendCharForGSO();
|
||||
return (ControlRectangle)paragraph.addNewGsoControl(GsoControlType.Rectangle);
|
||||
}
|
||||
|
||||
private static int fromMM(int mm) {
|
||||
return mm;
|
||||
//return mm == 0 ? 1 : (int)((double)mm * 72000.0f / 254.0f + 0.5f);
|
||||
}
|
||||
|
||||
private void setCtrlHeaderGso(ControlRectangle ctrlRect, Rectangle rect) {
|
||||
CtrlHeaderGso header = ctrlRect.getHeader();
|
||||
GsoHeaderProperty property = header.getProperty();
|
||||
|
||||
property.setLikeWord(false);
|
||||
property.setApplyLineSpace(false);
|
||||
property.setVertRelTo(VertRelTo.Para);
|
||||
property.setVertRelativeArrange(RelativeArrange.TopOrLeft);
|
||||
property.setHorzRelTo(HorzRelTo.Para);
|
||||
property.setHorzRelativeArrange(RelativeArrange.TopOrLeft);
|
||||
property.setVertRelToParaLimit(true);
|
||||
property.setAllowOverlap(true);
|
||||
property.setWidthCriterion(WidthCriterion.Absolute);
|
||||
property.setHeightCriterion(HeightCriterion.Absolute);
|
||||
property.setProtectSize(false);
|
||||
property.setTextFlowMethod(TextFlowMethod.FitWithText);
|
||||
property.setTextHorzArrange(TextHorzArrange.BothSides);
|
||||
property.setObjectNumberSort(ObjectNumberSort.Figure);
|
||||
|
||||
header.setyOffset(fromMM(rect.y));
|
||||
header.setxOffset(fromMM(rect.x));
|
||||
header.setWidth(fromMM(rect.width));
|
||||
header.setHeight(fromMM(rect.height));
|
||||
header.setzOrder(0);
|
||||
header.setOutterMarginLeft(0);
|
||||
header.setOutterMarginRight(0);
|
||||
header.setOutterMarginTop(0);
|
||||
header.setOutterMarginBottom(0);
|
||||
header.setInstanceId(instanceID);
|
||||
header.setPreventPageDivide(false);
|
||||
header.getExplanation().setBytes(null);
|
||||
}
|
||||
|
||||
private void setShapeComponent(ControlRectangle ctrlRect, Rectangle rect, int dataID) {
|
||||
ShapeComponentNormal shape = (ShapeComponentNormal) ctrlRect.getShapeComponent();
|
||||
shape.setOffsetX(0);
|
||||
shape.setOffsetY(0);
|
||||
shape.setGroupingCount(0);
|
||||
shape.setLocalFileVersion(1);
|
||||
int width = fromMM(rect.width),
|
||||
height = fromMM(rect.height);
|
||||
shape.setWidthAtCreate(width);
|
||||
shape.setHeightAtCreate(height);
|
||||
shape.setWidthAtCurrent(width);
|
||||
shape.setHeightAtCurrent(height);
|
||||
shape.setRotateAngle(0);
|
||||
shape.setRotateXCenter(fromMM(rect.width / 2));
|
||||
shape.setRotateYCenter(fromMM(rect.height / 2));
|
||||
|
||||
shape.createLineInfo();
|
||||
LineInfo line = shape.getLineInfo();
|
||||
line.getProperty().setLineEndShape(LineEndShape.Flat);
|
||||
line.getProperty().setStartArrowShape(LineArrowShape.None);
|
||||
line.getProperty().setStartArrowSize(LineArrowSize.MiddleMiddle);
|
||||
line.getProperty().setEndArrowShape(LineArrowShape.None);
|
||||
line.getProperty().setEndArrowSize(LineArrowSize.MiddleMiddle);
|
||||
line.getProperty().setFillStartArrow(true);
|
||||
line.getProperty().setFillEndArrow(true);
|
||||
line.getProperty().setLineType(LineType.None);
|
||||
line.setOutlineStyle(OutlineStyle.Normal);
|
||||
line.setThickness(0);
|
||||
line.getColor().setValue(0);
|
||||
|
||||
shape.createFillInfo();
|
||||
FillInfo fill = shape.getFillInfo();
|
||||
fill.getType().setPatternFill(false);
|
||||
fill.getType().setImageFill(true);
|
||||
fill.getType().setGradientFill(false);
|
||||
fill.createImageFill();
|
||||
ImageFill imgFill = fill.getImageFill();
|
||||
imgFill.setImageFillType(ImageFillType.FitSize);
|
||||
imgFill.getPictureInfo().setBrightness((byte)0);
|
||||
imgFill.getPictureInfo().setContrast((byte)0);
|
||||
imgFill.getPictureInfo().setEffect(PictureEffect.RealPicture);
|
||||
imgFill.getPictureInfo().setBinItemID(dataID);
|
||||
|
||||
shape.createShadowInfo();
|
||||
ShadowInfo shadow = shape.getShadowInfo();
|
||||
shadow.setType(ShadowType.None);
|
||||
shadow.getColor().setValue(0xc4c4c4);
|
||||
shadow.setOffsetX(283);
|
||||
shadow.setOffsetY(283);
|
||||
shadow.setTransparent((short)0);
|
||||
|
||||
shape.setMatrixsNormal();
|
||||
}
|
||||
|
||||
private void setShapeComponentRectangle(ControlRectangle ctrlRect, Rectangle rect) {
|
||||
ShapeComponentRectangle shapeRect = ctrlRect.getShapeComponentRectangle();
|
||||
shapeRect.setRoundRate((byte)0);
|
||||
shapeRect.setX1(0);
|
||||
shapeRect.setY1(0);
|
||||
int width = fromMM(rect.width),
|
||||
height = fromMM(rect.height);
|
||||
shapeRect.setX2(width);
|
||||
shapeRect.setY2(0);
|
||||
shapeRect.setX3(width);
|
||||
shapeRect.setY3(height);
|
||||
shapeRect.setX4(0);
|
||||
shapeRect.setY4(height);
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.Control;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlField;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlTable;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.ControlType;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.table.Cell;
|
||||
import kr.dogfoot.hwplib.object.bodytext.control.table.Row;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.Paragraph;
|
||||
import kr.dogfoot.hwplib.object.bodytext.paragraph.ParagraphList;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.TextBuffer;
|
||||
import kr.dogfoot.hwplib.tool.objectfinder.forField.ForParagraphList;
|
||||
|
||||
/**한글 파일의 테이블의 행에 반복되는 데이터 설정을 지원하는 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Table {
|
||||
private int
|
||||
index = -1,
|
||||
start = -1,
|
||||
rowspan = 1;
|
||||
private HWPWriter writer;
|
||||
|
||||
Table(HWPWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
/**한글 파일에서 지정한 인덱스의 테이블을 반환하고, 데이터를 설정할 레이아웃을 지정한다.
|
||||
* @param index 테이블 인덱스
|
||||
* @param start 데이터 행의 시작 인덱스
|
||||
* @param rowspan 데이터 행 하나의 줄수
|
||||
* @return 현재 Table
|
||||
*/
|
||||
public Table of(int index, int start, int rowspan) {
|
||||
this.index = index;
|
||||
this.start = start;
|
||||
this.rowspan = rowspan;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 객체목록을 테이블의 데이터로 설정한다.
|
||||
* @param <T> 객체유형
|
||||
* @param objs 설정할 데이터를 가지고 있는 객체 목록
|
||||
* @param mappers 객체 하나에서 설정할 데이터를 반환하는 함수들로 된 맵. 맵의 키는 데이터 행의 필드/누름틀 이름이어야 한다.
|
||||
* @return 현재 Image와 연결된 HWPWriter
|
||||
*/
|
||||
public <T> HWPWriter setValues(Iterable<T> objs, Map<String, Function<T, Object>> mappers) {
|
||||
if (Assert.isEmpty(objs)) return writer;
|
||||
Assert.notEmpty(mappers, "mappers");
|
||||
|
||||
ControlTable tableControl = Assert.notEmpty(getTable(index), "표 " + index);
|
||||
ArrayList<Row>
|
||||
rows = tableControl.getRowList(),
|
||||
template = new ArrayList<>();
|
||||
for (int i = start, length = start + rowspan; i < length; ++i)
|
||||
template.add(rows.remove(start));
|
||||
|
||||
try {
|
||||
for (T obj: objs) {
|
||||
for (Row row: template) {
|
||||
Row copy = row.clone();
|
||||
for (Cell cell: copy.getCellList()) {
|
||||
ParagraphList paragraphs = cell.getParagraphList();
|
||||
String fieldName = cell.getListHeader().getFieldName();
|
||||
Function<T, Object> mapper = !Assert.isEmpty(fieldName) ? mappers.get(fieldName) : null;
|
||||
|
||||
if (mapper != null) {
|
||||
Object val = mapper.apply(obj);
|
||||
Paragraph paragraph = paragraphs.getParagraph(0);
|
||||
if (!(val instanceof BiConsumer)) {
|
||||
writer.setValue(paragraph, val);
|
||||
} else {
|
||||
render(val, cell, paragraph);
|
||||
}
|
||||
} else {
|
||||
for (Paragraph paragraph: paragraphs) {
|
||||
ArrayList<Control> controls = paragraph.getControlList();
|
||||
Control control = !Assert.isEmpty(controls) ? controls.get(0) : null;
|
||||
if (!(control instanceof ControlField)) continue;
|
||||
|
||||
ControlField controlField = (ControlField)control;
|
||||
fieldName = controlField.getName();
|
||||
mapper = !Assert.isEmpty(fieldName) ? mappers.get(fieldName) : null;
|
||||
if (mapper == null) continue;
|
||||
|
||||
Object val = mapper.apply(obj);
|
||||
if (!(val instanceof BiConsumer)) {
|
||||
ArrayList<String> strs = new ArrayList<>();
|
||||
strs.add(val != null ? val.toString() : "");
|
||||
|
||||
ForParagraphList.setFieldText(paragraphs, controlField.getType(), fieldName, new TextBuffer(strs));
|
||||
} else {
|
||||
render(val, cell, paragraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rows.add(copy);
|
||||
}
|
||||
}
|
||||
|
||||
int rowCount = rows.size();
|
||||
kr.dogfoot.hwplib.object.bodytext.control.table.Table table = tableControl.getTable();
|
||||
table.setRowCount(rowCount);
|
||||
ArrayList<Integer> cellCounts = table.getCellCountOfRowList();
|
||||
cellCounts.clear();
|
||||
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
ArrayList<Cell> cells = rows.get(i).getCellList();
|
||||
int cellCount = cells.size();
|
||||
cellCounts.add(cellCount);
|
||||
for (int j = 0; j < cellCount; ++j)
|
||||
cells.get(j).getListHeader().setRowIndex(i);
|
||||
}
|
||||
|
||||
return writer;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void render(Object val, Cell cell, Paragraph paragraph) {
|
||||
BiConsumer<Paragraph, Rectangle> renderer = (BiConsumer<Paragraph, Rectangle>)val;
|
||||
int width = (int)cell.getListHeader().getWidth(),
|
||||
height = (int)cell.getListHeader().getHeight();
|
||||
renderer.accept(paragraph, new Rectangle(1, 1, width, height));
|
||||
}
|
||||
|
||||
private ControlTable getTable(int index) {
|
||||
List<Control> found = writer.getControls(ControlType.Table);
|
||||
return !found.isEmpty() ? (ControlTable)found.get(index) : null;
|
||||
}
|
||||
|
||||
/**지정한 맵의 값들을 테이블의 데이터로 설정한다.
|
||||
* @param <T> 맵 유형
|
||||
* @param dataset 맵. 맵의 키는 데이터 행의 필드/누름틀 이름이어야 한다.
|
||||
* @return 현재 Image와 연결된 HWPWriter
|
||||
*/
|
||||
public <T extends Map<?, ?>> HWPWriter setValues(Iterable<T> dataset) {
|
||||
if (Assert.isEmpty(dataset)) return writer;
|
||||
|
||||
T map = dataset.iterator().next();
|
||||
return setValues(dataset, writer.getMappers(map));
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
|
||||
/**셀 정보
|
||||
* <ul><li>셀 레이블</li>
|
||||
* <li>데이터 필드(키) 이름</li>
|
||||
* <li>폭</li>
|
||||
* <li>데이터 또는 스타일</li>
|
||||
* </ul>
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class CellDef {
|
||||
/**CellDef 목록에서 셀헤더 이름과 스타일을 반환한다.
|
||||
* @param defs CellDef 목록
|
||||
* @param factory 헤더 스타일을 제공하는 function
|
||||
* @return 셀헤더 이름과 스타일 목록
|
||||
*/
|
||||
public static List<Object> header(List<CellDef> defs, Supplier<Style> factory) {
|
||||
ArrayList<Object> result = new ArrayList<>();
|
||||
|
||||
for (CellDef def: defs) {
|
||||
result.add(def.label);
|
||||
|
||||
Style style = factory != null ? factory.get() : null;
|
||||
if (style != null) {
|
||||
style.width(def.width);
|
||||
result.add(style);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**valueMap에서 CellDef의 필드이름 또는 스타일을 설정한다.
|
||||
* @param defs CellDef 목록
|
||||
* @param valueMap 레이블별 필드이름 또는 스타일
|
||||
*/
|
||||
public static void setValues(List<CellDef> defs, Map<String, Object> valueMap) {
|
||||
defs.forEach(def -> {
|
||||
Object val = valueMap.getOrDefault(def.label, def.field);
|
||||
def.value = Assert.notEmpty(val, "Value or style for " + def.label);
|
||||
});
|
||||
}
|
||||
|
||||
/**CellDef 목록에서 셀값의 필드이름 또는 스타일 목록을 반환한다.
|
||||
* @param defs CellDef 목록
|
||||
* @return 셀값의 필드이름 또는 스타일 목록
|
||||
*/
|
||||
public static Object[] values(List<CellDef> defs) {
|
||||
if (defs == null)
|
||||
return null;
|
||||
|
||||
ArrayList<Object> objs = new ArrayList<>();
|
||||
for (CellDef def: defs) {
|
||||
String field = def.getField();
|
||||
Object val = def.getValue();
|
||||
|
||||
if (field != null) {
|
||||
objs.add(field);
|
||||
if (val != null)
|
||||
objs.add(val);
|
||||
} else {
|
||||
objs.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return objs.toArray();
|
||||
}
|
||||
|
||||
public static TypeReference<List<CellDef>> listType() {
|
||||
return new TypeReference<List<CellDef>>() {};
|
||||
}
|
||||
|
||||
private String
|
||||
label,
|
||||
field;
|
||||
private int width;
|
||||
private Object value;
|
||||
|
||||
/**레이블을 반환한다.
|
||||
* @return 레이블
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**레이블을 설정한다.
|
||||
* @param label 레이블
|
||||
* @return 현재 CellDef
|
||||
*/
|
||||
public CellDef setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**필드이름을 반환한다.
|
||||
* @return 필드이름
|
||||
*/
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
/**필드이름을 설정한다.
|
||||
* @param field 필드이름
|
||||
* @return 현재 CellDef
|
||||
*/
|
||||
public CellDef setField(String field) {
|
||||
this.field = field;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**넓이를 반환한다.
|
||||
* @return 넓이
|
||||
* @return 현재 CellDef
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**넓이를 설정한다.
|
||||
* @param width 넓이
|
||||
* @return 현재 CellDef
|
||||
*/
|
||||
public CellDef setWidth(int width) {
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**필드이름, 또는 스타일을 반환한다.
|
||||
* @return 필드이름, 또는 스타일
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**필드이름, 또는 스타일을 설정한다.
|
||||
* @param value 필드이름, 또는 스타일
|
||||
* @return 현재 CellDef
|
||||
*/
|
||||
public CellDef setValue(Object value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
|
||||
import org.apache.poi.ss.usermodel.ClientAnchor;
|
||||
import org.apache.poi.ss.usermodel.CreationHelper;
|
||||
import org.apache.poi.xssf.streaming.SXSSFDrawing;
|
||||
import org.apache.poi.xssf.usermodel.XSSFComment;
|
||||
import org.apache.poi.xssf.usermodel.XSSFPictureData;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRelation;
|
||||
import org.apache.poi.xssf.usermodel.XSSFVMLDrawing;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.openxmlformats.schemas.officeDocument.x2006.sharedTypes.STTrueFalse;
|
||||
|
||||
import com.microsoft.schemas.vml.CTFill;
|
||||
import com.microsoft.schemas.vml.CTShape;
|
||||
import com.microsoft.schemas.vml.STFillType;
|
||||
|
||||
/**메모(주석) 지원 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Comment {
|
||||
private XLS xlsx;
|
||||
private XSSFWorkbook workbook;
|
||||
private CreationHelper factory;
|
||||
private ClientAnchor anchor;
|
||||
private XLS.Image image;
|
||||
|
||||
/**새 Comment를 생성한다.
|
||||
* @param writer XLS
|
||||
*/
|
||||
public Comment(XLS writer) {
|
||||
this.xlsx = writer;
|
||||
workbook = this.xlsx.workbook.getXSSFWorkbook();
|
||||
factory = workbook.getCreationHelper();
|
||||
anchor = factory.createClientAnchor();
|
||||
image = new XLS.Image(this.xlsx);
|
||||
}
|
||||
|
||||
/**현재 셀에 메모를 생성하고 주어진 문자열을 설정한다.
|
||||
* @param str 문자열
|
||||
* @return 현재 셀에 생성된 메모
|
||||
*/
|
||||
public XSSFComment create(String str) {
|
||||
int row = xlsx.cell.getRowIndex();
|
||||
anchor.setRow1(row);
|
||||
anchor.setRow2(row + 17);
|
||||
int col = xlsx.cell.getColumnIndex();
|
||||
anchor.setCol1(col);
|
||||
anchor.setCol2(col + 6);
|
||||
|
||||
SXSSFDrawing drawing = xlsx.worksheet.createDrawingPatriarch();
|
||||
XSSFComment comment = (XSSFComment)drawing.createCellComment(anchor);
|
||||
comment.setString(str);
|
||||
xlsx.cell.setCellComment(comment);
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**주어진 경로의 배경 이미지 파일을 추가하고 제목을 설정한다.
|
||||
* @param title 제목
|
||||
* @param path 배경 이미지 파일 경로
|
||||
*/
|
||||
public void setBackgroundImage(String title, String path) {
|
||||
int picIndex = image.add(path);
|
||||
XSSFPictureData pic = workbook.getAllPictures().get(picIndex);
|
||||
XSSFVMLDrawing vml = xlsx.worksheet.getVMLDrawing(true);
|
||||
RelationPart rp = vml.addRelation(null, XSSFRelation.IMAGES, pic);
|
||||
|
||||
int row = xlsx.cell.getRowIndex();
|
||||
int col = xlsx.cell.getColumnIndex();
|
||||
CTShape shape = vml.findCommentShape(row, col);
|
||||
CTFill fill = shape.getFillArray(0);
|
||||
fill.setColor2(fill.getColor());
|
||||
fill.unsetColor();
|
||||
fill.setRelid(rp.getRelationship().getId());
|
||||
fill.setTitle(title);
|
||||
fill.setRecolor(STTrueFalse.T);
|
||||
fill.setRotate(STTrueFalse.T);
|
||||
fill.setType(STFillType.FRAME);
|
||||
}
|
||||
|
||||
/**주어진 경로의 이미지 파일을 배경으로 하는 메모를 생성한다.
|
||||
* @param path 이미지 파일 경로
|
||||
*/
|
||||
public void setImageComment(String path) {
|
||||
create("");
|
||||
setBackgroundImage("", path);
|
||||
}
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
|
||||
import cokr.xit.foundation.data.Convert;
|
||||
|
||||
/**데이터 포맷이나 셀 스타일 설정을 지원하는 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Format implements Convert.Support {
|
||||
private XLSWriter xlsx;
|
||||
private Object key;
|
||||
private UnaryOperator<Object> convert;
|
||||
private CellStyle cellStyle;
|
||||
private Consumer<Object> onCell;
|
||||
|
||||
/**새 Format을 생성한다.
|
||||
* @param xlsx XLSWriter
|
||||
*/
|
||||
public Format(XLSWriter xlsx) {
|
||||
this.xlsx = xlsx;
|
||||
}
|
||||
|
||||
/**새 Format을 생성하고 키를 설정하여 반환한다.
|
||||
* @param key 키. 데이터를 제공하는 객체가 Map일 경우 지정
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format of(Object key) {
|
||||
return new Format(xlsx).key(key);
|
||||
}
|
||||
|
||||
/**키를 설정한다.
|
||||
* @param key 키. 데이터를 제공하는 객체가 Map일 경우 지정
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format key(Object key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**현재 Format이 데이터를 제공할 수 있는지 반환한다.
|
||||
* 현재 Format이 키나 변환함수가 설정되어 있으면 데이터를 제공할 수 있는 것으로 간주한다.
|
||||
* @return 데이터 제공 여부
|
||||
* <ul><li>데이터를 제공할 수 있으면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return convert != null || key != null;
|
||||
}
|
||||
|
||||
/**데이터를 제공하는 객체에서 현재 Format에 설정된 키나 변환함수를 적용하여 데이터를 반환한다.
|
||||
* @param obj 데이터 제공 객체
|
||||
* @return 주어진 객체의 데이터
|
||||
*/
|
||||
public Object value(Object obj) {
|
||||
if (convert != null) {
|
||||
return convert.apply(obj);
|
||||
}
|
||||
|
||||
if (key != null && obj instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>)obj;
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unable to get a value from " + obj);
|
||||
}
|
||||
|
||||
/**데이터 변환함수를 설정한다.
|
||||
* @param func 데이터 변환함수
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format value(UnaryOperator<Object> func) {
|
||||
convert = func;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**주어진 객체가 데이터를 제공할 수 있는지 반환한다.
|
||||
* @param obj 객체
|
||||
* @return 데이터 제공 여부
|
||||
* <ul><li>데이터를 제공할 수 있으면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
static boolean hasValue(Object obj) {
|
||||
if (obj instanceof CellStyle)
|
||||
return false;
|
||||
if (obj instanceof Style)
|
||||
return false;
|
||||
if (obj instanceof Format) {
|
||||
Format format = (Format)obj;
|
||||
return format.hasValue();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**주어진 객체에서 Map에 적용할 키를 반환한다.
|
||||
* @param obj 객체
|
||||
* @return
|
||||
* <ul><li>Map에 적용할 키</li>
|
||||
* <li>키를 제공하지 않으면 null</li>
|
||||
* </ul>
|
||||
*/
|
||||
static Object getKey(Object obj) {
|
||||
if (obj instanceof CellStyle)
|
||||
return null;
|
||||
if (obj instanceof Style)
|
||||
return null;
|
||||
if (obj instanceof Format) {
|
||||
Format format = (Format)obj;
|
||||
if (format.key != null)
|
||||
return format.key;
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**설정된 CellStyle을 반환한다.
|
||||
* @return CellStyle
|
||||
*/
|
||||
public CellStyle style() {
|
||||
return cellStyle;
|
||||
}
|
||||
|
||||
/**현재 셀에 적용할 스타일을 설정한다.
|
||||
* @param obj Style 또는 CellStyle
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format style(Object obj) {
|
||||
if (obj instanceof Style) {
|
||||
cellStyle = cellStyle((Style)obj);
|
||||
} else if (obj instanceof CellStyle) {
|
||||
cellStyle = (CellStyle)obj;
|
||||
} else
|
||||
throw new IllegalArgumentException("Unable to get a CellStyle from " + obj);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**주어진 Style에서 CellStyle을 생성하여 반환한다.
|
||||
* @param style Style
|
||||
* @return CellStyle
|
||||
*/
|
||||
public CellStyle cellStyle(Style style) {
|
||||
return xlsx.cellStyle(style);
|
||||
}
|
||||
|
||||
/**숫자 스타일(오른쪽 정렬, 천단위 구분)을 반환한다.
|
||||
* @return 숫자 스타일(오른쪽 정렬, 천단위 구분)
|
||||
*/
|
||||
public CellStyle n_nn0() {
|
||||
return cellStyle(new Style().alignment(HorizontalAlignment.RIGHT).dataFormat("#,##0"));
|
||||
}
|
||||
|
||||
/**숫자 스타일(오른쪽 정렬, 천단위 구분, 소수점 2자리 표기)을 반환한다.
|
||||
* @return 숫자 스타일(오른쪽 정렬, 천단위 구분, 소수점 2자리 표기)
|
||||
*/
|
||||
public CellStyle n_nn0_2() {
|
||||
return cellStyle(Style.N_NN0_2);
|
||||
}
|
||||
|
||||
/**날짜 포맷(yyyy-MM-dd HH:mm:ss)의 셀스타일을 반환한다.
|
||||
* @return 날짜 포맷(yyyy-MM-dd HH:mm:ss)의 셀스타일
|
||||
*/
|
||||
public CellStyle yyyy_mm_dd_hh_mm_ss() {
|
||||
return cellStyle(Style.YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
/**날짜 포맷(yyyy-MM-dd)의 셀스타일을 반환한다.
|
||||
* @return 날짜 포맷(yyyy-MM-dd)의 셀스타일
|
||||
*/
|
||||
public CellStyle yyyy_mm_dd() {
|
||||
return cellStyle(Style.YYYY_MM_DD);
|
||||
}
|
||||
|
||||
/**시간 포맷(HH:mm:ss)의 셀스타일을 반환한다.
|
||||
* @return 시간 포맷(HH:mm:ss)의 셀스타일
|
||||
*/
|
||||
public CellStyle hh_mm_ss() {
|
||||
return cellStyle(Style.HH_MM_SS);
|
||||
}
|
||||
|
||||
/**현재 셀에 주어진 객체를 가공하여 적용한다.
|
||||
* @param obj 객체
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format onCell(Object obj) {
|
||||
if (onCell != null)
|
||||
onCell.accept(obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**현재 셀 또는 그 값을 가공하는 함수를 설정한다.
|
||||
* @param func 현재 셀 또는 그 값을 가공하는 함수
|
||||
* @return 현재 Format
|
||||
*/
|
||||
public Format onCell(Consumer<Object> func) {
|
||||
onCell = func;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.FillPatternType;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
|
||||
/**셀 스타일 정보
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Style {
|
||||
/** 빈 스타일 */
|
||||
public static final Style NONE = new Style().seal();
|
||||
/** 좌측 정렬 */
|
||||
public static final Style LEFT = new Style().alignment(HorizontalAlignment.LEFT).seal();
|
||||
/** 가운데 정렬 */
|
||||
public static final Style CENTER = new Style().alignment(HorizontalAlignment.CENTER).seal();
|
||||
/** 우측 정렬 */
|
||||
public static final Style RIGHT = new Style().alignment(HorizontalAlignment.RIGHT).seal();
|
||||
/**숫자 스타일(오른쪽 정렬, #,##0) */
|
||||
public static final Style N_NN0 = new Style().dataFormat("#,##0").merge(RIGHT).seal();
|
||||
/**숫자 스타일(오른쪽 정렬, #,###.00) */
|
||||
public static final Style N_NN0_2 = new Style().dataFormat("#,###.00").merge(RIGHT).seal();
|
||||
/**날짜시간 포맷(yyyy-MM-dd HH:mm:ss, 중앙 정렬) 스타일 */
|
||||
public static final Style YYYY_MM_DD_HH_MM_SS = new Style().dataFormat("yyyy-MM-dd HH:mm:ss").merge(CENTER).seal();
|
||||
/**날짜 포맷(yyyy-MM-dd, 중앙 정렬) 스타일 */
|
||||
public static final Style YYYY_MM_DD = new Style().dataFormat("yyyy-MM-dd").merge(CENTER).seal();
|
||||
/**시간 포맷(HH:mm:ss, 중앙 정렬) 스타일 */
|
||||
public static final Style HH_MM_SS = new Style().dataFormat("HH:mm:ss").merge(Style.CENTER).seal();
|
||||
|
||||
private boolean sealed;
|
||||
|
||||
private Integer width;
|
||||
private Short height;
|
||||
|
||||
private HorizontalAlignment horizontalAlignment;
|
||||
private VerticalAlignment verticalAlignment;
|
||||
|
||||
private Border
|
||||
borderTop,
|
||||
borderRight,
|
||||
borderBottom,
|
||||
borderLeft;
|
||||
|
||||
private Short foregroundColor;
|
||||
|
||||
private Font font;
|
||||
private String dataFormat;
|
||||
|
||||
/**열의 폭을 설정한다.
|
||||
* @param chars 열의 폭. 한번에 보여지는 문자수
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style width(int chars) {
|
||||
this.width = chars;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**행의 높이를 설정한다.
|
||||
* @param height 행의 높이
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style height(short height) {
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**가로정렬을 설정한다.
|
||||
* @param alignment 가로정렬
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style alignment(HorizontalAlignment alignment) {
|
||||
ensureNotSealed();
|
||||
horizontalAlignment = alignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**세로정렬을 설정한다.
|
||||
* @param alignment 세로정렬
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style alignment(VerticalAlignment alignment) {
|
||||
ensureNotSealed();
|
||||
verticalAlignment = alignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**윗쪽 테두리 스타일을 설정한다.
|
||||
* @param border 테두리 스타일 정보
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style borderTop(Border border) {
|
||||
ensureNotSealed();
|
||||
borderTop = border;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**오른쪽 테두리 스타일을 설정한다.
|
||||
* @param border 테두리 스타일 정보
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style borderRight(Border border) {
|
||||
ensureNotSealed();
|
||||
borderRight = border;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**아래쪽 테두리 스타일을 설정한다.
|
||||
* @param border 테두리 스타일 정보
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style borderBottom(Border border) {
|
||||
ensureNotSealed();
|
||||
borderBottom = border;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**왼쪽 테두리 스타일을 설정한다.
|
||||
* @param border 테두리 스타일 정보
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style borderLeft(Border border) {
|
||||
ensureNotSealed();
|
||||
borderLeft = border;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**배경색을 설정한다.
|
||||
* @param color
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style foregroundColor(short color) {
|
||||
ensureNotSealed();
|
||||
foregroundColor = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**폰트를 설정한다.
|
||||
* @param font 폰트
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style font(Font font) {
|
||||
ensureNotSealed();
|
||||
this.font = font;
|
||||
return this;
|
||||
}
|
||||
|
||||
String dataFormat() {
|
||||
return dataFormat;
|
||||
}
|
||||
|
||||
/**데이터 포맷을 설정한다.
|
||||
* @param dataFormat 데이터 포맷
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style dataFormat(String dataFormat) {
|
||||
ensureNotSealed();
|
||||
this.dataFormat = dataFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**셀스타일에 현재 스타일 정보를 설정한다.
|
||||
* @param style 셀스타일
|
||||
*/
|
||||
public void set(CellStyle style) {
|
||||
if (style == null) return;
|
||||
|
||||
if (horizontalAlignment != null)
|
||||
style.setAlignment(horizontalAlignment);
|
||||
if (verticalAlignment != null)
|
||||
style.setVerticalAlignment(verticalAlignment);
|
||||
|
||||
if (borderTop != null) {
|
||||
if (borderTop.style != null)
|
||||
style.setBorderTop(borderTop.style);
|
||||
if (borderTop.color != null)
|
||||
style.setTopBorderColor(borderTop.color);
|
||||
}
|
||||
if (borderRight != null) {
|
||||
if (borderRight.style != null)
|
||||
style.setBorderRight(borderRight.style);
|
||||
if (borderRight.color != null)
|
||||
style.setRightBorderColor(borderRight.color);
|
||||
}
|
||||
if (borderBottom != null) {
|
||||
if (borderBottom.style != null)
|
||||
style.setBorderBottom(borderBottom.style);
|
||||
if (borderBottom.color != null)
|
||||
style.setBottomBorderColor(borderBottom.color);
|
||||
}
|
||||
if (borderLeft != null) {
|
||||
if (borderLeft.style != null)
|
||||
style.setBorderLeft(borderLeft.style);
|
||||
if (borderLeft.color != null)
|
||||
style.setLeftBorderColor(borderLeft.color);
|
||||
}
|
||||
if (foregroundColor != null) {
|
||||
style.setFillForegroundColor(foregroundColor);
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
}
|
||||
|
||||
if (font != null)
|
||||
style.setFont(font);
|
||||
}
|
||||
|
||||
/**셀 스타일을 설정하는 함수를 설정한다.
|
||||
* @param configurer 셀 스타일을 설정하는 함수
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style configure(Consumer<Style> configurer) {
|
||||
ensureNotSealed();
|
||||
if (configurer != null)
|
||||
configurer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**현재 스타일에 지정하는 스타일을 병합하여 반환한다.
|
||||
* @param other 스타일
|
||||
* @return 현재 Style
|
||||
*/
|
||||
public Style merge(Style other) {
|
||||
Style copy = new Style();
|
||||
|
||||
copy.width = this.width;
|
||||
copy.height = this.height;
|
||||
|
||||
copy.horizontalAlignment = this.horizontalAlignment;
|
||||
copy.verticalAlignment = this.verticalAlignment;
|
||||
|
||||
copy.borderTop = this.borderTop;
|
||||
copy.borderRight = this.borderRight;
|
||||
copy.borderBottom = this.borderBottom;
|
||||
copy.borderLeft = this.borderLeft;
|
||||
|
||||
copy.foregroundColor = this.foregroundColor;
|
||||
copy.font = this.font;
|
||||
copy.dataFormat = this.dataFormat;
|
||||
|
||||
if (other.width != null)
|
||||
copy.width = other.width;
|
||||
if (other.height != null)
|
||||
copy.height = other.height;
|
||||
|
||||
if (other.horizontalAlignment != null)
|
||||
copy.horizontalAlignment = other.horizontalAlignment;
|
||||
if (other.verticalAlignment != null)
|
||||
copy.verticalAlignment = other.verticalAlignment;
|
||||
|
||||
if (other.borderTop != null)
|
||||
copy.borderTop = other.borderTop;
|
||||
if (other.borderRight != null)
|
||||
copy.borderRight = other.borderRight;
|
||||
if (other.borderBottom != null)
|
||||
copy.borderBottom = other.borderBottom;
|
||||
if (other.borderLeft != null)
|
||||
copy.borderLeft = other.borderLeft;
|
||||
|
||||
if (other.foregroundColor != null)
|
||||
copy.foregroundColor = other.foregroundColor;
|
||||
if (other.font != null)
|
||||
copy.font = other.font;
|
||||
if (other.dataFormat != null)
|
||||
copy.dataFormat = other.dataFormat;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
private Style seal() {
|
||||
sealed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ensureNotSealed() {
|
||||
if (sealed)
|
||||
throw new RuntimeException("The Style is sealed and unmodifieable");
|
||||
}
|
||||
|
||||
/**테두리 스타일 정보
|
||||
* @author mjkhan
|
||||
*/
|
||||
public static class Border {
|
||||
private BorderStyle style;
|
||||
private Short color;
|
||||
|
||||
/**테두리 스타일 정보를 설정한다.
|
||||
* @param style 테두리 스타일 정보
|
||||
* @return 현재 테두리 스타일 정보
|
||||
*/
|
||||
public Border style(BorderStyle style) {
|
||||
this.style = style;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**테두리 색깔을 설정한다.
|
||||
* @param color 테두리 색깔
|
||||
* @return 현재 테두리 스타일 정보
|
||||
*/
|
||||
public Border color(short color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.poi.xssf.streaming.SXSSFCell;
|
||||
import org.apache.poi.xssf.streaming.SXSSFRow;
|
||||
import org.apache.poi.xssf.streaming.SXSSFSheet;
|
||||
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
||||
|
||||
import cokr.xit.foundation.AbstractComponent;
|
||||
|
||||
/**엑셀 스프레드시트 처리를 지원하는 베이스 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public abstract class XLS extends AbstractComponent {
|
||||
/**엑셀 파일(*.xlsx)의 mime type */
|
||||
public static final String MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
/**메모리에 적재하는 row의 갯수*/
|
||||
protected int rowAccessWindowSize = 128;
|
||||
/**워크북*/
|
||||
protected SXSSFWorkbook workbook;
|
||||
/**현재 처리 중인 워크시트*/
|
||||
protected SXSSFSheet worksheet;
|
||||
/**현재 처리 중인 row*/
|
||||
protected SXSSFRow row;
|
||||
/**현재 처리 중인 cell*/
|
||||
protected SXSSFCell cell;
|
||||
|
||||
/**워크북을 반환한다.
|
||||
* @return 워크북
|
||||
*/
|
||||
public SXSSFWorkbook workbook() {
|
||||
if (workbook != null)
|
||||
return workbook;
|
||||
|
||||
try {
|
||||
workbook = newWorkbook();
|
||||
workbook.setCompressTempFiles(true);
|
||||
return workbook;
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**워크북을 생성하여 반환한다.
|
||||
* @return 워크북
|
||||
* @throws Exception
|
||||
*/
|
||||
protected SXSSFWorkbook newWorkbook() throws Exception {
|
||||
return new SXSSFWorkbook(rowAccessWindowSize);
|
||||
}
|
||||
|
||||
/**지정한 인덱스의 워크시트를 반환한다.
|
||||
* 워크시트가 없으면 해당 인덱스로 워크시트를 생성 후 반환한다.
|
||||
* @param index 워크시트 인덱스(0부터 시작)
|
||||
* @return 워크시트
|
||||
*/
|
||||
public XLS worksheet(int index) {
|
||||
SXSSFSheet sheet = null;
|
||||
try {
|
||||
sheet = workbook().getSheetAt(index);
|
||||
} catch (Exception e) {
|
||||
sheet = workbook().createSheet();
|
||||
sheet.setRandomAccessWindowSize(rowAccessWindowSize);
|
||||
}
|
||||
|
||||
if (!sheet.equals(worksheet)) {
|
||||
clear(false);
|
||||
worksheet = sheet;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 이름의 워크시트를 반환한다.
|
||||
* 워크시트가 없으면 해당 이름의 워크시트를 생성 후 반환한다.
|
||||
* @param name 워크시트 이름
|
||||
* @return 워크시트
|
||||
*/
|
||||
public XLS worksheet(String name) {
|
||||
SXSSFSheet sheet = null;
|
||||
try {
|
||||
SXSSFWorkbook wb = workbook();
|
||||
sheet = wb.getSheet(name);
|
||||
if (sheet == null) {
|
||||
sheet = wb.createSheet(name);
|
||||
sheet.setRandomAccessWindowSize(rowAccessWindowSize);
|
||||
}
|
||||
|
||||
if (!sheet.equals(worksheet)) {
|
||||
clear(false);
|
||||
worksheet = sheet;
|
||||
}
|
||||
|
||||
return this;
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**지정한 인덱스의 행(row)을 작업행으로 설정한다.
|
||||
* 해당 행이 없으면 생성 후 작업행으로 설정한다.
|
||||
* @param index 행 인덱스(0부터 시작)
|
||||
* @return 현재 XLS
|
||||
*/
|
||||
public XLS row(int index) {
|
||||
row = ifEmpty(worksheet.getRow(index), () -> worksheet.createRow(index));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**현재 작업행의 지정한 인덱스의 열(column)을 작업셀로 설정한다.
|
||||
* 해당 셀이 없으면 생성 후 작업셀로 설정한다.
|
||||
* @param index 열 인덱스(0부터 시작)
|
||||
* @return 현재 XLS
|
||||
*/
|
||||
public XLS col(int index) {
|
||||
cell = ifEmpty(row.getCell(index), () -> row.createCell(index));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 행과 열 인덱스의 셀을 작업셀로 설정한다.
|
||||
* @param row 행 인덱스
|
||||
* @param col 열 인덱스
|
||||
* @return 현재 XLS
|
||||
*/
|
||||
public XLS cell(int row, int col) {
|
||||
return row(row).col(col);
|
||||
}
|
||||
|
||||
/**현재 XLS의 row, cell, worksheet 등 내부 상태를 초기화한다.
|
||||
* @param all 초기화에 워크북 포함 여부
|
||||
* <ul><li>워크북을 포함한 모두를 초기화하려면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
protected void clear(boolean all) {
|
||||
cell = null;
|
||||
row = null;
|
||||
worksheet = null;
|
||||
if (all)
|
||||
workbook = null;
|
||||
rowAccessWindowSize = 128;
|
||||
}
|
||||
|
||||
/**이미지 추가 유틸리티
|
||||
* @author mjkhan
|
||||
*/
|
||||
public static class Image {
|
||||
private XLS xlsx;
|
||||
|
||||
/**새 Image를 생성한다.
|
||||
* @param xlsx XLS
|
||||
*/
|
||||
public Image(XLS xlsx) {
|
||||
this.xlsx = xlsx;
|
||||
}
|
||||
|
||||
/**지정한 이미지 파일을 엑셀 파일에 추가한다.
|
||||
* @param input 이미지 파일 InputStream
|
||||
* @param picType {@link SXSSFWorkbook 이미지 타입(SXSSFWorkbook.PICTURE_TYPE_XXX)}
|
||||
* @return 이미지 파일 추가 후 인덱스(1부터 시작)
|
||||
*/
|
||||
public int add(InputStream input, int picType) {
|
||||
try {
|
||||
return xlsx.workbook.addPicture(
|
||||
input.readAllBytes(),
|
||||
picType
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**지정한 경로의 이미지 파일을 엑셀 파일에 추가한다.
|
||||
* @param path 이미지 파일 경로
|
||||
* @return 이미지 파일 추가 후 인덱스(1부터 시작)
|
||||
*/
|
||||
public int add(String path) {
|
||||
try (FileInputStream input = new FileInputStream(path)) {
|
||||
return add(input, pictureType(path));
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private int pictureType(String path) {
|
||||
String str = path.toLowerCase();
|
||||
return
|
||||
str.endsWith(".png") ? SXSSFWorkbook.PICTURE_TYPE_PNG :
|
||||
str.endsWith(".emf") ? SXSSFWorkbook.PICTURE_TYPE_EMF :
|
||||
str.endsWith(".wmf") ? SXSSFWorkbook.PICTURE_TYPE_WMF :
|
||||
str.endsWith(".pict") ? SXSSFWorkbook.PICTURE_TYPE_PICT :
|
||||
str.endsWith(".dib") ? SXSSFWorkbook.PICTURE_TYPE_DIB :
|
||||
SXSSFWorkbook.PICTURE_TYPE_JPEG;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
/**엑셀 파일 작성 클래스
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class XLSWriter extends XLS {
|
||||
@Override
|
||||
public XLSWriter worksheet(int index) {
|
||||
super.worksheet(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XLSWriter worksheet(String name) {
|
||||
super.worksheet(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XLSWriter row(int index) {
|
||||
super.row(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XLSWriter col(int index) {
|
||||
super.col(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XLSWriter cell(int row, int col) {
|
||||
return row(row).col(col);
|
||||
}
|
||||
|
||||
/**지정한 범위의 셀들을 병합한다.
|
||||
* @param firstRow 시작행 인덱스
|
||||
* @param lastRow 끝행 인덱스
|
||||
* @param firstCol 시작열 인덱스
|
||||
* @param lastCol 끝열 인덱스
|
||||
* @return 현재 XLSXWriter
|
||||
*/
|
||||
public XLSWriter merge(int firstRow, int lastRow, int firstCol, int lastCol) {
|
||||
worksheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**현재 행의 지정한 열의 셀들을 병합한다.
|
||||
* @param firstCol 시작열 인덱스
|
||||
* @param lastCol 끝열 인덱스
|
||||
* @return 현재 XLSXWriter
|
||||
*/
|
||||
public XLSWriter merge(int firstCol, int lastCol) {
|
||||
int index = row.getRowNum();
|
||||
return merge(index, index, firstCol, lastCol);
|
||||
}
|
||||
|
||||
/**현재 행에서 열을 증가시키면서 주어진 값들을 설정한다.
|
||||
* @param objs 설정값
|
||||
* @return 현재 XLSWriter
|
||||
*/
|
||||
public XLSWriter rowValues(Iterable<?> objs) {
|
||||
return rowValues(objs, (Iterable<Object>)null);
|
||||
}
|
||||
|
||||
private <T> XLSWriter rowValues(T t, Iterable<Object> objs) {
|
||||
if (!isEmpty(t)) {
|
||||
int c = cell.getColumnIndex();
|
||||
if (isEmpty(objs)) {
|
||||
if (t instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>)t;
|
||||
objs = map.keySet().stream().map(v -> (Object)v).toList();
|
||||
} else if (t instanceof Iterable) {
|
||||
objs = (Iterable<Object>)t;
|
||||
} else {
|
||||
objs = List.of(t);
|
||||
}
|
||||
}
|
||||
|
||||
for (Object obj: objs) {
|
||||
boolean hasValue = Format.hasValue(obj);
|
||||
if (hasValue)
|
||||
col(c);
|
||||
|
||||
Object val = null;
|
||||
Format format = null;
|
||||
if (obj instanceof Format)
|
||||
format = (Format)obj;
|
||||
|
||||
if (format != null)
|
||||
val = format.value(t);
|
||||
else {
|
||||
Object key = Format.getKey(obj);
|
||||
if (key != null && t instanceof Map)
|
||||
val = ((Map<?, ?>)t).get(key);
|
||||
else
|
||||
val = obj;
|
||||
}
|
||||
|
||||
setCellValue(val);
|
||||
setCellStyle(obj);
|
||||
if (format != null)
|
||||
format.onCell(t);
|
||||
|
||||
if (hasValue)
|
||||
++c;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 데이터셋의 값들을 현재 작업셀부터 행과 열로 설정한다.
|
||||
* 설정할 값과 순서를 정하려면 해당 값들의 키를 지정한다.<br />
|
||||
* 값이 설정된 셀에 스타일을 적용하려면 {@link Format}로 키와 스타일을 지정한다.<br />
|
||||
* 다음은 데이터셋의 값들을 설정하는 예다.
|
||||
* <pre><code> List<MyObject> dataset = ...;
|
||||
* XLSWriter xlsx = new XLSWriter()
|
||||
* .worksheet(0) // 워크시트 지정
|
||||
* .cell(0, 0) // 작업셀 지정
|
||||
* .values(
|
||||
* dataset,
|
||||
* myObj -> List.of(
|
||||
* myObj.getPropA(),
|
||||
* myObj.getPropB(),
|
||||
* myObj.getPropC()
|
||||
* )
|
||||
* );</code></pre>
|
||||
* PropB, PropC에 숫자포맷과 날짜포맷을 적용하려면 다음과 같이 한다.
|
||||
* <pre><code> Format format = new Format(xlsx);
|
||||
* CellStyle numeric = format.n_nn0();
|
||||
* CellStyle datetime = format.yyyy_mm_dd_hh_mm_ss();
|
||||
* ...
|
||||
* xlsx.values(
|
||||
* dataset,
|
||||
* myObj -> List.of(
|
||||
* myObj.getPropA(),
|
||||
* myObj.getPropB(),
|
||||
* numeric,
|
||||
* myObj.getPropC(),
|
||||
* datetime
|
||||
* )
|
||||
* );</code></pre>
|
||||
* @param dataset 데이터셋
|
||||
* @param mapper 셀에 설정할 값, Style, 또는 CellStyle들을 반환하는 함수
|
||||
* @return 현재 XLSXWriter
|
||||
*/
|
||||
public <T extends Iterable<?>> XLSWriter values(Iterable<T> dataset, Function<T, Iterable<Object>> mapper) {
|
||||
if (!isEmpty(dataset)) {
|
||||
int r = cell.getRowIndex(),
|
||||
c = cell.getColumnIndex();
|
||||
|
||||
for (T rec: dataset) {
|
||||
cell(r, c);
|
||||
Iterable<Object> vals = mapper != null ? mapper.apply(rec) : (Iterable<Object>)rec;
|
||||
rowValues(rec, vals);
|
||||
++r;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 데이터셋의 값들을 현재 작업셀부터 행과 열로 설정한다.
|
||||
* 설정할 값과 순서를 정하려면 해당 값들의 키를 지정한다.<br />
|
||||
* 값이 설정된 셀에 스타일을 적용하려면 {@link Format}로 키와 스타일을 지정한다.<br />
|
||||
* 다음은 데이터셋의 모든 값들을 설정하는 예다.
|
||||
* <pre><code> List<DataObject> dataset = ...;
|
||||
* XLSWriter xlsx = new XLSWriter()
|
||||
* .worksheet(0) // 워크시트 지정
|
||||
* .cell(0, 0) // 작업셀 지정
|
||||
* .values(dataset);</code></pre>
|
||||
* 설정할 값들을 지정하려면 다음과 같이 한다.
|
||||
* <pre><code> xlsx.values(dataset, "PAYER", "RCPT_AMT", "RCPT_DT");</code></pre>
|
||||
* RCPT_AMT, RCPT_DT에 숫자포맷과 날짜포맷을 적용하려면 다음과 같이 한다.
|
||||
* <pre><code> Format format = new Format(xlsx);
|
||||
* CellStyle numeric = format.n_nn0();
|
||||
* CellStyle datetime = format.yyyy_mm_dd_hh_mm_ss();
|
||||
* ...
|
||||
* xlsx.values(dataset, "PAYER", "RCPT_AMT", numeric, "RCPT_DT", datetime));</code></pre>
|
||||
* @param dataset 데이터셋
|
||||
* @param objs 셀에 설정할 값들의 키, CellStyle, {@link Style}, 또는 {@link Format}
|
||||
* @return 현재 XLSXWriter
|
||||
*/
|
||||
public <T extends Map<?, ?>> XLSWriter values(Iterable<T> dataset, Object... objs) {
|
||||
if (!isEmpty(dataset)) {
|
||||
int r = cell.getRowIndex(),
|
||||
c = cell.getColumnIndex();
|
||||
List<Object> list = !isEmpty(objs) ? List.of(objs) : Collections.emptyList();
|
||||
|
||||
for (T row: dataset) {
|
||||
cell(r, c);
|
||||
rowValues(row, list);
|
||||
++r;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**지정한 Map의 데이터들을 현재 행과 열에 설정한다.
|
||||
* 설정할 값과 순서를 정하려면 해당 값들의 키를 지정한다.<br />
|
||||
* 값이 설정된 셀에 스타일을 적용하려면 {@link Format}로 키와 스타일을 지정한다.<br />
|
||||
* 다음은 데이터셋의 모든 값들을 설정하는 예다.
|
||||
* <pre><code> DataObject row = ...;
|
||||
* XLSWriter xlsx = new XLSWriter()
|
||||
* .worksheet(0) // 워크시트 지정
|
||||
* .cell(0, 0) // 작업셀 지정
|
||||
* .rowValues(row);</code></pre>
|
||||
* 설정할 값들을 지정하려면 다음과 같이 한다.
|
||||
* <pre><code> xlsx.rowValues(row, "PAYER", "RCPT_AMT", "RCPT_DT");</code></pre>
|
||||
* RCPT_AMT, RCPT_DT에 숫자포맷과 날짜포맷을 적용하려면 다음과 같이 한다.
|
||||
* <pre><code> Format format = new Format(xlsx);
|
||||
* CellStyle numeric = format.n_nn0();
|
||||
* CellStyle datetime = format.yyyy_mm_dd_hh_mm_ss();
|
||||
* ...
|
||||
* xlsx.rowValues(row, "PAYER", "RCPT_AMT", numeric, "RCPT_DT", datetime));</code></pre>
|
||||
* @param map 맵
|
||||
* @param objs 셀에 설정할 값들의 키, CellStyle, {@link Style}, 또는 {@link Format}
|
||||
* @return 현재 XLSWriter
|
||||
*/
|
||||
public XLSWriter rowValues(Map<?, ?> map, Object... objs) {
|
||||
return rowValues(map, !isEmpty(objs) ? List.of(objs) : Collections.emptyList());
|
||||
}
|
||||
|
||||
/**지정한 값을 현재 작업셀에 설정하고 스타일을 적용한다.
|
||||
* @param obj 설정값
|
||||
* @return 현재 XLSXWriter
|
||||
*/
|
||||
public XLSWriter value(Object obj) {
|
||||
if (obj != null) {
|
||||
setCellValue(obj);
|
||||
setCellStyle(obj);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void setCellValue(Object val) {
|
||||
if (val instanceof String) {
|
||||
cell.setCellValue((String)val);
|
||||
} else if (val instanceof Number) {
|
||||
Number number = (Number)val;
|
||||
cell.setCellValue(number.doubleValue());
|
||||
} else if (val instanceof Date) {
|
||||
cell.setCellValue((Date)val);
|
||||
} else if (val instanceof Boolean) {
|
||||
Boolean b = (Boolean)val;
|
||||
cell.setCellValue(b.booleanValue());
|
||||
}
|
||||
}
|
||||
|
||||
CellStyle cellStyle(Style style) {
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
style.set(cellStyle);
|
||||
if (style.dataFormat() != null) {
|
||||
cellStyle.setDataFormat(workbook.createDataFormat().getFormat(style.dataFormat()));
|
||||
}
|
||||
return cellStyle;
|
||||
}
|
||||
|
||||
void setCellStyle(Object obj) {
|
||||
CellStyle cellStyle = null;
|
||||
if (obj instanceof Format)
|
||||
cellStyle = ((Format)obj).style();
|
||||
else if (obj instanceof Style)
|
||||
cellStyle = cellStyle((Style)obj);
|
||||
else if (obj instanceof CellStyle)
|
||||
cellStyle = (CellStyle)obj;
|
||||
|
||||
if (cellStyle != null)
|
||||
cell.setCellStyle(cellStyle);
|
||||
}
|
||||
|
||||
/**설정된 데이터와 내용을 지정한 OutputStream에 저장한다.
|
||||
* @param out OutputStream
|
||||
*/
|
||||
public void write(OutputStream out) {
|
||||
try {
|
||||
workbook.write(out);
|
||||
workbook.dispose();
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
} finally {
|
||||
clear(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**설정된 데이터와 내용을 지정한 경로의 파일에 저장한다.
|
||||
* @param filepath 파일경로
|
||||
* @return 저장한 엑셀 파일
|
||||
*/
|
||||
public File write(String filepath) {
|
||||
File file = new File(filepath);
|
||||
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||
write(out);
|
||||
return file;
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package cokr.xit.base.docs.hwp;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import cokr.xit.foundation.data.DataFormat;
|
||||
import cokr.xit.foundation.data.DataObject;
|
||||
import kr.dogfoot.hwplib.object.HWPFile;
|
||||
|
||||
public class HWPTest {
|
||||
private String path(String str, boolean delete) {
|
||||
String path = "src\\test\\resources\\" + str;
|
||||
File file = new File(path);
|
||||
if (delete && file.exists())
|
||||
file.delete();
|
||||
return path;
|
||||
}
|
||||
|
||||
@Test
|
||||
void setValue() {
|
||||
HWPFile file = HWPWriter.filepath(path("files\\hwp-test.hwp", false));
|
||||
HWPWriter writer = new HWPWriter(file);
|
||||
/*
|
||||
TestObject tobj = new TestObject()
|
||||
.setName("홍길동")
|
||||
.setResidentNo("123456780123")
|
||||
.setAddress("서울특별시 양천구")
|
||||
.setTotalCount(23)
|
||||
.setTotalAmt(230000000);
|
||||
writer.setValues(
|
||||
tobj,
|
||||
Map.of(
|
||||
"성명", TestObject::getName,
|
||||
"주민번호", TestObject::getResidentNo,
|
||||
"주소", TestObject::getAddress,
|
||||
"총건수", TestObject::getTotalCount,
|
||||
"총금액", TestObject::getTotalAmt
|
||||
)
|
||||
);
|
||||
*/
|
||||
|
||||
writer
|
||||
.setValues(Map.of(
|
||||
"사용자", "이몽룡",
|
||||
"출력일자", DataFormat.yyyy_mm_dd("20240312"),
|
||||
|
||||
"성명", "홍길동",
|
||||
"주민번호", "1234567890123",
|
||||
"주소", "서울특별시 양천구",
|
||||
"총건수", 23,
|
||||
"총금액", DataFormat.n_nn0(230000000)
|
||||
));
|
||||
|
||||
List<DataObject> list = IntStream.range(0, 20).boxed().map(i ->
|
||||
(DataObject)new DataObject()
|
||||
.set("과태료", "과태료 " + i)
|
||||
.set("단속일시", "단속일시 " + i)
|
||||
.set("차량번호", "123가456" + (i % 10))
|
||||
.set("고지번호", "고지번호 " + i)
|
||||
.set("금액", "금액 " + i)
|
||||
.set("가상계좌번호", "가상계좌번호 " + i)
|
||||
.set("처리상태", "처리상태 " + i)
|
||||
.set("처리일자", "처리일자 " + i)
|
||||
.set("시군구명", "시군구명 " + i)
|
||||
.set("단속장소", "단속장소 " + i)
|
||||
.set("대체차량번호", "234나567" + (i % 10))
|
||||
.set("전자납부번호", "전자납부번호 " + i)
|
||||
.set("납부기한", "납부기한 " + i)
|
||||
.set("수납일자", "수납일자 " + i)
|
||||
.set("단속사진", writer.image().add(path("files\\" + (i % 2 == 0 ? "4148020220006850A.jpg" : "4148020220006869A.jpg"), false)))
|
||||
).toList();
|
||||
writer.table(2, 3, 4).setValues(list);
|
||||
//writer.header().setValue("abc");
|
||||
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(path("files\\hwp-test-result.hwp", true))) {
|
||||
writer.write(out);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class TestObject {
|
||||
private String
|
||||
name,
|
||||
residentNo,
|
||||
address;
|
||||
private int totalCount;
|
||||
private long totalAmt;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public TestObject setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getResidentNo() {
|
||||
return residentNo;
|
||||
}
|
||||
|
||||
public TestObject setResidentNo(String residentNo) {
|
||||
this.residentNo = residentNo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public TestObject setAddress(String address) {
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
public TestObject setTotalCount(int totalCount) {
|
||||
this.totalCount = totalCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getTotalAmt() {
|
||||
return totalAmt;
|
||||
}
|
||||
|
||||
public TestObject setTotalAmt(long totalAmt) {
|
||||
this.totalAmt = totalAmt;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
package cokr.xit.base.docs.xls;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import cokr.xit.foundation.data.DataObject;
|
||||
|
||||
public class XLSTest {
|
||||
private String file(String str) {
|
||||
String path = "C:\\project\\base project\\xit-docs\\src\\test\\resources\\" + str;
|
||||
File file = new File(path);
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
return path;
|
||||
}
|
||||
|
||||
@Test
|
||||
void sxssf() {
|
||||
XLSWriter xlsx = new XLSWriter().worksheet("테스트 시트");
|
||||
Format format = new Format(xlsx);
|
||||
CellStyle
|
||||
datetime = format.yyyy_mm_dd_hh_mm_ss(),
|
||||
numeric = format.n_nn0(),
|
||||
blue = format.cellStyle(new Style().foregroundColor(IndexedColors.LIGHT_BLUE.getIndex()));
|
||||
|
||||
xlsx.row(0)
|
||||
.col(0).value("테스트0")
|
||||
.col(1).value("테스트1")
|
||||
.col(2).value(new Date()).value(datetime)
|
||||
.col(3).value(1000000.0101).value(numeric)
|
||||
.cell(2, 0)
|
||||
.rowValues(List.of("테스트 1", blue, "테스트 2", 2500000, numeric));
|
||||
|
||||
List<DataObject> dataset = List.of(
|
||||
new DataObject().set("field0", "값 0, 0").set("field1", "값 0, 1").set("field2", 10000),
|
||||
new DataObject().set("field0", "값 1, 0").set("field1", "값 1, 1").set("field2", 20000),
|
||||
new DataObject().set("field0", "값 2, 0").set("field1", "값 2, 1").set("field2", 30000),
|
||||
new DataObject().set("field0", "값 3, 0").set("field1", "값 3, 1").set("field2", 40000),
|
||||
new DataObject().set("field0", "값 4, 0").set("field1", "값 4, 1").set("field2", 50000)
|
||||
);
|
||||
|
||||
xlsx.cell(4, 0)
|
||||
.values(
|
||||
dataset,
|
||||
"field0", "field1",
|
||||
"field2", numeric,
|
||||
format.of(null).value(obj -> format.toInt(((Map)obj).get("field2")) * 2).style(numeric)
|
||||
)
|
||||
// .values(dataset)
|
||||
.write(file("sxssf.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sxssf2() {
|
||||
Date now = new Date();
|
||||
List<DataObject> dataset = List.of(
|
||||
new DataObject().set("no", 1).set("title", "제목 1").set("content", "내용 1").set("amt", 1000000).set("regDate", now),
|
||||
new DataObject().set("no", 2).set("title", "제목 2").set("content", "내용 2").set("amt", 2000000).set("regDate", now),
|
||||
new DataObject().set("no", 3).set("title", "제목 3").set("content", "내용 3").set("amt", 3000000).set("regDate", now),
|
||||
new DataObject().set("no", 4).set("title", "제목 4").set("content", "내용 4").set("amt", 4000000).set("regDate", now),
|
||||
new DataObject().set("no", 5).set("title", "제목 5").set("content", "내용 5").set("amt", 5000000).set("regDate", now)
|
||||
);
|
||||
|
||||
XLSWriter xlsx = new XLSWriter().worksheet(0);
|
||||
Format format = new Format(xlsx);
|
||||
CellStyle
|
||||
datetime = format.yyyy_mm_dd_hh_mm_ss(),
|
||||
numeric = format.n_nn0();
|
||||
|
||||
xlsx.cell(5, 0)
|
||||
//.values(dataset, "no", "title", "content", "amt", numeric, "regDate", datetime);
|
||||
.values(dataset, "no", "title", "content",
|
||||
"amt", numeric,
|
||||
format.of(null).value(obj -> ((Map)obj).get("amt") + " 원"),
|
||||
"regDate", datetime
|
||||
)
|
||||
.write(file("sxssf2.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sxssf3() {
|
||||
Date now = new Date();
|
||||
List<DataObject> dataset = List.of(
|
||||
new DataObject().set("no", 1).set("title", "제목 1").set("content", "내용 1").set("amt", 1000000).set("regDate", now),
|
||||
new DataObject().set("no", 2).set("title", "제목 2").set("content", "내용 2").set("amt", 2000000).set("regDate", now),
|
||||
new DataObject().set("no", 3).set("title", "제목 3").set("content", "내용 3").set("amt", 3000000).set("regDate", now),
|
||||
new DataObject().set("no", 4).set("title", "제목 4").set("content", "내용 4").set("amt", 4000000).set("regDate", now),
|
||||
new DataObject().set("no", 5).set("title", "제목 5").set("content", "내용 5").set("amt", 5000000).set("regDate", now)
|
||||
);
|
||||
|
||||
XLSWriter xlsx = new XLSWriter().worksheet(0);
|
||||
Format format = new Format(xlsx);
|
||||
CellStyle
|
||||
datetime = format.yyyy_mm_dd_hh_mm_ss(),
|
||||
numeric = format.n_nn0();
|
||||
|
||||
List<CellDef> cellDefs = List.of(
|
||||
new CellDef().setLabel("번호").setField("no"),
|
||||
new CellDef().setLabel("제목").setField("title"),
|
||||
new CellDef().setLabel("내용").setField("content"),
|
||||
// new CellDef().setLabel("금액").setField("amt").setValue(format.of("amt").style(numeric)),
|
||||
new CellDef().setLabel("금액").setField("amt").setValue(numeric),
|
||||
// new CellDef().setLabel("등록일시").setField("regDate").setValue(format.of("regDate").style(datetime))
|
||||
new CellDef().setLabel("금액 2").setValue(format.of(null).value(obj -> ((Map)obj).get("amt") + " 원")),
|
||||
new CellDef().setLabel("등록일시").setField("regDate").setValue(datetime)
|
||||
);
|
||||
|
||||
xlsx.cell(4, 0)
|
||||
.rowValues(CellDef.header(cellDefs, null))
|
||||
.cell(5, 0)
|
||||
.values(dataset, CellDef.values(cellDefs))
|
||||
.write(file("sxssf3.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pictureComment() {
|
||||
Date now = new Date();
|
||||
String dir = "C:\\project\\base project\\xit-docs\\src\\test\\resources\\files\\",
|
||||
path1 = dir + "4148020220006850A.jpg",
|
||||
path2 = dir + "4148020220006869A.jpg";
|
||||
List<DataObject> dataset = List.of(
|
||||
new DataObject().set("no", 1).set("title", "제목 1").set("content", "내용 1").set("pic", path1).set("amt", 1000000).set("regDate", now),
|
||||
new DataObject().set("no", 2).set("title", "제목 2").set("content", "내용 2").set("pic", path2).set("amt", 2000000).set("regDate", now)
|
||||
);
|
||||
|
||||
XLSWriter xlsx = new XLSWriter()
|
||||
.worksheet(0)
|
||||
.cell(3, 0);
|
||||
Format format = new Format(xlsx);
|
||||
CellStyle
|
||||
datetime = format.yyyy_mm_dd_hh_mm_ss(),
|
||||
currency = format.cellStyle(new Style().dataFormat("#,##0 원").merge(Style.RIGHT));
|
||||
Comment comment = new Comment(xlsx);
|
||||
|
||||
xlsx.values(
|
||||
dataset, "no", "title", "content",
|
||||
format.of(null)
|
||||
.value(row -> null)
|
||||
.onCell(row -> comment.setImageComment((String)((Map)row).get("pic"))),
|
||||
"amt", currency,
|
||||
format.of("regDate").style(datetime)
|
||||
)
|
||||
.write(file("picture-comment.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoSheets() {
|
||||
Date now = new Date();
|
||||
List<DataObject> dataset = List.of(
|
||||
new DataObject().set("no", 1).set("title", "제목 1").set("content", "내용 1").set("amt", 1000000).set("regDate", now),
|
||||
new DataObject().set("no", 2).set("title", "제목 2").set("content", "내용 2").set("amt", 2000000).set("regDate", now),
|
||||
new DataObject().set("no", 3).set("title", "제목 3").set("content", "내용 3").set("amt", 3000000).set("regDate", now),
|
||||
new DataObject().set("no", 4).set("title", "제목 4").set("content", "내용 4").set("amt", 4000000).set("regDate", now),
|
||||
new DataObject().set("no", 5).set("title", "제목 5").set("content", "내용 5").set("amt", 5000000).set("regDate", now)
|
||||
);
|
||||
|
||||
XLSWriter xlsx = new XLSWriter().worksheet("첫번째 시트");
|
||||
Format format = new Format(xlsx);
|
||||
CellStyle
|
||||
datetime = format.yyyy_mm_dd_hh_mm_ss(),
|
||||
numeric = format.n_nn0();
|
||||
|
||||
List<CellDef> cellDefs = List.of(
|
||||
new CellDef().setLabel("번호").setField("no"),
|
||||
new CellDef().setLabel("제목").setField("title"),
|
||||
new CellDef().setLabel("내용").setField("content"),
|
||||
new CellDef().setLabel("금액").setField("amt").setValue(numeric),
|
||||
new CellDef().setLabel("금액 2").setValue(format.of(null).value(obj -> ((Map)obj).get("amt") + " 원")),
|
||||
new CellDef().setLabel("등록일시").setField("regDate").setValue(datetime)
|
||||
);
|
||||
|
||||
xlsx.cell(5, 0)
|
||||
.values(dataset, CellDef.values(cellDefs));
|
||||
|
||||
String dir = "C:\\project\\base project\\xit-docs\\src\\test\\resources\\files\\",
|
||||
path1 = dir + "4148020220006850A.jpg",
|
||||
path2 = dir + "4148020220006869A.jpg";
|
||||
dataset = List.of(
|
||||
new DataObject().set("no", 1).set("title", "제목 1").set("content", "내용 1").set("pic", path1).set("amt", 1000000).set("regDate", now),
|
||||
new DataObject().set("no", 2).set("title", "제목 2").set("content", "내용 2").set("pic", path2).set("amt", 2000000).set("regDate", now)
|
||||
);
|
||||
|
||||
xlsx.worksheet("두번째 시트").cell(3, 0);
|
||||
CellStyle currency = format.cellStyle(new Style().dataFormat("#,##0 원").merge(Style.RIGHT));
|
||||
Comment comment = new Comment(xlsx);
|
||||
|
||||
xlsx.values(
|
||||
dataset, "no", "title", "content",
|
||||
format.of(null)
|
||||
.value(row -> null)
|
||||
.onCell(row -> comment.setImageComment((String)((Map)row).get("pic"))),
|
||||
"amt", currency,
|
||||
format.of("regDate").style(datetime)
|
||||
)
|
||||
.write(file("two-sheets.xlsx"));
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
After Width: | Height: | Size: 297 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue