commit 74a20dfd0c38b2c716bcb0b540555761ca7d0283 Author: mjkhan21 Date: Mon Apr 1 11:17:30 2024 +0900 최초 커밋 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cc50454 --- /dev/null +++ b/pom.xml @@ -0,0 +1,182 @@ + + 4.0.0 + + cokr.xit.base + xit-docs + 23.04.01-SNAPSHOT + jar + + xit-docs + http://maven.apache.org + + + UTF-8 + + 17 + ${java.version} + ${java.version} + + 5.3.20 + 4.1.0 + + + + + mvn2s + https://repo1.maven.org/maven2/ + + true + + + true + + + + egovframe + https://maven.egovframe.go.kr/maven/ + + true + + + false + + + + maven-public + https://nas.xit.co.kr:8888/repository/maven-public/ + + + + + + + cokr.xit.base + xit-foundation + 23.04.01-SNAPSHOT + + + + commons-io + commons-io + 2.11.0 + + + + kr.dogfoot + hwplib + 1.1.1 + + + + + + install + ${basedir}/target + ${project.artifactId}-${project.version} + + + ${basedir}/src/main/resources + + + + ${basedir}/src/test/resources + ${basedir}/src/main/resources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + UTF-8 + + + + + org.codehaus.mojo + emma-maven-plugin + 1.0-alpha-3 + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.1 + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + **/*.class + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + true + xml + + **/Abstract*.java + **/*Suite.java + + + **/*Test.java + + + + + org.codehaus.mojo + emma-maven-plugin + true + + + org.apache.maven.plugins + maven-source-plugin + 2.2 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + + + + + + + maven-snapshot + https://nas.xit.co.kr:8888/repository/maven-snapshots/ + + + + maven-release + https://nas.xit.co.kr:8888/repository/maven-releases/ + + + + + \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/hwp/HWP.java b/src/main/java/cokr/xit/base/docs/hwp/HWP.java new file mode 100644 index 0000000..e6fe317 --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/hwp/HWP.java @@ -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 getControls(ControlType type) { + return ControlFinder.find(file, (control, paragraph, section) -> type == control.getType()); + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/hwp/HWPWriter.java b/src/main/java/cokr/xit/base/docs/hwp/HWPWriter.java new file mode 100644 index 0000000..0cb4982 --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/hwp/HWPWriter.java @@ -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)objs); + return this; + } + + ArrayList 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 객체유형 + * @param t 객체 + * @param mappers 필드/누름틀 이름을 키로하고, 해당 이름에 해당하는 값을 반환하는 함수를 값으로 하는 맵 + * @return 현재 HWPWriter + */ + public HWPWriter setValues(T t, Map> 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; + } + + > Map> 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() : ""); + } + + > void setValues(ParagraphList paragraphs, T obj, Map> mappers) throws Exception { + for (Paragraph paragraph: paragraphs) { + ArrayList 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 mapper = !Assert.isEmpty(fieldName) ? mappers.get(fieldName) : null; + if (mapper == null) continue; + + Object val = mapper.apply(obj); + ArrayList 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/hwp/Header.java b/src/main/java/cokr/xit/base/docs/hwp/Header.java new file mode 100644 index 0000000..8519d88 --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/hwp/Header.java @@ -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 headers() { + return writer.getControls(ControlType.Header); + } + + private ControlHeader header() { + List 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/hwp/Image.java b/src/main/java/cokr/xit/base/docs/hwp/Image.java new file mode 100644 index 0000000..c305edc --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/hwp/Image.java @@ -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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/hwp/Table.java b/src/main/java/cokr/xit/base/docs/hwp/Table.java new file mode 100644 index 0000000..ccad5b8 --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/hwp/Table.java @@ -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 객체유형 + * @param objs 설정할 데이터를 가지고 있는 객체 목록 + * @param mappers 객체 하나에서 설정할 데이터를 반환하는 함수들로 된 맵. 맵의 키는 데이터 행의 필드/누름틀 이름이어야 한다. + * @return 현재 Image와 연결된 HWPWriter + */ + public HWPWriter setValues(Iterable objs, Map> mappers) { + if (Assert.isEmpty(objs)) return writer; + Assert.notEmpty(mappers, "mappers"); + + ControlTable tableControl = Assert.notEmpty(getTable(index), "표 " + index); + ArrayList + 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 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 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 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 cellCounts = table.getCellCountOfRowList(); + cellCounts.clear(); + + for (int i = 0; i < rowCount; ++i) { + ArrayList 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 renderer = (BiConsumer)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 found = writer.getControls(ControlType.Table); + return !found.isEmpty() ? (ControlTable)found.get(index) : null; + } + + /**지정한 맵의 값들을 테이블의 데이터로 설정한다. + * @param 맵 유형 + * @param dataset 맵. 맵의 키는 데이터 행의 필드/누름틀 이름이어야 한다. + * @return 현재 Image와 연결된 HWPWriter + */ + public > HWPWriter setValues(Iterable dataset) { + if (Assert.isEmpty(dataset)) return writer; + + T map = dataset.iterator().next(); + return setValues(dataset, writer.getMappers(map)); + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/docs/xls/CellDef.java b/src/main/java/cokr/xit/base/docs/xls/CellDef.java new file mode 100644 index 0000000..99c3401 --- /dev/null +++ b/src/main/java/cokr/xit/base/docs/xls/CellDef.java @@ -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; + +/**셀 정보 + *
  • 셀 레이블
  • + *
  • 데이터 필드(키) 이름
  • + *
  • + *
  • 데이터 또는 스타일
  • + *
+ * @author mjkhan + */ +public class CellDef { + /**CellDef 목록에서 셀헤더 이름과 스타일을 반환한다. + * @param defs CellDef 목록 + * @param factory 헤더 스타일을 제공하는 function + * @return 셀헤더 이름과 스타일 목록 + */ + public static List header(List defs, Supplier