DocxImageExtractor.java

1
package pro.verron.officestamper.core;
2
3
import jakarta.xml.bind.JAXBElement;
4
import org.docx4j.dml.Graphic;
5
import org.docx4j.dml.picture.Pic;
6
import org.docx4j.dml.wordprocessingDrawing.Inline;
7
import org.docx4j.openpackaging.exceptions.Docx4JException;
8
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
9
import org.docx4j.wml.Drawing;
10
import org.docx4j.wml.R;
11
import pro.verron.officestamper.api.OfficeStamperException;
12
13
import java.io.IOException;
14
import java.io.InputStream;
15
import java.util.Arrays;
16
17
/**
18
 * Extracts images from a docx document.
19
 *
20
 * @author Joseph Verron
21
 * @version ${version}
22
 * @since 1.4.7
23
 */
24
public class DocxImageExtractor {
25
26
    private final WordprocessingMLPackage wordprocessingMLPackage;
27
28
    /**
29
     * Creates a new image extractor for the given docx document.
30
     *
31
     * @param wordprocessingMLPackage the docx document to extract images from.
32
     */
33
    public DocxImageExtractor(WordprocessingMLPackage wordprocessingMLPackage) {
34
        this.wordprocessingMLPackage = wordprocessingMLPackage;
35
    }
36
37
    /**
38
     * Extract an image bytes from an embedded image run.
39
     *
40
     * @param run run containing the embedded drawing.
41
     */
42
    byte[] getRunDrawingData(R run) {
43
        String imageRelId = getPic(run).getBlipFill()
44
                                       .getBlip()
45
                                       .getEmbed();
46
        String imageRelPartName = getImageRelPartName(imageRelId);
47
        long size = getImageSize(imageRelPartName);
48
        InputStream stream = getImageStream(imageRelPartName);
49 1 1. getRunDrawingData : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED
        return streamToByteArray(size, stream);
50
    }
51
52
    private static Pic getPic(R run) {
53 1 1. getPic : negated conditional → KILLED
        for (Object runContent : run.getContent()) {
54 1 1. getPic : negated conditional → KILLED
            if (!(runContent instanceof JAXBElement<?> runElement)) break;
55 1 1. getPic : negated conditional → KILLED
            if (!(runElement.getValue() instanceof Drawing drawing)) break;
56
            Graphic graphic = getInlineGraphic(drawing);
57 1 1. getPic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED
            return graphic.getGraphicData()
58
                          .getPic();
59
        }
60
        throw new OfficeStamperException("Run drawing not found !");
61
    }
62
63
    private String getImageRelPartName(String imageRelId) {
64
        // TODO: find a better way to find image rel part name in source part store
65 1 1. getImageRelPartName : replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED
        return wordprocessingMLPackage.getMainDocumentPart()
66
                                      .getRelationshipsPart()
67
                                      .getPart(imageRelId)
68
                                      .getPartName()
69
                                      .getName()
70
                                      .substring(1);
71
    }
72
73
    private long getImageSize(String imageRelPartName) {
74
        try {
75 1 1. getImageSize : replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED
            return wordprocessingMLPackage.getSourcePartStore()
76
                                          .getPartSize(imageRelPartName);
77
        } catch (Docx4JException e) {
78
            throw new OfficeStamperException(e);
79
        }
80
    }
81
82
    private InputStream getImageStream(String imageRelPartName) {
83
        try {
84 1 1. getImageStream : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED
            return wordprocessingMLPackage.getSourcePartStore()
85
                                          .loadPart(imageRelPartName);
86
        } catch (Docx4JException e) {
87
            throw new OfficeStamperException(e);
88
        }
89
    }
90
91
    /**
92
     * Converts an InputStream to a byte array.
93
     *
94
     * @param size expected size of the byte array.
95
     * @param is   input stream to read data from.
96
     *
97
     * @return the data from the input stream.
98
     */
99
    private static byte[] streamToByteArray(long size, InputStream is) {
100 2 1. streamToByteArray : changed conditional boundary → SURVIVED
2. streamToByteArray : negated conditional → KILLED
        if (size > Integer.MAX_VALUE) throw new OfficeStamperException("Image size exceeds maximum allowed (2GB)");
101
102
        int intSize = (int) size;
103
        byte[] data = new byte[intSize];
104
        int numRead = tryRead(is, data);
105 1 1. streamToByteArray : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED
        return Arrays.copyOfRange(data, 0, numRead);
106
    }
107
108
    /**
109
     * Extract an inline graphic from a drawing.
110
     *
111
     * @param drawing the drawing containing the graphic.
112
     */
113
    private static Graphic getInlineGraphic(Drawing drawing) {
114
        var anchorOrInline = drawing.getAnchorOrInline();
115 1 1. getInlineGraphic : negated conditional → KILLED
        if (anchorOrInline.isEmpty()) throw new OfficeStamperException("Anchor or Inline is empty !");
116 2 1. getInlineGraphic : negated conditional → KILLED
2. getInlineGraphic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getInlineGraphic → KILLED
        if (anchorOrInline.getFirst() instanceof Inline inline) return inline.getGraphic();
117
        throw new OfficeStamperException("Don't know how to process anchor !");
118
    }
119
120
    private static int tryRead(InputStream is, byte[] data) {
121
        try {
122 1 1. tryRead : replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED
            return is.read(data);
123
        } catch (IOException e) {
124
            throw new OfficeStamperException(e);
125
        }
126
    }
127
128
    /**
129
     * Extract the name of the image from an embedded image run.
130
     *
131
     * @param run run containing the embedded drawing.
132
     *
133
     * @return a {@link String} object
134
     */
135
    public String getRunDrawingFilename(R run) {
136 1 1. getRunDrawingFilename : replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingFilename → NO_COVERAGE
        return getPic(run).getNvPicPr()
137
                          .getCNvPr()
138
                          .getName();
139
    }
140
141
    /**
142
     * Extract the content type of the image from an embedded image run.
143
     *
144
     * @param run run containing the embedded drawing.
145
     *
146
     * @return a {@link String} object
147
     */
148
    public String getRunDrawingAltText(R run) {
149 1 1. getRunDrawingAltText : replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingAltText → NO_COVERAGE
        return getPic(run).getNvPicPr()
150
                          .getCNvPr()
151
                          .getDescr();
152
    }
153
154
    /**
155
     * Extract the width of the image from an embedded image run.
156
     *
157
     * @param run run containing the embedded drawing.
158
     *
159
     * @return a {@link Integer} object
160
     */
161
    public Integer getRunDrawingMaxWidth(R run) {
162 1 1. getRunDrawingMaxWidth : replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED
        return (int) getPic(run).getSpPr()
163
                                .getXfrm()
164
                                .getExt()
165
                                .getCx();
166
    }
167
}

Mutations

49

1.1
Location : getRunDrawingData
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED

53

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

54

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

55

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

57

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED

65

1.1
Location : getImageRelPartName
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED

75

1.1
Location : getImageSize
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED

84

1.1
Location : getImageStream
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED

100

1.1
Location : streamToByteArray
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

105

1.1
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED

115

1.1
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

116

1.1
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

2.2
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getInlineGraphic → KILLED

122

1.1
Location : tryRead
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED

136

1.1
Location : getRunDrawingFilename
Killed by : none
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingFilename → NO_COVERAGE

149

1.1
Location : getRunDrawingAltText
Killed by : none
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingAltText → NO_COVERAGE

162

1.1
Location : getRunDrawingMaxWidth
Killed by : none
replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED
Covering tests

Active mutators

Tests examined


Report generated by PIT 1.17.1