All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ttzero.excel.entity.e7.XMLDrawingsWriter Maven / Gradle / Ivy

/*
 * Copyright (c) 2017-2023, [email protected] All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.ttzero.excel.entity.e7;

import org.ttzero.excel.drawing.Bevel;
import org.ttzero.excel.drawing.Camera;
import org.ttzero.excel.drawing.Enums.Angle;
import org.ttzero.excel.drawing.Enums.Cap;
import org.ttzero.excel.drawing.Enums.JoinType;
import org.ttzero.excel.drawing.Enums.PresetCamera;
import org.ttzero.excel.drawing.Enums.ShapeType;
import org.ttzero.excel.drawing.Fill;
import org.ttzero.excel.drawing.Glow;
import org.ttzero.excel.drawing.LightRig;
import org.ttzero.excel.drawing.Outline;
import org.ttzero.excel.drawing.Effect;
import org.ttzero.excel.drawing.Reflection;
import org.ttzero.excel.drawing.Scene3D;
import org.ttzero.excel.drawing.Shadow;
import org.ttzero.excel.drawing.Shape3D;
import org.ttzero.excel.entity.IDrawingsWriter;
import org.ttzero.excel.entity.Picture;
import org.ttzero.excel.entity.Relationship;
import org.ttzero.excel.entity.style.ColorIndex;
import org.ttzero.excel.manager.Const;
import org.ttzero.excel.manager.RelManager;
import org.ttzero.excel.manager.TopNS;
import org.ttzero.excel.manager.docProps.Tuple2;
import org.ttzero.excel.util.ExtBufferedWriter;
import org.ttzero.excel.util.FileUtil;
import org.ttzero.excel.util.StringUtil;

import java.awt.Color;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;

import static org.ttzero.excel.util.FileUtil.exists;

/**
 * 多媒体类型输出协议(目前只支持图片)
 *
 * @author guanquan.wang at 2023-03-07 09:09
 */
@TopNS(prefix = {"xdr", "a", "r"}, value = "wsDr"
    , uri = {"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
    , "http://schemas.openxmlformats.org/drawingml/2006/main"
    , Const.Relationship.RELATIONSHIP})
public class XMLDrawingsWriter implements IDrawingsWriter {
    protected Path path;
    protected ExtBufferedWriter bw;
    protected int size;
    protected RelManager relManager;
    /**
     * Store async pictures
     */
    protected Picture[] pictures;
    /**
     * Mark complete status
     * 0: free or complete
     * 1: wait
     */
    protected long[] bits;
    protected int countDown;

    public XMLDrawingsWriter(Path path) {
        this.path = path;
        this.relManager = new RelManager();
        try {
            if (!exists(path.getParent())) {
                FileUtil.mkdir(path.getParent());
            }
            bw = new ExtBufferedWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8));
            bw.write("");
        } catch (IOException e) {
            throw new RuntimeException("Create XMLDrawingsWriter error", e);
        }
    }

    @Override
    public void close() throws IOException {
        if (bw == null) return;
        if (countDown > 0) {
            int counter = 30; // Loop 30 times (1 minutes)
            do {
                // Check status
                if (checkComplete() == 0) break;

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    break;
                }
            } while (--counter >= 0);
        }
        // End tag
        bw.write("");
        relManager.write(path.getParent(), path.getFileName().toString());
        FileUtil.close(bw);
        bw = null;
    }

    @Override
    public void writeTo(Path root) throws IOException { }

    static final String[] ANCHOR_PROPERTY = {"twoCell", "oneCell", "absolute"};

    @Override
    public void drawing(Picture picture) throws IOException {
        if (StringUtil.isEmpty(picture.picName)) return;
        Relationship picRel = relManager.add(new Relationship("../media/" + picture.picName, Const.Relationship.IMAGE));
        size++;
        int editAs = picture.property >= 0 ? picture.property & 3 : 0;
        if (editAs >= 0) {
            bw.write("");
        } else bw.write("");

        // From
        bw.write("");
        bw.writeInt(picture.col);
        bw.write("");
        bw.writeInt(picture.padding[3] * 12700);
        bw.write("");
        bw.writeInt(picture.row);
        bw.write("");
        bw.writeInt(picture.padding[0] * 12700);
        bw.write("");

        // TO
        bw.write("");
        bw.writeInt(Math.max(picture.col, picture.toCol));
        bw.write("");
        bw.writeInt(picture.padding[1] * 12700);
        bw.write("");
        bw.writeInt(Math.max(picture.row, picture.toRow));
        bw.write("");
        bw.writeInt(picture.padding[2] * 12700);
        bw.write("");

        // Picture
        bw.write("");
        bw.write("");

        bw.write("");
        bw.write("");

        // Revolve
        if (picture.revolve != 0) {
            bw.write("");
        }

        // Picture Effects
        if (picture.effect == null) {
            // Default geometry: rect
            bw.write("");
        }
        // Write effects
        else {
            writeEffects(picture);
        }

        bw.write("");
        // End Picture
        bw.write("");
    }

    @Override
    public void asyncDrawing(Picture picture) throws IOException {
        if (pictures == null) {
            bits = new long[2];
            pictures = new Picture[bits.length << 6];
        }
        int freeIndex = getFreeIndex(bits);
        // Grow and copy
        if (freeIndex < 0) {
            freeIndex = bits.length << 6;
            bits = Arrays.copyOf(bits, bits.length + 2);
            pictures = Arrays.copyOf(pictures, bits.length << 6);
        }
        // Write file if current location is completed
        else if (pictures[freeIndex] != null) {
            drawing(pictures[freeIndex]);
            countDown--;
        }

        picture.idx = freeIndex;
        pictures[freeIndex] = picture;
        markIndex(bits, freeIndex);
        countDown++;
    }

    @Override
    public void complete(Picture picture) {
        if (bits == null) return;
        freeIndex(bits, picture.idx);
    }

    protected int checkComplete() throws IOException {
        while (countDown > 0) {
            int i = getFreeIndex(bits);
            // None complete picture
            if (i < 0) break;
            Picture p = pictures[i];
            // Overflow
            if (p == null) break;
            // Write picture
            drawing(p);
            // The completed position is marked as 1 to prevent further acquisition
            markIndex(bits, i);
            countDown--;
        }
        return countDown;
    }

    public static int getFreeIndex(long[] bits) {
        int i = 0, idx = 64;
        for (; i < bits.length && (idx = Long.numberOfTrailingZeros(Long.highestOneBit(~bits[i]))) == 64; i++);
        return idx < 64 ? (i << 6) + (64 - idx - 1) : -1;
    }

    /**
     * Mark the bits at the specified position as 1
     *
     * @param bits bit array
     * @param idx index
     */
    public static synchronized void markIndex(long[] bits, int idx) {
        bits[idx >> 6] |= 1L << (63 - (idx - (idx >>> 6 << 6)));
    }

    /**
     * Mark the bits at the specified position as 0
     *
     * @param bits bit array
     * @param idx index
     */
    public static synchronized void freeIndex(long[] bits, int idx) {
        bits[idx >> 6] &= ~(1L << (63 - (idx - (idx >>> 6 << 6))));
    }

    protected void writeEffects(Picture pict) throws IOException {
        Effect effect = pict.effect;
        // Geometry
        bw.write("");
            for (Tuple2 guide : effect.geometryAdjustValueList) {
                bw.write("");
            }
            bw.write("");
        } else bw.write("\">");

        // Fill
        if (effect.fill != null) attachFill(effect.fill);

        // Outline
        if (effect.outline != null && effect.outline.width > 0.) attachOutline(effect.outline);

        boolean hasShadow = effect.shadow != null && effect.shadow.size > 0
            , hasInnerShadow = effect.innerShadow != null
            , hasReflection = effect.reflection != null
            , hasSoftEdges = effect.softEdges > 0
            , hasGlow = effect.glow != null;

        // Picture Effects
        if (hasShadow || hasInnerShadow || hasReflection || hasSoftEdges || hasGlow) {
            bw.write("");

            // Glow
            if (hasGlow) attachGlow(effect.glow);

            // Shadow
            if (hasShadow) attachShadow(effect.shadow, "outerShdw");
            if (hasInnerShadow) attachShadow(effect.innerShadow, "innerShdw");

            // Reflection
            if (hasReflection) attachReflection(effect.reflection);

            // Soft Edges
            if (hasSoftEdges) attachSoftEdges(effect.softEdges);

            bw.write("");
        }

        // 3D Scene
        if (effect.scene3D != null && effect.scene3D.camera != null) attachScene3d(effect.scene3D);

        // 3D Shape
        if (effect.shape3D != null) attachShape3D(effect.shape3D);
    }

    protected void attachShadow(Shadow shadow, String tag) throws IOException {
        bw.write(" 0) {
            bw.write(" blurRad=\""); bw.writeInt(n);
        }
        n = (int) (shadow.dist % 201 * 12700) / 100 * 100;
        if (n > 0) {
            bw.write("\" dist=\""); bw.writeInt(n);
        }
        n = shadow.direction % 361 * 60000;
        if (n > 0) {
            bw.write("\" dir=\""); bw.writeInt(n);
        }
        boolean hs = shadow.sx != 0. || shadow.sy != 0.;
        if (hs) {
            if (shadow.sx != 0.) {
                bw.write("\" sx=\"");
                bw.writeInt(((int) (shadow.sx % 201 * 1000)));
            }
            if (shadow.sy != 0.) {
                bw.write("\" sy=\"");
                bw.writeInt(((int) (shadow.sy % 201 * 1000)));
            }
        } else if (shadow.size != 100.) {
            bw.write("\" sx=\"");
            bw.writeInt(n = ((int) (shadow.size % 201 * 1000)));
            bw.write("\" sy=\"");
            bw.writeInt(n);
        }
        if (shadow.kx > 0) {
            bw.write("\" kx=\"");
            bw.writeInt(((int) (shadow.kx % 361 * 60000)));
        }
        if (shadow.ky > 0) {
            bw.write("\" ky=\"");
            bw.writeInt(((int) (shadow.ky % 361 * 60000)));
        }
        if (shadow.angle != null) {
            bw.write("\" algn=\"");
            bw.write(shadow.angle.shotName);
        }
        if ("outerShdw".equals(tag)) {
            bw.write("\" rotWithShape=\"");
            bw.writeInt(shadow.rotWithShape);
        }
        bw.write("\">");
        } else bw.write("\"/>");
        bw.write("");
    }

    protected void attachReflection(Reflection reflection) throws IOException {
        bw.write(" 0) {
            bw.write("\" dist=\"");
            bw.writeInt((int) (reflection.dist % 101 * 12700));
        }
        bw.write("\" endPos=\"");
        bw.writeInt((int) (reflection.size % 101 * 1000 + 0.5D));
        bw.write("\" dir=\"");
        bw.writeInt(reflection.direction % 361 * 60000);
        bw.write("\" sy=\"-100000\" algn=\"bl\" rotWithShape=\"0\"/>");
    }

    protected void attachGlow(Glow glow) throws IOException {
        int red = (int) (glow.dist % 151 * 12700) / 100 * 100;
        bw.write(" 0) {
            bw.write(" rad=\"");
            bw.writeInt(red);
            bw.write('\"');
        }
        bw.write(">");
        } else bw.write("\"/>");
    }

    protected void attachSoftEdges(double softEdges) throws IOException {
        bw.write("");
    }

    protected void attachFill(Fill fill) throws IOException {
        if (fill instanceof Fill.SolidFill) {
            Fill.SolidFill solidFill = (Fill.SolidFill) fill;
            if (solidFill.color == null) return;
            bw.write("");
            if (solidFill.shade > 0) {
                bw.write("");
            }
            int t = 100 - solidFill.alpha % 101;
            if (t != 100) {
                bw.write("");
            }
            bw.write("");
        }
//        // TODO Pattern fill
//        else if (fill instanceof Fill.PatternFill) {
//        }
//        // TODO Gradient fill
//        else if (fill instanceof Fill.GradientFill) {
//        }
    }

    protected void attachOutline(Outline ln) throws IOException {
        bw.write("");
        // Support 'solidFill' only
        bw.write("");
        if (ln.dash != null) {
            bw.write("");
        }
        if (ln.joinType != null) {
            bw.write(" 0 ? ((int) (ln.miterLimit * 1000)) : 800000);
                bw.write("\"/>");
            } else bw.write("/>");
        }
        bw.write("");
    }

    protected void attachScene3d(Scene3D scene) throws IOException {
        bw.write("");
        Camera camera = scene.camera;
        bw.write(" 0.) {
            bw.write("\" fov=\"");
            bw.writeInt((int) (camera.fov % 181 * 60000));
        }
        if (camera.zoom > 0) {
            bw.write("\" zoom=\"");
            bw.writeInt(camera.zoom);
            bw.write("%");
        }
        if (camera.latitude > 0. || camera.longitude > 0. || camera.revolution > 0.) {
            bw.write("\">");
        } else bw.write("\"/>");
        LightRig lightRig = scene.lightRig;
        if (lightRig != null && lightRig.rig != null) {
            bw.write("");
            if (lightRig.latitude > 0. || lightRig.longitude > 0. || lightRig.revolution > 0.) {
                bw.write("");
            }
            bw.write("");
        }
        bw.write("");
    }

    protected void attachShape3D(Shape3D shape) throws IOException {
        bw.write(" 0.) writeShapeProp(shape.contourWidth, "contourW");
        if (shape.extrusionHeight > 0.) writeShapeProp(shape.extrusionHeight, "extrusionH");
        if (shape.material != null) {
            bw.write(" prstMaterial=\"");
            bw.write(shape.material.name());
            bw.write("\"");
        }
        bw.write(">");
        if (shape.bevelBottom != null) writeBevel(shape.bevelBottom, 'B');
        if (shape.bevelTop != null) writeBevel(shape.bevelTop, 'T');
        if (shape.contourColor != null) {
            bw.write("");
        }
        if (shape.extrusionColor != null) {
            bw.write("");
        }
        bw.write("");
    }

    protected void writeBevel(Bevel bevel, char angle) throws IOException {
        bw.write("");
    }

    private void writeShapeProp(double v, String tag) throws IOException {
        bw.write(" "); bw.write(tag); bw.write("=\"");
        bw.writeInt((int) (v * 12700));
        bw.write("\"");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy