package com.fdkankan.dxf.generate; import com.fdkankan.dxf.generate.model.*; import com.fdkankan.dxf.generate.model.base.BaseDxfEntity; import com.fdkankan.dxf.generate.model.base.DxfEntity; import com.fdkankan.dxf.generate.model.support.DxfExtent; import com.fdkankan.dxf.generate.util.DxfUtil; import com.fdkankan.dxf.generate.util.FreemarkerSupport; import com.fdkankan.dxf.generate.util.StreamUtil; import com.fdkankan.dxf.generate.util.StringUtil; import java.io.*; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; /** * Dxf处理类,支持向矿图中添加自定义图形,目前支持的图形有圆形Circle,圆弧Arc,直线Line,多线段LwPolyLine,文字Text 目前仅仅支持对圆形Circle和其子类Arc的颜色填充。 可以通过调用 {@link #addEntity(DxfEntity)} 和 {@link #removeEntity(DxfEntity)} 进行自定义图元的添加和删除。 *

* 并支持对新dxf文件的导出,可以选择调用函数 {@link #save(String, boolean)} 导出所有文件,这样的文件可以在AutoCad中打开 也可以通过函数 {@link #saveEntities(String, boolean)} 仅仅导出entity图元信息,这样的文件仅仅支持使用解析库进行解析 *

* 另外和文件流一样,dxfDocument对象也需要在使用结束后关闭,通过调用 {@link #close()} 函数进行关闭,可以以想流式对象那样使用,如

* *
 *     try(DxfDocument dxf = new DxfDocument(path)) {
 *         body
 *     }
 * 
* *
* * @author YTZJJ */ @SuppressWarnings("unused") public class DxfDocWriter implements Closeable { /** * 当选择保存为dxf文件的时候,要保留的Entities类型的数组,不在数组中的对象将会被忽略掉(不包含自定义加入的图元) */ public static final String[] DEFAULT_ENTITY_NO_REDUCE_PART = {"LINE", "CIRCLE", "ARC", "TEXT", "MTEXT", "LWPOLYLINE"}; private List entityNoReducePart = Arrays.asList(DEFAULT_ENTITY_NO_REDUCE_PART); public List newDxfEntityList; private long maxMeta; private Charset charset; private DxfExtent extent; private BufferedReader br; private File dxfFile; /** * 加载dxf文件,默认的文件加载编码为UFT8 * * @param dxfFilePath dxf文件位置 */ public DxfDocWriter(String dxfFilePath, DxfExtent extent) { this(dxfFilePath, extent, StandardCharsets.UTF_8); } /** * 以指定编码格式加载dxf文件 * * @param dxfFilePath dxf文件位置 * @param charset 文件编码 * @see StandardCharsets */ public DxfDocWriter(String dxfFilePath, DxfExtent extent, Charset charset) { this.charset = charset; this.extent = extent; newDxfEntityList = new ArrayList<>(); if (dxfFilePath != null) { this.dxfFile = new File(dxfFilePath); try { this.br = StreamUtil.getFileReader(dxfFile.getAbsolutePath(), charset); } catch (FileNotFoundException e) { System.err.println("file " + dxfFilePath + "not exists" + e.getMessage()); } maxMeta = DxfUtil.readMaxMeta(dxfFilePath); } else { maxMeta = Long.parseLong("1F6", 16); } } /** * 创建一个空的dxf文件 */ public DxfDocWriter() { this(null, null, StandardCharsets.UTF_8); } /** * 向矿图文件中添加一个图元,目前仅仅支持 {@link DxfCircle},{@link DxfArc},{@link DxfLine},{@link DxfLwPolyLine},{@link DxfText} *

* 也可以通过继承 {@link BaseDxfEntity} 或实现 {@link DxfEntity} 接口来扩展图元 * * @param dxfEntity 图元 */ public void addEntity(DxfEntity dxfEntity) { if (dxfEntity instanceof DxfRay) { if (Vector3.ZERO.equals(((DxfRay) dxfEntity).getDirection())) { System.err.println(dxfEntity.getEntityName() + " direction cant be zero!!, will ignore this entity"); return; } } if (dxfEntity instanceof DxfLwPolyLine) { if (((DxfLwPolyLine) dxfEntity).isEmpty()) { System.err.println("LwPolyLine not contains any point, will ignore this entity"); return; } } dxfEntity.setMeta(++maxMeta); this.newDxfEntityList.add(dxfEntity); if (dxfEntity instanceof DxfCircle || dxfEntity instanceof DxfLwPolyLine) { if (((BaseDxfEntity) dxfEntity).isSolid()) { addEntity(DxfHatch.buildHatchBy((BaseDxfEntity) dxfEntity)); } } } /** * 移除一个自定义的图元 * * @param dxfEntity 图元 */ public void removeEntity(DxfEntity dxfEntity) { this.newDxfEntityList.remove(dxfEntity); for (DxfEntity entity : newDxfEntityList) { if (entity instanceof DxfHatch) { if (((DxfHatch) dxfEntity).getDxfSolid().getDxfEntity() == dxfEntity) { this.newDxfEntityList.remove(dxfEntity); } } } } /** * 设置要保存的矿图文件中要保留的图元类型列表,如果设置为 null, 那么原矿图中的所有图元都将会被保留 默认的要保存的图元列表表为 {@link #DEFAULT_ENTITY_NO_REDUCE_PART} * * @param entityNoReducePart 要保留的图元列表 */ public void setEntityNoReducePart(String... entityNoReducePart) { if (entityNoReducePart == null) { this.entityNoReducePart = null; } else { this.entityNoReducePart = Arrays.asList(entityNoReducePart); } } /** * 仅仅保存矿图中的图元信息(ENTITIES 部分)到文件中,其他部分(HEADER, CLASSES, TABLES...)都将被舍弃 通过 {@link #setEntityNoReducePart(String...)} 可以自定义保留那些类型的图元 * * @param entitiesFilePath 要保存的文件位置 * @param containNewEntity 是否包含自定义添加进去的图元信息 */ public void saveEntities(String entitiesFilePath, boolean containNewEntity) { save(entitiesFilePath, true, containNewEntity); } /** * 保存为新的dxf文件 * * @param saveDxfFilePath 要保存的问题件位置 * @param containNewEntity 是否报刊自定义添加进去的图元信息 */ public void save(String saveDxfFilePath, boolean containNewEntity) { save(saveDxfFilePath, false, containNewEntity); } public String readString(boolean containNewEntity) { StringBuilder result = new StringBuilder(); read(false, containNewEntity, result::append); return result.toString(); } private void read(boolean justSaveEntity, boolean containNewEntity, ReadDxfConsumer readStr) { FreemarkerSupport.getInstance(); try { if (dxfFile != null) { br.mark((int) (dxfFile.length() + 1)); } else { String value = FreemarkerSupport.getInstance().processPath("/dxf/empty.dxf.ftl", extent); br = StreamUtil.getReader(new ByteArrayInputStream(value.getBytes())); // // br = StreamUtil.getReader(StreamUtil.getResourceStream("empty.dxf")); } StringBuffer writeBuffer = new StringBuffer(); while (true) { String[] pair = StreamUtil.readNextPair(br); if (pair == null) { break; } if ("0".equals(pair[0].trim()) && "SECTION".equals(pair[1].trim())) { // nextPartStart String[] nextPairTitleTag = StreamUtil.readNextPair(br); if (nextPairTitleTag == null) { continue; } String nextPartName = nextPairTitleTag[1].trim(); if (justSaveEntity) { justSaveEntity(writeBuffer, pair, nextPairTitleTag, containNewEntity); } else { handleAll(writeBuffer, pair, nextPairTitleTag, containNewEntity); } readStr.accept(writeBuffer.toString()); writeBuffer = new StringBuffer(); System.out.println("handle part " + nextPartName + " end"); } else if ("EOF".equals(pair[1].trim())) { StringUtil.appendLnCrLf(writeBuffer, pair); readStr.accept(writeBuffer.toString()); System.out.println("completed!! dxf reduce end<<<<<<<<<<<<<"); break; } } } catch (Exception e) { System.err.println("Reduce dxf fail!!!!!!" + e.getMessage()); } finally { try { if (dxfFile != null) { br.reset(); } else { br.close(); } } catch (IOException e) { e.printStackTrace(); } } } private void save(String saveDxfFilePath, boolean justSaveEntity, boolean containNewEntity) { try (BufferedWriter fileWriter = StreamUtil.getFileWriter(saveDxfFilePath, charset)) { read(justSaveEntity, containNewEntity, fileWriter::write); fileWriter.flush(); } catch (IOException e) { System.err.println("save fail" + e.getLocalizedMessage()); } } private void justSaveEntity(StringBuffer writeBuffer, String[] pair, String[] nextPairTitleTag, boolean containNewEntity) throws IOException { String nextPartName = nextPairTitleTag[1].trim(); if ("ENTITIES".equals(nextPartName)) { StringUtil.appendLnCrLf(writeBuffer, pair); StringUtil.appendLnCrLf(writeBuffer, nextPairTitleTag); handEntities(writeBuffer, containNewEntity); } else { ignoreNextPart(); } } private void handleAll(StringBuffer writeBuffer, String[] pair, String[] nextPairTitleTag, boolean containNewEntity) throws IOException { String nextPartName = nextPairTitleTag[1].trim(); switch (nextPartName) { case "HEADER": StringUtil.appendLnCrLf(writeBuffer, pair); StringUtil.appendLnCrLf(writeBuffer, nextPairTitleTag); handHeader(writeBuffer); break; case "ENTITIES": StringUtil.appendLnCrLf(writeBuffer, pair); StringUtil.appendLnCrLf(writeBuffer, nextPairTitleTag); handEntities(writeBuffer, containNewEntity); break; case "CLASSES": case "TABLES": case "BLOCKS": case "OBJECTS": StringUtil.appendLnCrLf(writeBuffer, pair); StringUtil.appendLnCrLf(writeBuffer, nextPairTitleTag); justOutput(writeBuffer); break; default: ignoreNextPart(); break; } } private void ignoreNextPart() throws IOException { while (true) { String[] piar = StreamUtil.readNextPair(br); if (piar == null) { break; } if ("ENDSEC".equals(piar[1].trim())) { // header end, endsec is the end tag break; } } } private void justOutput(StringBuffer writeBuffer) throws IOException { System.out.println("part keep raw info"); while (true) { String[] piar = StreamUtil.readNextPair(br); if (piar == null) { break; } StringUtil.appendLnCrLf(writeBuffer, piar); if ("ENDSEC".equals(piar[1].trim())) { // header end, endsec is the end tag break; } } } private void handEntities(StringBuffer writeBuffer, boolean containNewEntity) throws IOException { Map keepMapResult = new HashMap<>(4); Map ignoreMapResult = new HashMap<>(4); String[] nextPair = StreamUtil.readNextPair(br); while (true) { if (nextPair == null) { break; } if ("ENDSEC".equals(nextPair[1].trim())) { // read entities end if (containNewEntity) { appendNewEntities(writeBuffer); } StringUtil.appendLnCrLf(writeBuffer, nextPair); break; } else { // read entities body String nextEntityName = nextPair[1].trim(); boolean isReduced = entityNoReducePart != null && !entityNoReducePart.contains(nextEntityName); if (!isReduced) { StringUtil.appendLnCrLf(writeBuffer, nextPair); keepMapResult.put(nextEntityName, keepMapResult.getOrDefault(nextEntityName, 0) + 1); } else { ignoreMapResult.put(nextEntityName, ignoreMapResult.getOrDefault(nextEntityName, 0) + 1); } nextPair = readNextEntity(writeBuffer, isReduced); } } System.out.println("[Entities]keep=>" + keepMapResult + "ignore=>" + ignoreMapResult); } private void appendNewEntities(StringBuffer buffer) { for (DxfEntity dxfEntity : newDxfEntityList) { buffer.append(dxfEntity.getDxfStr()); } } /** * add not reduce tag content to writeBuffer, and return next entity title tag * * @param writeBuffer content buffer * @return next entity title tag * @throws IOException if read file error throw this exception */ private String[] readNextEntity(StringBuffer writeBuffer, boolean isReduced) throws IOException { // check tag is need to reduce while (true) { String[] pair = StreamUtil.readNextPair(br); if (pair == null) { return null; } // code equals zero mean meet next entity tag title, and mean this entity end if ("0".equals(pair[0].trim())) { return pair; } if (!isReduced) { StringUtil.appendLnCrLf(writeBuffer, pair); } } } private void handHeader(StringBuffer writeBuffer) throws IOException { while (true) { String[] pair = StreamUtil.readNextPair(br); if (pair == null) { break; } StringUtil.appendLnCrLf(writeBuffer, pair); if (!newDxfEntityList.isEmpty() && DxfUtil.isPairNameEquals(pair, "$HANDSEED")) { pair = StreamUtil.readNextPair(br); if (pair == null) { return; } if ("5".equals(pair[0].trim())) { pair[1] = DxfUtil.formatMeta(maxMeta + 2); } StringUtil.appendLnCrLf(writeBuffer, pair); } if ("ENDSEC".equals(pair[1].trim())) { // header end, endsec is the end tag break; } } } /** * close * * @throws IOException IOException */ @Override public void close() throws IOException { StreamUtil.closeStream(br); System.out.println("Dxf document has been closed."); } interface ReadDxfConsumer { void accept(String string) throws IOException; } }