mirror of https://github.com/octoleo/plantuml.git
version 1.2017.20
This commit is contained in:
parent
c3662b656e
commit
c17f4a1254
|
@ -58,7 +58,7 @@
|
|||
</copy>
|
||||
<copy todir="build/stdlib">
|
||||
<fileset dir="stdlib">
|
||||
<include name="**/*.puml" />
|
||||
<include name="**/*.repx" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -35,7 +35,7 @@
|
|||
|
||||
<groupId>net.sourceforge.plantuml</groupId>
|
||||
<artifactId>plantuml</artifactId>
|
||||
<version>1.2017.20-SNAPSHOT</version>
|
||||
<version>1.2017.21-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>PlantUML</name>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<resource>
|
||||
<directory>${project.basedir}</directory>
|
||||
<includes>
|
||||
<include>stdlib/**/*.puml</include>
|
||||
<include>stdlib/**/*.repx</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
|
|
@ -43,7 +43,8 @@ public enum AlignParam {
|
|||
ARROW_MESSAGE_ALIGN(HorizontalAlignment.LEFT),
|
||||
SEQUENCE_MESSAGE_ALIGN(HorizontalAlignment.LEFT),
|
||||
SEQUENCE_MESSAGETEXT_ALIGN(HorizontalAlignment.LEFT),
|
||||
SEQUENCE_REFERENCE_ALIGN(HorizontalAlignment.CENTER);
|
||||
SEQUENCE_REFERENCE_ALIGN(HorizontalAlignment.CENTER),
|
||||
PACKAGE_TITLE_ALIGNMENT(HorizontalAlignment.CENTER);
|
||||
|
||||
private final HorizontalAlignment defaultValue;
|
||||
|
||||
|
|
|
@ -57,6 +57,10 @@ public class BasicEnsureVisible implements EnsureVisible {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return minX != Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
public String getCoords(double scale) {
|
||||
if (minX == Double.MAX_VALUE) {
|
||||
return "0,0,0,0";
|
||||
|
|
|
@ -60,7 +60,7 @@ public class CMapData {
|
|||
stringBuilder.append(s);
|
||||
}
|
||||
|
||||
public void appendUrl(int seq, Url url, double scale) {
|
||||
private void appendUrl(int seq, Url url, double scale) {
|
||||
appendString("<area shape=\"rect\" id=\"id");
|
||||
appendLong(seq);
|
||||
appendString("\" href=\"");
|
||||
|
@ -76,9 +76,6 @@ public class CMapData {
|
|||
appendString(BackSlash.NEWLINE);
|
||||
}
|
||||
|
||||
// private CMapData() {
|
||||
// }
|
||||
|
||||
public static CMapData cmapString(Set<Url> allUrlEncountered, double scale) {
|
||||
final CMapData cmapdata = new CMapData();
|
||||
|
||||
|
@ -87,6 +84,9 @@ public class CMapData {
|
|||
|
||||
int seq = 1;
|
||||
for (Url u : all) {
|
||||
if (u.hasData() == false) {
|
||||
continue;
|
||||
}
|
||||
cmapdata.appendUrl(seq, u, scale);
|
||||
seq++;
|
||||
}
|
||||
|
|
|
@ -103,4 +103,21 @@ public class CharSequence2Impl implements CharSequence2 {
|
|||
return preprocessorError;
|
||||
}
|
||||
|
||||
public CharSequence2 removeInnerComment() {
|
||||
final String string = s.toString();
|
||||
final String trim = string.replace('\t', ' ').trim();
|
||||
if (trim.startsWith("/'")) {
|
||||
final int idx = string.indexOf("'/");
|
||||
if (idx != -1) {
|
||||
return new CharSequence2Impl(s.subSequence(idx + 2, s.length()), location, preprocessorError);
|
||||
}
|
||||
}
|
||||
if (trim.endsWith("'/")) {
|
||||
final int idx = string.lastIndexOf("/'");
|
||||
if (idx != -1) {
|
||||
return new CharSequence2Impl(s.subSequence(0, idx), location, preprocessorError);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
*/
|
||||
package net.sourceforge.plantuml;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
|
@ -46,8 +48,11 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
// Used by the Eclipse Plugin, so do not change package location.
|
||||
public class FileUtils {
|
||||
|
||||
|
@ -115,19 +120,27 @@ public class FileUtils {
|
|||
|
||||
static public String readSvg(File svgFile) throws IOException {
|
||||
final BufferedReader br = new BufferedReader(new FileReader(svgFile));
|
||||
return readSvg(br, true);
|
||||
return readSvg(br, false, true);
|
||||
}
|
||||
|
||||
static public String readSvg(InputStream is) throws IOException {
|
||||
final BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
return readSvg(br, false);
|
||||
return readSvg(br, false, false);
|
||||
}
|
||||
|
||||
private static String readSvg(final BufferedReader br, boolean withClose) throws IOException {
|
||||
static public String readFile(File svgFile) throws IOException {
|
||||
final BufferedReader br = new BufferedReader(new FileReader(svgFile));
|
||||
return readSvg(br, true, true);
|
||||
}
|
||||
|
||||
private static String readSvg(final BufferedReader br, boolean withNewline, boolean withClose) throws IOException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
sb.append(s);
|
||||
if (withNewline) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
if (withClose) {
|
||||
br.close();
|
||||
|
@ -135,4 +148,36 @@ public class FileUtils {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
// public static BufferedImage ImageIO_read(File f) throws IOException {
|
||||
// return ImageIO.read(f);
|
||||
// }
|
||||
|
||||
public static BufferedImage ImageIO_read(File f) {
|
||||
// https://www.experts-exchange.com/questions/26171948/Why-are-ImageIO-read-images-losing-their-transparency.html
|
||||
// https://stackoverflow.com/questions/18743790/can-java-load-images-with-transparency
|
||||
|
||||
try {
|
||||
return readImage(new ImageIcon(f.getAbsolutePath()));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedImage ImageIO_read(URL url) {
|
||||
try {
|
||||
return readImage(new ImageIcon(url));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage readImage(final ImageIcon imageIcon) {
|
||||
final Image tmpImage = imageIcon.getImage();
|
||||
final BufferedImage image = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
image.getGraphics().drawImage(tmpImage, 0, 0, null);
|
||||
tmpImage.flush();
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ public class Option {
|
|||
|
||||
private File outputDir = null;
|
||||
private File outputFile = null;
|
||||
private String filename;
|
||||
|
||||
private final List<String> result = new ArrayList<String>();
|
||||
|
||||
|
@ -168,6 +169,12 @@ public class Option {
|
|||
continue;
|
||||
}
|
||||
charset = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg[i]);
|
||||
} else if (s.equalsIgnoreCase("-filename")) {
|
||||
i++;
|
||||
if (i == arg.length) {
|
||||
continue;
|
||||
}
|
||||
filename = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg[i]);
|
||||
} else if (s.startsWith("-o") && s.length() > 3) {
|
||||
s = s.substring(2);
|
||||
outputDir = new File(StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(s));
|
||||
|
@ -296,6 +303,8 @@ public class Option {
|
|||
OptionFlags.getInstance().setEnableStats(true);
|
||||
} else if (s.equalsIgnoreCase("-disablestats")) {
|
||||
OptionFlags.getInstance().setEnableStats(false);
|
||||
} else if (s.equalsIgnoreCase("-extractstdlib")) {
|
||||
OptionFlags.getInstance().setExtractStdLib(true);
|
||||
} else if (s.equalsIgnoreCase("-htmlstats")) {
|
||||
StatsUtils.setHtmlStats(true);
|
||||
} else if (s.equalsIgnoreCase("-xmlstats")) {
|
||||
|
@ -548,4 +557,12 @@ public class Option {
|
|||
return imageIndex;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public final void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ public class OptionFlags {
|
|||
private boolean loopStats;
|
||||
private boolean overwrite;
|
||||
private boolean enableStats = defaultForStats();
|
||||
private boolean extractStdLib;
|
||||
private String fileSeparator = "_";
|
||||
private long timeoutMs = 15 * 60 * 1000L; // 15 minutes
|
||||
private File logData;
|
||||
|
@ -333,4 +334,12 @@ public class OptionFlags {
|
|||
public final void setTimeoutMs(long timeoutMs) {
|
||||
this.timeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
public void setExtractStdLib(boolean extractStdLib) {
|
||||
this.extractStdLib = extractStdLib;
|
||||
}
|
||||
|
||||
public boolean getExtractStdLib() {
|
||||
return extractStdLib;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,6 +141,8 @@ public class OptionPrint {
|
|||
System.out.println(" -splash\t\tTo display a splash screen with some progress bar");
|
||||
System.out.println(" -progress\t\tTo display a textual progress bar in console");
|
||||
System.out.println(" -pipeimageindex N\tTo generate the Nth image with pipe option");
|
||||
System.out.println(" -extractstdlib\tTo extract PlantUML Standard Library into stdlib folder");
|
||||
System.out.println(" -filename \"example.puml\"\tTo override %filename% variable");
|
||||
System.out.println();
|
||||
System.out.println("If needed, you can setup the environment variable GRAPHVIZ_DOT.");
|
||||
exit();
|
||||
|
|
|
@ -69,8 +69,8 @@ public class Pipe {
|
|||
ps.flush();
|
||||
return error;
|
||||
}
|
||||
final SourceStringReader sourceStringReader = new SourceStringReader(Defines.createEmpty(), source,
|
||||
option.getConfig());
|
||||
final SourceStringReader sourceStringReader = new SourceStringReader(Defines.createEmpty(option
|
||||
.getFilename()), source, option.getConfig());
|
||||
if (option.isComputeurl()) {
|
||||
for (BlockUml s : sourceStringReader.getBlocks()) {
|
||||
ps.println(s.getEncodedUrl());
|
||||
|
@ -88,11 +88,17 @@ public class Pipe {
|
|||
ps.println(system.getDescription());
|
||||
}
|
||||
} else if (option.isPipeMap()) {
|
||||
final String result = sourceStringReader.getCMapData(0, option.getFileFormatOption());
|
||||
ps.println(result);
|
||||
final String result = sourceStringReader.getCMapData(option.getImageIndex(),
|
||||
option.getFileFormatOption());
|
||||
if (result == null) {
|
||||
ps.println();
|
||||
} else {
|
||||
ps.println(result);
|
||||
}
|
||||
} else {
|
||||
final OutputStream os = noStdErr ? new ByteArrayOutputStream() : ps;
|
||||
final DiagramDescription result = sourceStringReader.outputImage(os, option.getImageIndex(), option.getFileFormatOption());
|
||||
final DiagramDescription result = sourceStringReader.outputImage(os, option.getImageIndex(),
|
||||
option.getFileFormatOption());
|
||||
if (result != null && "(error)".equalsIgnoreCase(result.getDescription())) {
|
||||
error = true;
|
||||
manageErrors(noStdErr ? ps : System.err, sourceStringReader);
|
||||
|
|
|
@ -62,6 +62,7 @@ import net.sourceforge.plantuml.descdiagram.DescriptionDiagramFactory;
|
|||
import net.sourceforge.plantuml.ftp.FtpServer;
|
||||
import net.sourceforge.plantuml.objectdiagram.ObjectDiagramFactory;
|
||||
import net.sourceforge.plantuml.png.MetadataTag;
|
||||
import net.sourceforge.plantuml.preproc.Stdlib;
|
||||
import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory;
|
||||
import net.sourceforge.plantuml.statediagram.StateDiagramFactory;
|
||||
import net.sourceforge.plantuml.stats.StatsUtils;
|
||||
|
@ -77,6 +78,10 @@ public class Run {
|
|||
saveCommandLine(argsArray);
|
||||
final Option option = new Option(argsArray);
|
||||
ProgressBar.setEnable(option.isTextProgressBar());
|
||||
if (OptionFlags.getInstance().getExtractStdLib()) {
|
||||
Stdlib.extractStdLib();
|
||||
return;
|
||||
}
|
||||
if (OptionFlags.getInstance().isDumpStats()) {
|
||||
StatsUtils.dumpStats();
|
||||
return;
|
||||
|
|
|
@ -114,6 +114,10 @@ public class Url implements EnsureVisible {
|
|||
visible.ensureVisible(x, y);
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return visible.hasData();
|
||||
}
|
||||
|
||||
public static final Comparator<Url> SURFACE_COMPARATOR = new Comparator<Url>() {
|
||||
public int compare(Url url1, Url url2) {
|
||||
final double surface1 = url1.visible.getSurface();
|
||||
|
|
|
@ -38,6 +38,7 @@ package net.sourceforge.plantuml.activitydiagram3.ftile.vcompact;
|
|||
import java.awt.geom.Dimension2D;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.plantuml.AlignParam;
|
||||
import net.sourceforge.plantuml.FontParam;
|
||||
import net.sourceforge.plantuml.ISkinParam;
|
||||
import net.sourceforge.plantuml.LineParam;
|
||||
|
@ -82,7 +83,7 @@ public class FtileGroup extends AbstractFtile {
|
|||
super(inner.skinParam());
|
||||
this.backColor = backColor == null ? HtmlColorUtils.WHITE : backColor;
|
||||
this.inner = FtileUtils.addHorizontalMargin(inner, 10);
|
||||
this.borderColor = backColor == null ? HtmlColorUtils.BLACK : borderColor;
|
||||
this.borderColor = borderColor == null ? HtmlColorUtils.BLACK : borderColor;
|
||||
final UFont font = skinParam.getFont(null, false, FontParam.PARTITION);
|
||||
|
||||
final HtmlColor fontColor = skinParam.getFontHtmlColor(null, FontParam.PARTITION);
|
||||
|
@ -195,8 +196,8 @@ public class FtileGroup extends AbstractFtile {
|
|||
|
||||
final SymbolContext symbolContext = new SymbolContext(backColor, borderColor).withShadow(
|
||||
skinParam().shadowing()).withStroke(stroke);
|
||||
USymbol.FRAME.asBig(name, TextBlockUtils.empty(0, 0), dimTotal.getWidth(), dimTotal.getHeight(), symbolContext)
|
||||
.drawU(ug);
|
||||
USymbol.FRAME.asBig(name, inner.skinParam().getHorizontalAlignment(AlignParam.PACKAGE_TITLE_ALIGNMENT, null),
|
||||
TextBlockUtils.empty(0, 0), dimTotal.getWidth(), dimTotal.getHeight(), symbolContext).drawU(ug);
|
||||
|
||||
final Dimension2D dimHeaderNote = headerNote.calculateDimension(stringBounder);
|
||||
headerNote.drawU(ug.apply(new UTranslate(dimTotal.getWidth() - dimHeaderNote.getWidth() - 10,
|
||||
|
|
|
@ -37,10 +37,11 @@ package net.sourceforge.plantuml.code;
|
|||
|
||||
public class AsciiEncoder implements URLEncoder {
|
||||
|
||||
final private char encode6bit[] = new char[64];
|
||||
final private byte decode6bit[] = new byte[128];
|
||||
// Temporary because of AsciiEncoderFinalZeros
|
||||
final static/* private */char encode6bit[] = new char[64];
|
||||
final static/* private */byte decode6bit[] = new byte[128];
|
||||
|
||||
public AsciiEncoder() {
|
||||
static {
|
||||
for (byte b = 0; b < 64; b++) {
|
||||
encode6bit[b] = encode6bit(b);
|
||||
decode6bit[encode6bit[b]] = b;
|
||||
|
@ -51,32 +52,55 @@ public class AsciiEncoder implements URLEncoder {
|
|||
if (data == null) {
|
||||
return "";
|
||||
}
|
||||
final StringBuilder resu = new StringBuilder((data.length * 4 + 2) / 3);
|
||||
final StringBuilder result = new StringBuilder((data.length * 4 + 2) / 3);
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
append3bytes(resu, data[i] & 0xFF, i + 1 < data.length ? data[i + 1] & 0xFF : 0,
|
||||
append3bytes(result, data[i] & 0xFF, i + 1 < data.length ? data[i + 1] & 0xFF : 0,
|
||||
i + 2 < data.length ? data[i + 2] & 0xFF : 0);
|
||||
}
|
||||
return resu.toString();
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public byte[] decode(String s) {
|
||||
if (s.length() % 4 != 0) {
|
||||
throw new IllegalArgumentException("Cannot decode " + s);
|
||||
}
|
||||
final byte data[] = new byte[(s.length() * 3 + 3) / 4];
|
||||
// if (s.length() % 4 != 0) {
|
||||
// throw new IllegalArgumentException("Cannot decode " + s);
|
||||
// }
|
||||
final byte data[] = new byte[computeSize(s.length())];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < s.length(); i += 4) {
|
||||
decode3bytes(data, pos, s.charAt(i), s.charAt(i + 1), s.charAt(i + 2), s.charAt(i + 3));
|
||||
decode3bytes(data, pos, scharAt(s, i), scharAt(s, i + 1), scharAt(s, i + 2), scharAt(s, i + 3));
|
||||
pos += 3;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public int decode6bit(char c) {
|
||||
private int computeSize(int length) {
|
||||
// while (length % 4 != 0) {
|
||||
// length++;
|
||||
// }
|
||||
final int r = length % 4;
|
||||
if (r != 0) {
|
||||
length += 4 - r;
|
||||
}
|
||||
// System.err.println("length=" + length);
|
||||
// System.err.println("length1=" + (length % 4));
|
||||
// length += length % 4;
|
||||
// System.err.println("length2=" + length);
|
||||
assert length % 4 == 0 : "length=" + length;
|
||||
return (length * 3 + 3) / 4;
|
||||
}
|
||||
|
||||
private char scharAt(String s, int i) {
|
||||
if (i >= s.length()) {
|
||||
return '0';
|
||||
}
|
||||
return s.charAt(i);
|
||||
}
|
||||
|
||||
public static int decode6bit(char c) {
|
||||
return decode6bit[c];
|
||||
}
|
||||
|
||||
public char encode6bit(byte b) {
|
||||
public static char encode6bit(byte b) {
|
||||
assert b >= 0 && b < 64;
|
||||
if (b < 10) {
|
||||
return (char) ('0' + b);
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.code;
|
||||
|
||||
public class AsciiEncoderFinalZeros {
|
||||
|
||||
public String encode(byte data[]) {
|
||||
if (data == null) {
|
||||
return "";
|
||||
}
|
||||
final StringBuilder result = new StringBuilder((data.length * 4 + 2) / 3);
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
append3bytes(result, data[i] & 0xFF, i + 1 < data.length ? data[i + 1] & 0xFF : 0,
|
||||
i + 2 < data.length ? data[i + 2] & 0xFF : 0);
|
||||
}
|
||||
while (result.length() > 0 && result.charAt(result.length() - 1) == '0') {
|
||||
result.setLength(result.length() - 1);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void append3bytes(StringBuilder sb, int b1, int b2, int b3) {
|
||||
final int c1 = b1 >> 2;
|
||||
final int c2 = ((b1 & 0x3) << 4) | (b2 >> 4);
|
||||
final int c3 = ((b2 & 0xF) << 2) | (b3 >> 6);
|
||||
final int c4 = b3 & 0x3F;
|
||||
sb.append(AsciiEncoder.encode6bit[c1 & 0x3F]);
|
||||
sb.append(AsciiEncoder.encode6bit[c2 & 0x3F]);
|
||||
sb.append(AsciiEncoder.encode6bit[c3 & 0x3F]);
|
||||
sb.append(AsciiEncoder.encode6bit[c4 & 0x3F]);
|
||||
}
|
||||
|
||||
}
|
|
@ -38,47 +38,24 @@ package net.sourceforge.plantuml.code;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class CompressionGZip implements Compression {
|
||||
import net.sourceforge.plantuml.FileUtils;
|
||||
|
||||
class MyGZIPOutputStream extends GZIPOutputStream {
|
||||
import org.brotli.dec.BrotliInputStream;
|
||||
|
||||
public MyGZIPOutputStream(OutputStream baos) throws IOException {
|
||||
super(baos);
|
||||
def.setLevel(9);
|
||||
}
|
||||
|
||||
}
|
||||
public class CompressionBrotli implements Compression {
|
||||
|
||||
public byte[] compress(byte[] in) {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
final GZIPOutputStream gz = new MyGZIPOutputStream(baos);
|
||||
gz.write(in);
|
||||
gz.close();
|
||||
baos.close();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e.toString());
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public byte[] decompress(byte[] in) throws IOException {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(in);
|
||||
final GZIPInputStream gz = new GZIPInputStream(bais);
|
||||
int read;
|
||||
while ((read = gz.read()) != -1) {
|
||||
baos.write(read);
|
||||
}
|
||||
gz.close();
|
||||
bais.close();
|
||||
baos.close();
|
||||
return baos.toByteArray();
|
||||
final BrotliInputStream brotli = new BrotliInputStream(new ByteArrayInputStream(in));
|
||||
final ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
FileUtils.copyToStream(brotli, result);
|
||||
brotli.close();
|
||||
result.close();
|
||||
return result.toByteArray();
|
||||
}
|
||||
|
||||
}
|
|
@ -42,10 +42,13 @@ import java.util.zip.Inflater;
|
|||
|
||||
public class CompressionZlib implements Compression {
|
||||
|
||||
private static boolean USE_ZOPFLI = false;
|
||||
private static final int COMPRESSION_LEVEL = 9;
|
||||
// private static final int COMPRESSION_LEVEL = 1;
|
||||
|
||||
public byte[] compress(byte[] in) {
|
||||
if (USE_ZOPFLI) {
|
||||
return new CompressionZopfliZlib().compress(in);
|
||||
}
|
||||
if (in.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -33,54 +33,33 @@
|
|||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.webp;
|
||||
package net.sourceforge.plantuml.code;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import net.sourceforge.plantuml.zopfli.Options;
|
||||
import net.sourceforge.plantuml.zopfli.Options.BlockSplitting;
|
||||
import net.sourceforge.plantuml.zopfli.Options.OutputFormat;
|
||||
import net.sourceforge.plantuml.zopfli.Zopfli;
|
||||
|
||||
public class Portrait {
|
||||
public class CompressionZopfliZlib implements Compression {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
private final String quote;
|
||||
private final byte webp[];
|
||||
|
||||
public Portrait(String name, int age, String quote, byte webp[]) throws IOException {
|
||||
this.name = name;
|
||||
this.quote = quote;
|
||||
this.age = age;
|
||||
this.webp = webp;
|
||||
}
|
||||
|
||||
public BufferedImage getBufferedImage() {
|
||||
try {
|
||||
final InputStream is = new ByteArrayInputStream(webp);
|
||||
final ImageInputStream iis = ImageIO.createImageInputStream(is);
|
||||
final VP8Decoder vp8Decoder = new VP8Decoder();
|
||||
vp8Decoder.decodeFrame(iis, false);
|
||||
iis.close();
|
||||
return vp8Decoder.getFrame().getBufferedImage();
|
||||
} catch (IOException e) {
|
||||
public byte[] compress(byte[] in) {
|
||||
if (in.length == 0) {
|
||||
return null;
|
||||
}
|
||||
int len = in.length * 2;
|
||||
if (len < 100) {
|
||||
len = 100;
|
||||
}
|
||||
final Zopfli compressor = new Zopfli(len);
|
||||
final Options options = new Options(OutputFormat.DEFLATE, BlockSplitting.FIRST, 30);
|
||||
|
||||
return compressor.compress(options, in).getResult();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public String getQuote() {
|
||||
return quote;
|
||||
public byte[] decompress(byte[] in) throws IOException {
|
||||
return new CompressionZlib().decompress(in);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.code;
|
||||
|
||||
public class PairInt {
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
|
||||
public PairInt(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public PairInt rotate() {
|
||||
return new PairInt(-y, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + "," + y + ")";
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public PairInt plus(PairInt other) {
|
||||
return new PairInt(x + other.x, y + other.y);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.code;
|
||||
|
||||
public class Spiral {
|
||||
|
||||
private PairInt current = new PairInt(0, 0);
|
||||
private PairInt direction = new PairInt(1, 0);
|
||||
private int step = 0;
|
||||
private int lim = 1;
|
||||
private int len = 1;
|
||||
private int cpt = 0;
|
||||
|
||||
public PairInt nextPoint() {
|
||||
final PairInt result = current;
|
||||
oneStep();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void oneStep() {
|
||||
this.current = this.current.plus(this.direction);
|
||||
step++;
|
||||
if (step == lim) {
|
||||
this.direction = this.direction.rotate();
|
||||
cpt++;
|
||||
if (cpt == 2) {
|
||||
cpt = 0;
|
||||
len++;
|
||||
}
|
||||
lim += len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.code;
|
||||
|
||||
public class SpiralOnRectangle {
|
||||
|
||||
private final Spiral spiral = new Spiral();
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final PairInt delta;
|
||||
|
||||
public SpiralOnRectangle(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.delta = new PairInt(width / 2, height / 2);
|
||||
}
|
||||
|
||||
private boolean inside(PairInt point) {
|
||||
final int x = point.getX();
|
||||
final int y = point.getY();
|
||||
return x >= 0 && x < width && y >= 0 && y < height;
|
||||
}
|
||||
|
||||
public PairInt nextPoint() {
|
||||
do {
|
||||
final PairInt result = spiral.nextPoint().plus(delta);
|
||||
if (inside(result)) {
|
||||
return result;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
|
@ -47,6 +47,10 @@ public class TranscoderImpl implements Transcoder {
|
|||
this(new AsciiEncoder(), new StringCompressorNone(), new CompressionHuffman());
|
||||
}
|
||||
|
||||
public TranscoderImpl(Compression compression) {
|
||||
this(new AsciiEncoder(), new StringCompressorNone(), compression);
|
||||
}
|
||||
|
||||
public TranscoderImpl(URLEncoder urlEncoder, Compression compression) {
|
||||
this(urlEncoder, new ArobaseStringCompressor(), compression);
|
||||
}
|
||||
|
|
|
@ -39,17 +39,17 @@ import java.io.IOException;
|
|||
|
||||
public class TranscoderSmart implements Transcoder {
|
||||
|
||||
private final Transcoder oldOne = new TranscoderImpl(new AsciiEncoder(), new CompressionHuffman());
|
||||
// private final Transcoder oldOne = new TranscoderImpl(new AsciiEncoder(), new CompressionHuffman());
|
||||
private final Transcoder zlib = new TranscoderImpl(new AsciiEncoder(), new CompressionZlib());
|
||||
|
||||
|
||||
private final Transcoder brotli = new TranscoderImpl(new AsciiEncoder(), new CompressionBrotli());
|
||||
|
||||
public String decode(String code) throws IOException {
|
||||
try {
|
||||
return zlib.decode(code);
|
||||
} catch (Exception ex) {
|
||||
return oldOne.decode(code);
|
||||
if (code.startsWith("0")) {
|
||||
return brotli.decode(code.substring(1));
|
||||
}
|
||||
return zlib.decode(code);
|
||||
}
|
||||
|
||||
public String encode(String text) throws IOException {
|
||||
return zlib.encode(text);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,9 @@ public class BlocLines implements Iterable<CharSequence> {
|
|||
}
|
||||
|
||||
public CharSequence getFirst499() {
|
||||
if (lines.size()==0) {
|
||||
return null;
|
||||
}
|
||||
return lines.get(0);
|
||||
}
|
||||
|
||||
|
@ -229,34 +232,38 @@ public class BlocLines implements Iterable<CharSequence> {
|
|||
return lines.iterator();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public BlocLines removeComments() {
|
||||
final List<CharSequence> copy = new ArrayList<CharSequence>();
|
||||
boolean inComment = false;
|
||||
for (CharSequence cs : lines) {
|
||||
if (inComment == false && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_SINGLE_LINE)) {
|
||||
continue;
|
||||
}
|
||||
if (inComment == false && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_MULTILINE_START)) {
|
||||
inComment = true;
|
||||
continue;
|
||||
}
|
||||
if (inComment && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_MULTILINE_END)) {
|
||||
inComment = false;
|
||||
continue;
|
||||
}
|
||||
if (inComment == false) {
|
||||
copy.add(cs);
|
||||
}
|
||||
}
|
||||
return new BlocLines(copy);
|
||||
return this;
|
||||
// final List<CharSequence> copy = new ArrayList<CharSequence>();
|
||||
// boolean inComment = false;
|
||||
// for (CharSequence cs : lines) {
|
||||
// if (inComment == false && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_SINGLE_LINE)) {
|
||||
// continue;
|
||||
// }
|
||||
// if (inComment == false && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_MULTILINE_START)) {
|
||||
// inComment = true;
|
||||
// continue;
|
||||
// }
|
||||
// if (inComment && MyPattern.mtches(cs, CommandMultilinesComment.COMMENT_MULTILINE_END)) {
|
||||
// inComment = false;
|
||||
// continue;
|
||||
// }
|
||||
// if (inComment == false) {
|
||||
// copy.add(cs);
|
||||
// }
|
||||
// }
|
||||
// return new BlocLines(copy);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public BlocLines removeInnerComments() {
|
||||
final List<CharSequence> copy = new ArrayList<CharSequence>();
|
||||
for (CharSequence cs : lines) {
|
||||
copy.add(MyPattern.removeAll(cs, CommandMultilinesComment.INNER_COMMENT));
|
||||
}
|
||||
return new BlocLines(copy);
|
||||
return this;
|
||||
// final List<CharSequence> copy = new ArrayList<CharSequence>();
|
||||
// for (CharSequence cs : lines) {
|
||||
// copy.add(MyPattern.removeAll(cs, CommandMultilinesComment.INNER_COMMENT));
|
||||
// }
|
||||
// return new BlocLines(copy);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import net.sourceforge.plantuml.core.Diagram;
|
|||
public class CommandComment extends SingleLineCommand<Diagram> {
|
||||
|
||||
|
||||
public CommandComment() {
|
||||
private CommandComment() {
|
||||
super(CommandMultilinesComment.COMMENT_SINGLE_LINE);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,11 @@ public abstract class CommandMultilines2<S extends Diagram> implements Command<S
|
|||
if (isCommandForbidden()) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
final boolean result1 = starting.match(StringUtils.trin(lines.getFirst499()));
|
||||
final CharSequence first = lines.getFirst499();
|
||||
if (first == null) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
final boolean result1 = starting.match(StringUtils.trin(first));
|
||||
if (result1 == false) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,11 @@ public abstract class CommandMultilines3<S extends Diagram> implements Command<S
|
|||
if (isCommandForbidden()) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
final boolean result1 = starting.match(StringUtils.trin(lines.getFirst499()));
|
||||
final CharSequence first = lines.getFirst499();
|
||||
if (first == null) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
final boolean result1 = starting.match(StringUtils.trin(first));
|
||||
if (result1 == false) {
|
||||
return CommandControl.NOT_OK;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class CommandMultilinesComment extends CommandMultilines<Diagram> {
|
|||
public static final String COMMENT_SINGLE_LINE = "(?i)^[%s]*([%q].*||/[%q].*[%q]/[%s]*)$";
|
||||
public static final String INNER_COMMENT = "/[%q].*?[%q]/";
|
||||
|
||||
public CommandMultilinesComment() {
|
||||
private CommandMultilinesComment() {
|
||||
super(COMMENT_MULTILINE_START);
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ public class CommandSpriteFile extends SingleLineCommand2<UmlDiagram> {
|
|||
if (isSvg(f.getName())) {
|
||||
sprite = new SpriteSvg(f);
|
||||
} else {
|
||||
sprite = new SpriteImage(ImageIO.read(f));
|
||||
sprite = new SpriteImage(FileUtils.ImageIO_read(f));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -266,8 +266,8 @@ public abstract class UmlDiagramFactory extends PSystemAbstractFactory {
|
|||
|
||||
final protected void addCommonCommands(List<Command> cmds) {
|
||||
cmds.add(new CommandNope());
|
||||
cmds.add(new CommandComment());
|
||||
cmds.add(new CommandMultilinesComment());
|
||||
// cmds.add(new CommandComment());
|
||||
// cmds.add(new CommandMultilinesComment());
|
||||
cmds.add(new CommandPragma());
|
||||
cmds.add(new CommandTitle());
|
||||
cmds.add(new CommandCaption());
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.creole;
|
||||
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.plantuml.Dimension2DDouble;
|
||||
import net.sourceforge.plantuml.graphic.StringBounder;
|
||||
import net.sourceforge.plantuml.ugraphic.UGraphic;
|
||||
import net.sourceforge.plantuml.ugraphic.UTranslate;
|
||||
|
||||
public class AtomHorizontalTexts implements Atom {
|
||||
private final List<Atom> all;
|
||||
|
||||
public AtomHorizontalTexts(List<Atom> texts) {
|
||||
this.all = texts;
|
||||
}
|
||||
|
||||
public Dimension2D calculateDimension(StringBounder stringBounder) {
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
for (Atom text : all) {
|
||||
final Dimension2D dim = text.calculateDimension(stringBounder);
|
||||
height = Math.max(height, dim.getHeight());
|
||||
width += dim.getWidth();
|
||||
}
|
||||
return new Dimension2DDouble(width, height);
|
||||
}
|
||||
|
||||
public double getStartingAltitude(StringBounder stringBounder) {
|
||||
if (all.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return all.get(0).getStartingAltitude(stringBounder);
|
||||
}
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
double x = 0;
|
||||
for (Atom text : all) {
|
||||
final Dimension2D dim = text.calculateDimension(ug.getStringBounder());
|
||||
text.drawU(ug.apply(new UTranslate(x, 0)));
|
||||
x += dim.getWidth();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -35,7 +35,6 @@
|
|||
*/
|
||||
package net.sourceforge.plantuml.creole;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -50,6 +49,7 @@ import javax.imageio.ImageIO;
|
|||
|
||||
import net.sourceforge.plantuml.Dimension2DDouble;
|
||||
import net.sourceforge.plantuml.FileSystem;
|
||||
import net.sourceforge.plantuml.FileUtils;
|
||||
import net.sourceforge.plantuml.code.Base64Coder;
|
||||
import net.sourceforge.plantuml.graphic.FontConfiguration;
|
||||
import net.sourceforge.plantuml.graphic.ImgValign;
|
||||
|
@ -89,19 +89,19 @@ public class AtomImg implements Atom {
|
|||
if (f.exists() == false) {
|
||||
// Check if valid URL
|
||||
if (src.startsWith("http:") || src.startsWith("https:")) {
|
||||
final byte image[] = getFile(src);
|
||||
return build(src, fc, image, scale);
|
||||
// final byte image[] = getFile(src);
|
||||
return build(src, fc, new URL(src), scale);
|
||||
}
|
||||
return AtomText.create("(File not found: " + f + ")", fc);
|
||||
return AtomText.create("(File not found: " + f.getCanonicalPath() + ")", fc);
|
||||
}
|
||||
if (f.getName().endsWith(".svg")) {
|
||||
return new AtomImgSvg(new TileImageSvg(f));
|
||||
}
|
||||
final BufferedImage read = ImageIO.read(f);
|
||||
final BufferedImage read = FileUtils.ImageIO_read(f);
|
||||
if (read == null) {
|
||||
return AtomText.create("(Cannot decode: " + f + ")", fc);
|
||||
return AtomText.create("(Cannot decode: " + f.getCanonicalPath() + ")", fc);
|
||||
}
|
||||
return new AtomImg(ImageIO.read(f), scale);
|
||||
return new AtomImg(FileUtils.ImageIO_read(f), scale);
|
||||
} catch (IOException e) {
|
||||
return AtomText.create("ERROR " + e.toString(), fc);
|
||||
}
|
||||
|
@ -116,8 +116,16 @@ public class AtomImg implements Atom {
|
|||
return new AtomImg(read, scale);
|
||||
}
|
||||
|
||||
private static Atom build(String source, final FontConfiguration fc, URL url, double scale) throws IOException {
|
||||
final BufferedImage read = FileUtils.ImageIO_read(url);
|
||||
if (read == null) {
|
||||
return AtomText.create("(Cannot decode: " + source + ")", fc);
|
||||
}
|
||||
return new AtomImg(read, scale);
|
||||
}
|
||||
|
||||
// Added by Alain Corbiere
|
||||
static byte[] getFile(String host) throws IOException {
|
||||
private static byte[] getFile(String host) throws IOException {
|
||||
final ByteArrayOutputStream image = new ByteArrayOutputStream();
|
||||
InputStream input = null;
|
||||
try {
|
||||
|
|
|
@ -37,6 +37,7 @@ package net.sourceforge.plantuml.creole;
|
|||
|
||||
import java.awt.geom.Dimension2D;
|
||||
|
||||
import net.sourceforge.plantuml.Url;
|
||||
import net.sourceforge.plantuml.graphic.FontConfiguration;
|
||||
import net.sourceforge.plantuml.graphic.StringBounder;
|
||||
import net.sourceforge.plantuml.graphic.TextBlock;
|
||||
|
@ -49,8 +50,14 @@ public class AtomOpenIcon implements Atom {
|
|||
private final OpenIcon openIcon;
|
||||
private final FontConfiguration fontConfiguration;
|
||||
private final double factor;
|
||||
private final Url url;
|
||||
|
||||
public AtomOpenIcon(OpenIcon openIcon, FontConfiguration fontConfiguration) {
|
||||
this(openIcon, fontConfiguration, null);
|
||||
}
|
||||
|
||||
public AtomOpenIcon(OpenIcon openIcon, FontConfiguration fontConfiguration, Url url) {
|
||||
this.url = url;
|
||||
this.openIcon = openIcon;
|
||||
this.fontConfiguration = fontConfiguration;
|
||||
this.factor = fontConfiguration.getSize2D() / 12;
|
||||
|
@ -69,7 +76,13 @@ public class AtomOpenIcon implements Atom {
|
|||
}
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
if (url != null) {
|
||||
ug.startUrl(url);
|
||||
}
|
||||
asTextBlock().drawU(ug);
|
||||
if (url != null) {
|
||||
ug.closeAction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.plantuml.BackSlash;
|
||||
import net.sourceforge.plantuml.Dimension2DDouble;
|
||||
|
@ -50,8 +52,10 @@ import net.sourceforge.plantuml.StringUtils;
|
|||
import net.sourceforge.plantuml.Url;
|
||||
import net.sourceforge.plantuml.cucadiagram.Display;
|
||||
import net.sourceforge.plantuml.graphic.FontConfiguration;
|
||||
import net.sourceforge.plantuml.graphic.Splitter;
|
||||
import net.sourceforge.plantuml.graphic.StringBounder;
|
||||
import net.sourceforge.plantuml.graphic.TextBlockUtils;
|
||||
import net.sourceforge.plantuml.openiconic.OpenIcon;
|
||||
import net.sourceforge.plantuml.ugraphic.UChangeColor;
|
||||
import net.sourceforge.plantuml.ugraphic.UGraphic;
|
||||
import net.sourceforge.plantuml.ugraphic.UText;
|
||||
|
@ -84,14 +88,46 @@ public class AtomText implements Atom {
|
|||
fontConfiguration = fontConfiguration.hyperlink();
|
||||
final Display display = Display.getWithNewlines(url.getLabel());
|
||||
if (display.size() > 1) {
|
||||
final List<AtomText> all = new ArrayList<AtomText>();
|
||||
final List<Atom> all = new ArrayList<Atom>();
|
||||
for (CharSequence s : display.as()) {
|
||||
all.add(new AtomText(s.toString(), fontConfiguration, url, ZERO, ZERO));
|
||||
all.add(createAtomText(s.toString(), url, fontConfiguration));
|
||||
}
|
||||
return new AtomTexts(all);
|
||||
return new AtomVerticalTexts(all);
|
||||
|
||||
}
|
||||
return new AtomText(url.getLabel(), fontConfiguration, url, ZERO, ZERO);
|
||||
return createAtomText(url.getLabel(), url, fontConfiguration);
|
||||
}
|
||||
|
||||
private static Atom createAtomText(final String text, Url url, FontConfiguration fontConfiguration) {
|
||||
final Pattern p = Pattern.compile(Splitter.openiconPattern);
|
||||
final Matcher m = p.matcher(text);
|
||||
final List<Atom> result = new ArrayList<Atom>();
|
||||
|
||||
while (m.find()) {
|
||||
final String val = m.group(1);
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
m.appendReplacement(sb, "");
|
||||
if (sb.length() > 0) {
|
||||
result.add(new AtomText(sb.toString(), fontConfiguration, url, ZERO, ZERO));
|
||||
}
|
||||
final OpenIcon openIcon = OpenIcon.retrieve(val);
|
||||
if (openIcon != null) {
|
||||
result.add(new AtomOpenIcon(openIcon, fontConfiguration, url));
|
||||
}
|
||||
}
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
m.appendTail(sb);
|
||||
if (sb.length() > 0) {
|
||||
result.add(new AtomText(sb.toString(), fontConfiguration, url, ZERO, ZERO));
|
||||
}
|
||||
if (result.size() == 1) {
|
||||
return result.get(0);
|
||||
}
|
||||
return new AtomHorizontalTexts(result);
|
||||
}
|
||||
|
||||
private static Atom createAtomTextOld(final String text, Url url, FontConfiguration fontConfiguration) {
|
||||
return new AtomText(text, fontConfiguration, url, ZERO, ZERO);
|
||||
}
|
||||
|
||||
public static AtomText createHeading(String text, FontConfiguration fontConfiguration, int order) {
|
||||
|
|
|
@ -43,17 +43,17 @@ import net.sourceforge.plantuml.graphic.StringBounder;
|
|||
import net.sourceforge.plantuml.ugraphic.UGraphic;
|
||||
import net.sourceforge.plantuml.ugraphic.UTranslate;
|
||||
|
||||
public class AtomTexts implements Atom {
|
||||
private final List<AtomText> all;
|
||||
public class AtomVerticalTexts implements Atom {
|
||||
private final List<Atom> all;
|
||||
|
||||
public AtomTexts(List<AtomText> texts) {
|
||||
public AtomVerticalTexts(List<Atom> texts) {
|
||||
this.all = texts;
|
||||
}
|
||||
|
||||
public Dimension2D calculateDimension(StringBounder stringBounder) {
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
for (AtomText text : all) {
|
||||
for (Atom text : all) {
|
||||
final Dimension2D dim = text.calculateDimension(stringBounder);
|
||||
width = Math.max(width, dim.getWidth());
|
||||
height += dim.getHeight();
|
||||
|
@ -67,7 +67,7 @@ public class AtomTexts implements Atom {
|
|||
|
||||
public void drawU(UGraphic ug) {
|
||||
double y = 0;
|
||||
for (AtomText text : all) {
|
||||
for (Atom text : all) {
|
||||
final Dimension2D dim = text.calculateDimension(ug.getStringBounder());
|
||||
text.drawU(ug.apply(new UTranslate(0, y)));
|
||||
y += dim.getHeight();
|
|
@ -114,6 +114,13 @@ public class LinkStyle {
|
|||
return new UStroke(nonZeroThickness());
|
||||
}
|
||||
|
||||
public UStroke muteStroke(UStroke stroke) {
|
||||
if (type == Type.DASHED || type == Type.DOTTED || type == Type.BOLD) {
|
||||
return getStroke3();
|
||||
}
|
||||
return stroke;
|
||||
}
|
||||
|
||||
private double nonZeroThickness() {
|
||||
if (thickness == 0) {
|
||||
return 1;
|
||||
|
|
|
@ -46,6 +46,10 @@ public class LinkType {
|
|||
private final LinkHat hat2;
|
||||
private final LinkMiddleDecor middleDecor;
|
||||
|
||||
public boolean isDoubleDecorated() {
|
||||
return decor1 != LinkDecor.NONE && decor2 != LinkDecor.NONE;
|
||||
}
|
||||
|
||||
public LinkType(LinkDecor decor1, LinkDecor decor2) {
|
||||
this(LinkHat.NONE, decor1, decor2, LinkHat.NONE);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
|||
import net.sourceforge.plantuml.graphic.USymbol;
|
||||
import net.sourceforge.plantuml.graphic.color.ColorParser;
|
||||
import net.sourceforge.plantuml.graphic.color.ColorType;
|
||||
import net.sourceforge.plantuml.graphic.color.Colors;
|
||||
import net.sourceforge.plantuml.utils.UniqueSequence;
|
||||
|
||||
public class CommandPackageWithUSymbol extends SingleLineCommand2<AbstractEntityDiagram> {
|
||||
|
@ -93,10 +94,14 @@ public class CommandPackageWithUSymbol extends SingleLineCommand2<AbstractEntity
|
|||
new RegexLeaf("[%s]*"), //
|
||||
new RegexLeaf("URL", "(" + UrlBuilder.getRegexp() + ")?"), //
|
||||
new RegexLeaf("[%s]*"), //
|
||||
ColorParser.exp1(), //
|
||||
color().getRegex(), //
|
||||
new RegexLeaf("[%s]*\\{$"));
|
||||
}
|
||||
|
||||
private static ColorParser color() {
|
||||
return ColorParser.simpleColor(ColorType.BACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CommandExecutionResult executeArg(AbstractEntityDiagram diagram, RegexResult arg) {
|
||||
final String codeRaw = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.getLazzy("CODE", 0));
|
||||
|
@ -123,11 +128,8 @@ public class CommandPackageWithUSymbol extends SingleLineCommand2<AbstractEntity
|
|||
if (stereotype != null) {
|
||||
p.setStereotype(new Stereotype(stereotype, false));
|
||||
}
|
||||
final String color = arg.get("COLOR", 0);
|
||||
if (color != null) {
|
||||
p.setSpecificColorTOBEREMOVED(ColorType.BACK,
|
||||
diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(color));
|
||||
}
|
||||
final Colors colors = color().getColor(arg, diagram.getSkinParam().getIHtmlColorSet());
|
||||
p.setColors(colors);
|
||||
return CommandExecutionResult.ok();
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ import java.util.StringTokenizer;
|
|||
import net.sourceforge.plantuml.AbstractPSystem;
|
||||
import net.sourceforge.plantuml.BackSlash;
|
||||
import net.sourceforge.plantuml.FileFormatOption;
|
||||
import net.sourceforge.plantuml.code.CompressionBrotli;
|
||||
import net.sourceforge.plantuml.code.Transcoder;
|
||||
import net.sourceforge.plantuml.code.TranscoderImpl;
|
||||
import net.sourceforge.plantuml.core.DiagramDescription;
|
||||
|
@ -65,19 +66,19 @@ import net.sourceforge.plantuml.version.PSystemVersion;
|
|||
|
||||
public class PSystemDonors extends AbstractPSystem {
|
||||
|
||||
public static final String DONORS = "UDfjL4jksp0GtSyfk1RIGoXsxDI_17QQ0jcDvR75c29TKd8ghOBUe421hdk3eaKMHOjiUW7bHXqvbKcb"
|
||||
+ "9M3058V3zsRUcrX5VnTPkAISh8XrRhJ41AueyijaC35-Qnt4xLLSmj3w8ofXGKjqkKGjPYRL4EkLiM9e"
|
||||
+ "AA-e-RjUSqGRZXec63oaS-ZBTYCLn3wrsSEYy11A6CgekxI1KOkoSB38QPUI5ZrZTioltxv33iRPYwl6"
|
||||
+ "GiyegSNS58vWLBmuZsuD4satEmdJ03LcuPQVYXMzHxtYSlPIoLatQ4jcEZ_IrXkOXUZp2YwKtFE5laos"
|
||||
+ "ZFnu8ZMSkMDg7O28Q0_zAfEvBSK4KxD263L5lqcHqmTn4IdVBbxRaikaCUSuniUuHi_WQTjXfLUyjGdN"
|
||||
+ "P81BMsxDdTjAJgJuseNDPGNdFdfbO2gJb9GuHQdAa86gGZ3EmOXGlnfXyguW9_h5qYBtKdc-r-OsfoGL"
|
||||
+ "OuiEFQGDiKI3Zjqrok8GCJPx6Ghb89Ktdh14c0VYRPu8UKQtV7iNoCficygS6eiQrUv4t52EPkTWpWJH"
|
||||
+ "hiHODgs5WJAsgVVqzo-b3attWb21E5bEawESBBRLRhgUTXavkCHM7dFP0HmhAHQ8AxQEtP1uItf3zaHC"
|
||||
+ "uwlNYzXBEp8QXjInJCXS9ooHJ5mpwZwBTkmZBJMTPRDCYE1i7of_Zp3pIIXhGKO1ZM4XMHT5Hc9W2ifz"
|
||||
+ "VHC7Wx0p1ovU3ReAoyPmPtB36bh8p5kG0R6JrqsbaLqTW8wKWlfJ_H2ahn_g7z1BEzOksNZ5yZ08h6Ol"
|
||||
+ "d9iEd26PzTfIAMOouyQDyD-fCiSrAZ4jdMAFr5_gXwVx-dlpbAW_ru_rp_hnwGF_xldJDpYpL8angeoT"
|
||||
+ "THxskf4hiLvFIJM7EcWRloA3gHX5F2IxB5XWr_HoA4M5uhdHKibXSKZeSgW_rb-F9M5Wx3cVvyei9SFe"
|
||||
+ "eyr2dx88J6VmAHwXNeLhxcfOkAtF-PyPyzpDgyhFkpzUfhdf";
|
||||
public static final String DONORS = "6w8602mIZ6r6h8Vz5iSz4U4e4OD_QwRy4VHnsX9GMiAKM7PtB6kpHqv0K9vJl6yGnOAW0UkI7k_btCRE"
|
||||
+ "s8BWkQAuWWgGLz6DShx6dP852fGRwsVRI0vl_tyk5zYzA93UUPlo1CFZW6viQeo14x1jzwEoGVz5Tu4M"
|
||||
+ "y3IwiuP5bpLRFaGClgArQvZPYpGGGYk_WL9IPoX6M28br2viuHIIWn7KMkTrn3FSldvIBrABZLaivJaJ"
|
||||
+ "oArsSJJV_h6thcJxSMQ9e1_gWAm3E803vKvE64T-i57vrI11PGwwiQcIcITkY8X10xtnG-cKb9AApE78"
|
||||
+ "H1Lm1KY4_1aZ08z7DNLoDZSDA5aoM_Gi2UhKUk1kZfTfARKl6awI0m5Ewe1M2UG_q1rwxpBxIuDdNGmn"
|
||||
+ "Kz_9t6JpK6n4TF7zCT2db1lwv0b0xSoSTREfNKggSUcllJx6Ne0ebffzb3VzZNUsO5x_FOHql5Y0yW8w"
|
||||
+ "rpiYviyVryb29aHpwVQ22-VYoTL5GDeu31O-n9BLr8TtCM98cgSuYt2utg3QY40dLrrS1gwkbSnNErTW"
|
||||
+ "cgN5zLkVXjNh8DSarUPzYrRIYwNiVuHtDMiP3jhlOapN9ObmpJXPTGUBBWB3eEs1i99SIBNDzLFTl7_B"
|
||||
+ "Flj9S6z396653P4wKDVJb6yTLX5lLBhKn02m_f2eTrH13_4WgmFeCgYymjCHTZjSBTEdZl7QQKqUmcuC"
|
||||
+ "wgiwKxdZFZoKliCmAOzcTTNs5kS0BgDWBZ_RsBwAO_KCxx4NeNbE1iOjFUY1z1rMrhNxI-xR0c5ciHRS"
|
||||
+ "BMI_7hxpvw6RVv_-B1gvkl-v_WzpLJn-9zIkjvJbiAJRctLPnVSYgWZGEq6xG7LcF0JiEvCJrJb2vJU3"
|
||||
+ "6V3m98cmFhYuFh7WDX2rPMLdAJP4R4UixWCjoG-LJXfLrC2IzTxkf3mZMbIptvntOOx7Ogb2hmMKowg2"
|
||||
+ "QpGGzff2rVGvMLHSSW00";
|
||||
|
||||
@Override
|
||||
final protected ImageData exportDiagramNow(OutputStream os, int num, FileFormatOption fileFormat, long seed)
|
||||
|
@ -126,7 +127,7 @@ public class PSystemDonors extends AbstractPSystem {
|
|||
|
||||
private List<String> getDonors() throws IOException {
|
||||
final List<String> lines = new ArrayList<String>();
|
||||
final Transcoder t = new TranscoderImpl();
|
||||
final Transcoder t = new TranscoderImpl(new CompressionBrotli());
|
||||
final String s = t.decode(DONORS).replace('*', '.');
|
||||
final StringTokenizer st = new StringTokenizer(s, BackSlash.NEWLINE);
|
||||
while (st.hasMoreTokens()) {
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.graphic;
|
||||
|
||||
|
||||
public class DateEventUtils {
|
||||
|
||||
// private static final String PARIS = "A thought for those who died in Paris the 13th November 2015.";
|
||||
//
|
||||
// private static TextBlock addEvent(final TextBlock textBlock, final HtmlColor color) {
|
||||
// try {
|
||||
// final String today = new SimpleDateFormat("MM-dd", Locale.US).format(new Date());
|
||||
// if ("11-05".equals(today)) {
|
||||
// final List<String> asList = Arrays.asList("<u>November 5th, 1955",
|
||||
// "Doc Brown's discovery of the Flux Capacitor, that makes time-travel possible.");
|
||||
// return TextBlockUtils.mergeTB(textBlock, getComment(asList, color), HorizontalAlignment.LEFT);
|
||||
// } else if ("08-29".equals(today)) {
|
||||
// final List<String> asList = Arrays.asList("<u>August 29th, 1997",
|
||||
// "Skynet becomes self-aware at 02:14 AM Eastern Time.");
|
||||
// return TextBlockUtils.mergeTB(textBlock, getComment(asList, color), HorizontalAlignment.LEFT);
|
||||
// } else if ("06-29".equals(today)) {
|
||||
// final List<String> asList = Arrays.asList("<u>June 29th, 1975",
|
||||
// "\"It was the first time in history that anyone had typed",
|
||||
// "a character on a keyboard and seen it show up on their",
|
||||
// "own computer's screen right in front of them.\"", "\t\t\t\t\t\t\t\t\t\t<i>Steve Wozniak");
|
||||
// return TextBlockUtils.mergeTB(textBlock, getComment(asList, color), HorizontalAlignment.LEFT);
|
||||
// } else if ("01-07".equals(today) || ("01-08".equals(today) && getDayOfWeek() == Calendar.MONDAY)) {
|
||||
// return addCharlie(textBlock);
|
||||
// } else if ("11-13".equals(today) || ("11-14".equals(today) && getDayOfWeek() == Calendar.MONDAY)) {
|
||||
// return addMemorial(textBlock, color);
|
||||
// }
|
||||
// } catch (Throwable t) {
|
||||
// Log.debug("Error " + t);
|
||||
// }
|
||||
// return textBlock;
|
||||
// }
|
||||
//
|
||||
// private synchronized static int getDayOfWeek() {
|
||||
// return Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
|
||||
// }
|
||||
//
|
||||
// private static TextBlock addMemorial(TextBlock textBlock, HtmlColor color) {
|
||||
// final Portrait portrait = Portraits.getOne();
|
||||
// if (portrait == null) {
|
||||
// return textBlock;
|
||||
// }
|
||||
// final BufferedImage im = portrait.getBufferedImage();
|
||||
// if (im == null) {
|
||||
// return textBlock;
|
||||
// }
|
||||
//
|
||||
// final String name = portrait.getName();
|
||||
// final String quote = portrait.getQuote();
|
||||
// final String age = "" + portrait.getAge() + " years old";
|
||||
// final UFont font12 = new UFont("SansSerif", Font.BOLD, 12);
|
||||
// TextBlock comment = Display.create(name, age, quote).create(
|
||||
// new FontConfiguration(font12, color, HtmlColorUtils.BLUE, true), HorizontalAlignment.LEFT,
|
||||
// new SpriteContainerEmpty());
|
||||
// comment = TextBlockUtils.withMinWidth(TextBlockUtils.withMargin(comment, 4, 4), 800, HorizontalAlignment.LEFT);
|
||||
//
|
||||
// final TextBlock bottom0 = getComment(Arrays.asList(PARIS), color);
|
||||
// final TextBlock bottom1 = new AbstractTextBlock() {
|
||||
// private double margin = 10;
|
||||
//
|
||||
// public void drawU(UGraphic ug) {
|
||||
// ug = ug.apply(new UTranslate(0, margin));
|
||||
// ug.draw(new UImage(im));
|
||||
// if (ug instanceof LimitFinder) {
|
||||
// return;
|
||||
// }
|
||||
// Portraits.nextOne();
|
||||
// }
|
||||
//
|
||||
// public Dimension2D calculateDimension(StringBounder stringBounder) {
|
||||
// return new Dimension2DDouble(im.getWidth(), margin + im.getHeight());
|
||||
// }
|
||||
// };
|
||||
// final TextBlock bottom = TextBlockUtils.mergeTB(bottom0,
|
||||
// TextBlockUtils.mergeLR(bottom1, comment, VerticalAlignment.CENTER), HorizontalAlignment.LEFT);
|
||||
// final TextBlock mergeTB = TextBlockUtils.mergeTB(textBlock, bottom, HorizontalAlignment.LEFT);
|
||||
// return addMajesty(mergeTB, color);
|
||||
// }
|
||||
//
|
||||
// public static TextBlock addMajesty(TextBlock block, HtmlColor color) {
|
||||
// final UFont font12 = new UFont("SansSerif", Font.BOLD, 12);
|
||||
// final String arabic1 = "<size:16>\u0625\u0646 \u0627\u0644\u0625\u0631\u0647\u0627\u0628\u064A\u064A\u0646 \u0628\u0627\u0633\u0645 \u0627\u0644\u0625\u0633\u0644\u0627\u0645 \u0644\u064A\u0633\u0648\u0627 \u0645\u0633\u0644\u0645\u064A\u0646\u060C \u0648\u0644\u0627 \u064A\u0631\u0628\u0637\u0647\u0645 \u0628\u0627\u0644\u0625\u0633\u0644\u0627\u0645 \u0625\u0644\u0627 \u0627\u0644\u062F\u0648\u0627\u0641\u0639 \u0627\u0644\u062A\u064A \u064A\u0631\u0643\u0628\u0648\u0646 \u0639\u0644\u064A\u0647\u0627 \u0644\u062A\u0628\u0631\u064A\u0631 \u062C\u0631\u0627\u0626\u0645\u0647\u0645 \u0648\u062D\u0645\u0627\u0642\u0627\u062A\u0647\u0645.";
|
||||
// final String arabic2 = "<size:16>\u0641\u0647\u0645 \u0642\u0648\u0645 \u0636\u0627\u0644\u0648\u0646\u060C \u0645\u0635\u064A\u0631\u0647\u0645 \u062C\u0647\u0646\u0645 \u062E\u0627\u0644\u062F\u064A\u0646 \u0641\u064A\u0647\u0627 \u0623\u0628\u062F\u0627.";
|
||||
// final String english1 = "<size:10>Those who engage in terrorism, in the name of Islam, are not Muslims.";
|
||||
// final String english2 = "<size:10>Their only link to Islam is the pretexts they use to justify their crimes and their folly.";
|
||||
// final String english3 = "<size:10>They have strayed from the right path, and their fate is to dwell forever in hell.";
|
||||
// final TextBlock arabic = Display
|
||||
// .create(" ",
|
||||
// arabic1,
|
||||
// arabic2,
|
||||
// "<size:16>\u0635\u0627\u062D\u0628 \u0627\u0644\u062C\u0644\u0627\u0644\u0629 \u0627\u0644\u0645\u0644\u0643 \u0645\u062D\u0645\u062F \u0627\u0644\u0633\u0627\u062F\u0633 \u0623\u0645\u064A\u0631 \u0627\u0644\u0645\u0624\u0645\u0646\u064A\u0646 \u0646\u0635\u0631\u0647 \u0627\u0644\u0644\u0647")
|
||||
// .create(new FontConfiguration(font12, color, HtmlColorUtils.BLUE, true), HorizontalAlignment.RIGHT,
|
||||
// new SpriteContainerEmpty());
|
||||
// final TextBlock english = Display.create(english1, english2, english3,
|
||||
// "<size:10>-- His Majesty the King Mohammed the Sixth, Commander of the Faithful").create(
|
||||
// new FontConfiguration(font12, color, HtmlColorUtils.BLUE, true), HorizontalAlignment.LEFT,
|
||||
// new SpriteContainerEmpty());
|
||||
// return TextBlockUtils.mergeTB(block, TextBlockUtils.mergeTB(arabic, english, HorizontalAlignment.LEFT),
|
||||
// HorizontalAlignment.LEFT);
|
||||
// }
|
||||
//
|
||||
// private static TextBlock addCharlie(TextBlock textBlock) {
|
||||
// final TextBlock charlie = new AbstractTextBlock() {
|
||||
// private final BufferedImage charlie = PSystemVersion.getCharlieImage();
|
||||
//
|
||||
// public void drawU(UGraphic ug) {
|
||||
// ug.draw(new UImage(charlie));
|
||||
// }
|
||||
//
|
||||
// public Dimension2D calculateDimension(StringBounder stringBounder) {
|
||||
// return new Dimension2DDouble(charlie.getWidth(), charlie.getHeight());
|
||||
// }
|
||||
// };
|
||||
// return TextBlockUtils.mergeTB(charlie, textBlock, HorizontalAlignment.LEFT);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public static TextBlock getComment(final List<String> asList, HtmlColor color) {
|
||||
// final UFont font = UFont.sansSerif(14).bold();
|
||||
// TextBlock comment = Display.create(asList).create(
|
||||
// new FontConfiguration(font, color, HtmlColorUtils.BLUE, true), HorizontalAlignment.LEFT,
|
||||
// new SpriteContainerEmpty());
|
||||
// comment = TextBlockUtils.withMargin(comment, 4, 4);
|
||||
// comment = new SimpleTextBlockBordered(comment, color);
|
||||
// comment = TextBlockUtils.withMargin(comment, 10, 10);
|
||||
// return comment;
|
||||
// }
|
||||
}
|
|
@ -69,4 +69,8 @@ public enum HorizontalAlignment {
|
|||
return result;
|
||||
}
|
||||
|
||||
public String getGraphVizValue() {
|
||||
return toString().substring(0, 1).toLowerCase();
|
||||
}
|
||||
|
||||
}
|
|
@ -47,6 +47,7 @@ import java.net.URLConnection;
|
|||
import javax.imageio.ImageIO;
|
||||
|
||||
import net.sourceforge.plantuml.FileSystem;
|
||||
import net.sourceforge.plantuml.FileUtils;
|
||||
import net.sourceforge.plantuml.StringUtils;
|
||||
import net.sourceforge.plantuml.command.regex.Matcher2;
|
||||
import net.sourceforge.plantuml.command.regex.MyPattern;
|
||||
|
@ -102,8 +103,9 @@ public class Img implements HtmlCommand {
|
|||
if (f.exists() == false) {
|
||||
// Check if valid URL
|
||||
if (src.startsWith("http:") || src.startsWith("https:")) {
|
||||
final byte image[] = getFile(src);
|
||||
final BufferedImage read = ImageIO.read(new ByteArrayInputStream(image));
|
||||
// final byte image[] = getFile(src);
|
||||
// final BufferedImage read = ImageIO.read(new ByteArrayInputStream(image));
|
||||
final BufferedImage read = FileUtils.ImageIO_read(new URL(src));
|
||||
if (read == null) {
|
||||
return new Text("(Cannot decode: " + src + ")");
|
||||
}
|
||||
|
@ -114,11 +116,11 @@ public class Img implements HtmlCommand {
|
|||
if (f.getName().endsWith(".svg")) {
|
||||
return new Img(new TileImageSvg(f));
|
||||
}
|
||||
final BufferedImage read = ImageIO.read(f);
|
||||
final BufferedImage read = FileUtils.ImageIO_read(f);
|
||||
if (read == null) {
|
||||
return new Text("(Cannot decode: " + f + ")");
|
||||
}
|
||||
return new Img(new TileImage(ImageIO.read(f), valign, vspace));
|
||||
return new Img(new TileImage(FileUtils.ImageIO_read(f), valign, vspace));
|
||||
} catch (IOException e) {
|
||||
return new Text("ERROR " + e.toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.graphic;
|
||||
|
||||
import net.sourceforge.plantuml.ugraphic.UChange;
|
||||
import net.sourceforge.plantuml.ugraphic.UGraphic;
|
||||
import net.sourceforge.plantuml.ugraphic.UShape;
|
||||
import net.sourceforge.plantuml.ugraphic.UText;
|
||||
|
||||
public class UGraphicAddAriaLabel extends UGraphicDelegator {
|
||||
|
||||
private final String ariaLabel;
|
||||
|
||||
public UGraphicAddAriaLabel(UGraphic ug, String ariaLabel) {
|
||||
super(ug);
|
||||
this.ariaLabel = ariaLabel;
|
||||
}
|
||||
|
||||
public UGraphic apply(UChange change) {
|
||||
return new UGraphicAddAriaLabel(getUg().apply(change), ariaLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(UShape shape) {
|
||||
if (shape instanceof UText) {
|
||||
shape = ((UText) shape).withAriaLabel(ariaLabel);
|
||||
}
|
||||
super.draw(shape);
|
||||
}
|
||||
|
||||
}
|
|
@ -126,8 +126,8 @@ public abstract class USymbol {
|
|||
|
||||
public abstract TextBlock asSmall(TextBlock name, TextBlock label, TextBlock stereotype, SymbolContext symbolContext);
|
||||
|
||||
public abstract TextBlock asBig(TextBlock label, TextBlock stereotype, double width, double height,
|
||||
SymbolContext symbolContext);
|
||||
public abstract TextBlock asBig(TextBlock label, HorizontalAlignment labelAlignment, TextBlock stereotype,
|
||||
double width, double height, SymbolContext symbolContext);
|
||||
|
||||
static class Margin {
|
||||
private final double x1;
|
||||
|
|
|
@ -88,13 +88,14 @@ class USymbolArtifact extends USymbol {
|
|||
private Margin getMargin() {
|
||||
return new Margin(10, 10 + 10, 10 + 3, 10);
|
||||
}
|
||||
|
||||
|
||||
public boolean manageHorizontalLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -115,8 +116,9 @@ class USymbolArtifact extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -44,19 +44,18 @@ import net.sourceforge.plantuml.ugraphic.URectangle;
|
|||
import net.sourceforge.plantuml.ugraphic.UTranslate;
|
||||
|
||||
class USymbolCard extends USymbol {
|
||||
|
||||
|
||||
private final SkinParameter skinParameter;
|
||||
|
||||
public USymbolCard(SkinParameter skinParameter) {
|
||||
this.skinParameter = skinParameter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SkinParameter getSkinParameter() {
|
||||
return skinParameter;
|
||||
}
|
||||
|
||||
|
||||
private void drawRect(UGraphic ug, double width, double height, boolean shadowing, double top) {
|
||||
final URectangle shape = new URectangle(width, height);
|
||||
if (shadowing) {
|
||||
|
@ -72,7 +71,9 @@ class USymbolCard extends USymbol {
|
|||
return new Margin(10, 10, 3, 3);
|
||||
}
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -92,8 +93,9 @@ class USymbolCard extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -51,7 +51,6 @@ class USymbolCloud extends USymbol {
|
|||
return SkinParameter.CLOUD;
|
||||
}
|
||||
|
||||
|
||||
private void drawCloud(UGraphic ug, double width, double height, boolean shadowing) {
|
||||
final UPath shape = getSpecificFrontierForCloud(width, height);
|
||||
if (shadowing) {
|
||||
|
@ -90,7 +89,9 @@ class USymbolCloud extends USymbol {
|
|||
return new Margin(10, 10, 10, 10);
|
||||
}
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -111,8 +112,9 @@ class USymbolCloud extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -132,11 +134,10 @@ class USymbolCloud extends USymbol {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean manageHorizontalLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -83,6 +83,7 @@ class USymbolCollections extends USymbol {
|
|||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -107,8 +108,9 @@ class USymbolCollections extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
public void drawU(UGraphic ug) {
|
||||
final Dimension2D dim = calculateDimension(ug.getStringBounder());
|
||||
|
|
|
@ -72,6 +72,7 @@ class USymbolComponent1 extends USymbol {
|
|||
return new Margin(10, 10, 10, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -95,8 +96,9 @@ class USymbolComponent1 extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ class USymbolComponent2 extends USymbol {
|
|||
return new Margin(10 + 5, 20 + 5, 15 + 5, 5 + 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -99,8 +100,9 @@ class USymbolComponent2 extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -52,8 +52,6 @@ class USymbolDatabase extends USymbol {
|
|||
return SkinParameter.DATABASE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void drawDatabase(UGraphic ug, double width, double height, boolean shadowing) {
|
||||
final UPath shape = new UPath();
|
||||
if (shadowing) {
|
||||
|
@ -114,7 +112,9 @@ class USymbolDatabase extends USymbol {
|
|||
return new Margin(10, 10, 24, 5);
|
||||
}
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -135,8 +135,9 @@ class USymbolDatabase extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -80,6 +80,7 @@ class USymbolFile extends USymbol {
|
|||
return new Margin(10, 10, 10, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -102,8 +103,9 @@ class USymbolFile extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -105,6 +105,7 @@ public class USymbolFolder extends USymbol {
|
|||
return new Margin(10, 10 + 10, 10 + 3, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(final TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
if (name == null) {
|
||||
|
@ -133,8 +134,9 @@ public class USymbolFolder extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -94,6 +94,7 @@ class USymbolFrame extends USymbol {
|
|||
return new Margin(10 + 5, 20 + 5, 15 + 5, 5 + 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -116,8 +117,9 @@ class USymbolFrame extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -109,6 +109,7 @@ class USymbolNode extends USymbol {
|
|||
return new Margin(10 + 5, 20 + 5, 15 + 5, 5 + 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -131,8 +132,9 @@ class USymbolNode extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -127,6 +127,7 @@ class USymbolQueue extends USymbol {
|
|||
return new Margin(5, 15, 5, 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -150,8 +151,9 @@ class USymbolQueue extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
// return new TextBlock() {
|
||||
//
|
||||
|
|
|
@ -76,6 +76,7 @@ class USymbolRect extends USymbol {
|
|||
return new Margin(10, 10, 10, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -99,8 +100,9 @@ class USymbolRect extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, final HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
public void drawU(UGraphic ug) {
|
||||
final Dimension2D dim = calculateDimension(ug.getStringBounder());
|
||||
|
@ -119,7 +121,14 @@ class USymbolRect extends USymbol {
|
|||
}
|
||||
stereotype.drawU(ug.apply(new UTranslate(posStereoX, posStereoY)));
|
||||
final Dimension2D dimTitle = title.calculateDimension(ug.getStringBounder());
|
||||
final double posTitle = (width - dimTitle.getWidth()) / 2;
|
||||
final double posTitle;
|
||||
if (labelAlignment == HorizontalAlignment.LEFT) {
|
||||
posTitle = 3;
|
||||
} else if (labelAlignment == HorizontalAlignment.RIGHT) {
|
||||
posTitle = width - dimTitle.getWidth() - 3;
|
||||
} else {
|
||||
posTitle = (width - dimTitle.getWidth()) / 2;
|
||||
}
|
||||
title.drawU(ug.apply(new UTranslate(posTitle, 2 + dimStereo.getHeight())));
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ import net.sourceforge.plantuml.ugraphic.UTranslate;
|
|||
|
||||
abstract class USymbolSimpleAbstract extends USymbol {
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
if (stereotype == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -79,8 +81,9 @@ abstract class USymbolSimpleAbstract extends USymbol {
|
|||
|
||||
abstract protected TextBlock getDrawing(final SymbolContext symbolContext);
|
||||
|
||||
public TextBlock asBig(final TextBlock title, TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ class USymbolStack extends USymbol {
|
|||
return new Margin(25, 25, 10, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
@ -98,8 +99,9 @@ class USymbolStack extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,9 @@ class USymbolStorage extends USymbol {
|
|||
return new Margin(10, 10, 10, 10);
|
||||
}
|
||||
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype, final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -84,8 +86,9 @@ class USymbolStorage extends USymbol {
|
|||
};
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
@ -106,11 +109,10 @@ class USymbolStorage extends USymbol {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean manageHorizontalLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -47,13 +47,15 @@ class USymbolTogether extends USymbol {
|
|||
return SkinParameter.STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBlock asSmall(TextBlock name, final TextBlock label, final TextBlock stereotype,
|
||||
final SymbolContext symbolContext) {
|
||||
return TextBlockUtils.empty(10, 10);
|
||||
}
|
||||
|
||||
public TextBlock asBig(final TextBlock title, final TextBlock stereotype, final double width, final double height,
|
||||
final SymbolContext symbolContext) {
|
||||
@Override
|
||||
public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype,
|
||||
final double width, final double height, final SymbolContext symbolContext) {
|
||||
return new AbstractTextBlock() {
|
||||
|
||||
public void drawU(UGraphic ug) {
|
||||
|
|
|
@ -239,4 +239,11 @@ public class Colors {
|
|||
return shadowing;
|
||||
}
|
||||
|
||||
public UStroke muteStroke(UStroke stroke) {
|
||||
if (lineStyle == null) {
|
||||
return stroke;
|
||||
}
|
||||
return lineStyle.muteStroke(stroke);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,20 @@ public class Defines implements Truth {
|
|||
}
|
||||
|
||||
public static Defines createEmpty() {
|
||||
return new Defines();
|
||||
return createEmpty(null);
|
||||
}
|
||||
|
||||
public static Defines createEmpty(String filename) {
|
||||
final Defines result = new Defines();
|
||||
if (filename != null) {
|
||||
result.overrideFilename(filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void overrideFilename(String filename) {
|
||||
environment.put("filename", filename);
|
||||
environment.put("filenameNoExtension", nameNoExtension(filename));
|
||||
}
|
||||
|
||||
public void importFrom(Defines other) {
|
||||
|
@ -85,16 +98,15 @@ public class Defines implements Truth {
|
|||
if (file == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
final Defines result = createEmpty();
|
||||
final Defines result = createEmpty(file.getName());
|
||||
result.environment.put("filedate", new Date(file.lastModified()).toString());
|
||||
result.environment.put("filename", file.getName());
|
||||
result.environment.put("filenameNoExtension", nameNoExtension(file));
|
||||
// result.environment.put("filename", file.getName());
|
||||
// result.environment.put("filenameNoExtension", nameNoExtension(file));
|
||||
result.environment.put("dirpath", file.getAbsoluteFile().getParentFile().getAbsolutePath().replace('\\', '/'));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String nameNoExtension(File file) {
|
||||
final String name = file.getName();
|
||||
private static String nameNoExtension(String name) {
|
||||
final int x = name.lastIndexOf('.');
|
||||
if (x == -1) {
|
||||
return name;
|
||||
|
|
|
@ -59,6 +59,7 @@ public class Preprocessor implements ReadLine {
|
|||
private static final String ARG = "(?:\\(" + ID_ARG + "(?:," + ID_ARG + ")*?\\))?";
|
||||
private static final Pattern2 definePattern = MyPattern.cmpile("^[%s]*!define[%s]+(" + ID + ARG + ")"
|
||||
+ "(?:[%s]+(.*))?$");
|
||||
private static final Pattern2 filenamePattern = MyPattern.cmpile("^[%s]*!filename[%s]+(.+)$");
|
||||
private static final Pattern2 undefPattern = MyPattern.cmpile("^[%s]*!undef[%s]+(" + ID + ")$");
|
||||
private static final Pattern2 definelongPattern = MyPattern.cmpile("^[%s]*!definelong[%s]+(" + ID + ARG + ")");
|
||||
private static final Pattern2 enddefinelongPattern = MyPattern.cmpile("^[%s]*" + END_DEFINE_LONG + "[%s]*$");
|
||||
|
@ -103,7 +104,11 @@ public class Preprocessor implements ReadLine {
|
|||
this.defines.restoreState();
|
||||
}
|
||||
|
||||
Matcher2 m = definePattern.matcher(s);
|
||||
Matcher2 m = filenamePattern.matcher(s);
|
||||
if (m.find()) {
|
||||
return manageFilename(m);
|
||||
}
|
||||
m = definePattern.matcher(s);
|
||||
if (m.find()) {
|
||||
return manageDefine(m, s.toString().trim().endsWith("()"));
|
||||
}
|
||||
|
@ -170,14 +175,20 @@ public class Preprocessor implements ReadLine {
|
|||
if (read == null) {
|
||||
return null;
|
||||
}
|
||||
def.add(read.toString2());
|
||||
if (enddefinelongPattern.matcher(read).find()) {
|
||||
defines.define(group1, def, emptyParentheses);
|
||||
return this.readLine();
|
||||
}
|
||||
def.add(read.toString2());
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence2 manageFilename(Matcher2 m) throws IOException {
|
||||
final String group1 = m.group(1);
|
||||
this.defines.overrideFilename(group1);
|
||||
return this.readLine();
|
||||
}
|
||||
|
||||
private CharSequence2 manageDefine(Matcher2 m, boolean emptyParentheses) throws IOException {
|
||||
final String group1 = m.group(1);
|
||||
final String group2 = m.group(2);
|
||||
|
|
|
@ -67,6 +67,7 @@ public class PreprocessorInclude implements ReadLine {
|
|||
|
||||
private static final Pattern2 includeDefPattern = MyPattern.cmpile("^[%s]*!includedef[%s]+[%g]?([^%g]+)[%g]?$");
|
||||
private static final Pattern2 includePattern = MyPattern.cmpile("^[%s]*!include[%s]+[%g]?([^%g]+)[%g]?$");
|
||||
private static final Pattern2 includePatternStdlib = MyPattern.cmpile("^[%s]*!include[%s]+(\\<[^%g]+\\>)$");
|
||||
private static final Pattern2 includeManyPattern = MyPattern.cmpile("^[%s]*!include_many[%s]+[%g]?([^%g]+)[%g]?$");
|
||||
private static final Pattern2 includeURLPattern = MyPattern.cmpile("^[%s]*!includeurl[%s]+[%g]?([^%g]+)[%g]?$");
|
||||
|
||||
|
@ -100,7 +101,7 @@ public class PreprocessorInclude implements ReadLine {
|
|||
this.config = config;
|
||||
this.defines = defines;
|
||||
this.charset = charset;
|
||||
this.reader2 = reader;
|
||||
this.reader2 = new ReadLineQuoteComment(reader);
|
||||
this.definitionsContainer = definitionsContainer;
|
||||
this.filesUsedCurrent = filesUsedCurrent;
|
||||
this.filesUsedGlobal = filesUsedGlobal;
|
||||
|
@ -161,6 +162,11 @@ public class PreprocessorInclude implements ReadLine {
|
|||
if (m3.find()) {
|
||||
return manageDefinitionInclude(s, m3);
|
||||
}
|
||||
} else {
|
||||
final Matcher2 m1 = includePatternStdlib.matcher(s);
|
||||
if (m1.find()) {
|
||||
return manageFileInclude(s, m1, false);
|
||||
}
|
||||
}
|
||||
final Matcher2 mUrl = includeURLPattern.matcher(s);
|
||||
if (s.getPreprocessorError() == null && mUrl.find()) {
|
||||
|
@ -260,6 +266,10 @@ public class PreprocessorInclude implements ReadLine {
|
|||
}
|
||||
|
||||
private InputStream getStdlibInputStream(String filename) {
|
||||
return Stdlib.getResourceAsStream(filename);
|
||||
}
|
||||
|
||||
private InputStream getStdlibInputStreamOld(String filename) {
|
||||
if (filename.endsWith(".puml") == false) {
|
||||
filename = filename + ".puml";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.preproc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sourceforge.plantuml.CharSequence2;
|
||||
import net.sourceforge.plantuml.CharSequence2Impl;
|
||||
|
||||
public class ReadLineQuoteComment implements ReadLine {
|
||||
|
||||
private final ReadLine raw;
|
||||
private boolean longComment = false;
|
||||
|
||||
public ReadLineQuoteComment(ReadLine source) {
|
||||
this.raw = source;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
raw.close();
|
||||
}
|
||||
|
||||
public CharSequence2 readLine() throws IOException {
|
||||
while (true) {
|
||||
final CharSequence2 result = raw.readLine();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
final String trim = result.toString().replace('\t', ' ').trim();
|
||||
if (this.longComment && trim.endsWith("'/")) {
|
||||
this.longComment = false;
|
||||
continue;
|
||||
}
|
||||
if (this.longComment) {
|
||||
continue;
|
||||
}
|
||||
if (trim.startsWith("'")) {
|
||||
continue;
|
||||
}
|
||||
if (trim.startsWith("/'") && trim.endsWith("'/")) {
|
||||
continue;
|
||||
}
|
||||
if (trim.startsWith("/'") && trim.contains("'/") == false) {
|
||||
this.longComment = true;
|
||||
continue;
|
||||
}
|
||||
return ((CharSequence2Impl) result).removeInnerComment();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package net.sourceforge.plantuml.preproc;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sourceforge.plantuml.Log;
|
||||
|
||||
import org.brotli.dec.BrotliInputStream;
|
||||
|
||||
public class Stdlib {
|
||||
|
||||
static private final Map<String, Stdlib> all = new ConcurrentHashMap<String, Stdlib>();
|
||||
|
||||
static class Portion {
|
||||
int position;
|
||||
int length;
|
||||
|
||||
Portion(int position, int length) {
|
||||
this.position = position;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte[] of(DataInputStream is) throws IOException {
|
||||
is.skipBytes(position);
|
||||
final byte result[] = new byte[length];
|
||||
is.readFully(result);
|
||||
is.close();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, Portion> data = new HashMap<String, Portion>();
|
||||
private final Map<String, SoftReference<byte[]>> cache = new ConcurrentHashMap<String, SoftReference<byte[]>>();
|
||||
private final String name;
|
||||
private final Map<String, String> info = new HashMap<String, String>();
|
||||
|
||||
public static InputStream getResourceAsStream(String fullname) {
|
||||
fullname = fullname.toLowerCase().replace(".puml", "");
|
||||
final int last = fullname.indexOf('/');
|
||||
if (last == -1) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final Stdlib folder = retrieve(fullname.substring(0, last));
|
||||
if (folder == null) {
|
||||
return null;
|
||||
}
|
||||
final byte[] data = folder.getData(fullname.substring(last + 1));
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return new ByteArrayInputStream(data);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream getInternalInputStream(String fullname, String extension) {
|
||||
final String res = "/stdlib/" + fullname + extension;
|
||||
return Stdlib.class.getResourceAsStream(res);
|
||||
}
|
||||
|
||||
private static Stdlib retrieve(final String name) throws IOException {
|
||||
Stdlib result = all.get(name);
|
||||
if (result == null) {
|
||||
result = new Stdlib(name);
|
||||
all.put(name, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Stdlib(String name) throws IOException {
|
||||
this.name = name;
|
||||
Log.info("Opening archive " + name);
|
||||
int pos = 0;
|
||||
final DataInputStream is = getDataIndex();
|
||||
fillMap(is.readUTF());
|
||||
while (true) {
|
||||
final String filename = is.readUTF();
|
||||
if (filename.length() == 0) {
|
||||
break;
|
||||
}
|
||||
final int len = is.readInt();
|
||||
this.data.put(filename.toLowerCase(), new Portion(pos, len));
|
||||
pos += len;
|
||||
}
|
||||
is.close();
|
||||
}
|
||||
|
||||
private void fillMap(String infoString) {
|
||||
for (String s : infoString.split("\n")) {
|
||||
if (s.contains("=")) {
|
||||
final String data[] = s.split("=");
|
||||
this.info.put(data[0], data[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DataInputStream getDataIndex() {
|
||||
final InputStream raw = getInternalInputStream(name, "-idx.repx");
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
return new DataInputStream(raw);
|
||||
}
|
||||
|
||||
private DataInputStream getFullTexts() throws IOException {
|
||||
final InputStream raw = getInternalInputStream(name, "-dat.repx");
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
return new DataInputStream(new BrotliInputStream(raw));
|
||||
}
|
||||
|
||||
private byte[] getData(String filename) throws IOException {
|
||||
final SoftReference<byte[]> ref = cache.get(filename);
|
||||
byte[] result = ref == null ? null : ref.get();
|
||||
final String full = name + "/" + filename + ".puml";
|
||||
if (result == null) {
|
||||
result = getDataSlow(filename);
|
||||
cache.put(filename, new SoftReference<byte[]>(result));
|
||||
Log.info("Reading " + full);
|
||||
} else {
|
||||
Log.info("Retrieving " + full);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] getDataSlow(String filename) throws IOException {
|
||||
final Portion portion = data.get(filename);
|
||||
return portion.of(getFullTexts());
|
||||
}
|
||||
|
||||
public static void extractStdLib() throws IOException {
|
||||
for (String name : getAll()) {
|
||||
final Stdlib folder = new Stdlib(name);
|
||||
folder.extractMeFull(new File("stdlib", name));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getAll() throws IOException {
|
||||
final List<String> result = new ArrayList<String>();
|
||||
final InputStream home = getInternalInputStream("home", ".repx");
|
||||
final BufferedReader br = new BufferedReader(new InputStreamReader(home));
|
||||
String name;
|
||||
while ((name = br.readLine()) != null) {
|
||||
result.add(name);
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
private void extractMeFull(File dir) throws IOException {
|
||||
final DataInputStream idx = getDataIndex();
|
||||
final DataInputStream txt = getFullTexts();
|
||||
final String infoString = idx.readUTF();
|
||||
while (true) {
|
||||
final String filename = idx.readUTF();
|
||||
if (filename.length() == 0) {
|
||||
idx.close();
|
||||
txt.close();
|
||||
return;
|
||||
}
|
||||
final int len = idx.readInt();
|
||||
final File f = new File("stdlib/" + name + "/" + filename + ".puml");
|
||||
f.getParentFile().mkdirs();
|
||||
final FileOutputStream fos = new FileOutputStream(f);
|
||||
final byte data[] = new byte[len];
|
||||
txt.readFully(data);
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void addInfoVersion(List<String> strings) {
|
||||
try {
|
||||
for (String name : getAll()) {
|
||||
final Stdlib folder = new Stdlib(name);
|
||||
strings.add("* " + name + " (Version " + folder.getVersion() + ")");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getVersion() {
|
||||
return info.get("VERSION");
|
||||
}
|
||||
}
|
|
@ -57,8 +57,8 @@ public class PSystemProjectFactory2 extends UmlDiagramFactory {
|
|||
protected List<Command> createCommands() {
|
||||
final List<Command> cmds = new ArrayList<Command>();
|
||||
cmds.add(new CommandNope());
|
||||
cmds.add(new CommandComment());
|
||||
cmds.add(new CommandMultilinesComment());
|
||||
// cmds.add(new CommandComment());
|
||||
// cmds.add(new CommandMultilinesComment());
|
||||
cmds.add(new CommandAffectation());
|
||||
cmds.add(new CommandCloseWeekDay());
|
||||
return cmds;
|
||||
|
|
|
@ -63,8 +63,8 @@ public class GanttDiagramFactory extends UmlDiagramFactory {
|
|||
final List<Command> cmds = new ArrayList<Command>();
|
||||
//addCommonCommands(cmds);
|
||||
cmds.add(new CommandNope());
|
||||
cmds.add(new CommandComment());
|
||||
cmds.add(new CommandMultilinesComment());
|
||||
// cmds.add(new CommandComment());
|
||||
// cmds.add(new CommandMultilinesComment());
|
||||
for (Command cmd : getLanguageCommands()) {
|
||||
cmds.add(cmd);
|
||||
}
|
||||
|
|
|
@ -48,12 +48,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.plantuml.AlignParam;
|
||||
import net.sourceforge.plantuml.ColorParam;
|
||||
import net.sourceforge.plantuml.Dimension2DDouble;
|
||||
import net.sourceforge.plantuml.FontParam;
|
||||
import net.sourceforge.plantuml.ISkinParam;
|
||||
import net.sourceforge.plantuml.LineParam;
|
||||
import net.sourceforge.plantuml.OptionFlags;
|
||||
import net.sourceforge.plantuml.SkinParamUtils;
|
||||
import net.sourceforge.plantuml.StringUtils;
|
||||
import net.sourceforge.plantuml.UmlDiagramType;
|
||||
|
@ -66,6 +66,7 @@ import net.sourceforge.plantuml.cucadiagram.Member;
|
|||
import net.sourceforge.plantuml.cucadiagram.MethodsOrFieldsArea;
|
||||
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
||||
import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersion;
|
||||
import net.sourceforge.plantuml.graphic.HorizontalAlignment;
|
||||
import net.sourceforge.plantuml.graphic.HtmlColor;
|
||||
import net.sourceforge.plantuml.graphic.HtmlColorTransparent;
|
||||
import net.sourceforge.plantuml.graphic.StringBounder;
|
||||
|
@ -74,6 +75,7 @@ import net.sourceforge.plantuml.graphic.TextBlockEmpty;
|
|||
import net.sourceforge.plantuml.graphic.TextBlockWidth;
|
||||
import net.sourceforge.plantuml.graphic.USymbol;
|
||||
import net.sourceforge.plantuml.graphic.color.ColorType;
|
||||
import net.sourceforge.plantuml.graphic.color.Colors;
|
||||
import net.sourceforge.plantuml.posimo.Moveable;
|
||||
import net.sourceforge.plantuml.skin.rose.Rose;
|
||||
import net.sourceforge.plantuml.svek.image.EntityImageState;
|
||||
|
@ -301,7 +303,10 @@ public class Cluster implements Moveable {
|
|||
}
|
||||
|
||||
public void drawU(UGraphic ug, UStroke stroke, final UmlDiagramType umlDiagramType, final ISkinParam skinParam2) {
|
||||
ug.draw(new UComment("cluster " + group.getCode().getFullName()));
|
||||
final String fullName = group.getCode().getFullName();
|
||||
if (fullName.startsWith("##") == false) {
|
||||
ug.draw(new UComment("cluster " + fullName));
|
||||
}
|
||||
final Stereotype stereotype = group.getStereotype();
|
||||
HtmlColor borderColor;
|
||||
if (umlDiagramType == UmlDiagramType.STATE) {
|
||||
|
@ -351,9 +356,11 @@ public class Cluster implements Moveable {
|
|||
final double roundCorner = group.getUSymbol() == null ? 0 : group.getUSymbol().getSkinParameter()
|
||||
.getRoundCorner(skinParam, stereotype);
|
||||
|
||||
final UStroke stroke2 = getStrokeInternal(skinParam2);
|
||||
final ClusterDecoration decoration = new ClusterDecoration(style, group.getUSymbol(), ztitle, zstereo,
|
||||
minX, minY, maxX, maxY, getStroke(skinParam2, group.getStereotype()));
|
||||
decoration.drawU(ug, back, borderColor, skinParam2.shadowing(), roundCorner);
|
||||
minX, minY, maxX, maxY, stroke2);
|
||||
decoration.drawU(ug, back, borderColor, skinParam2.shadowing(), roundCorner,
|
||||
skinParam2.getHorizontalAlignment(AlignParam.PACKAGE_TITLE_ALIGNMENT, null));
|
||||
return;
|
||||
}
|
||||
final URectangle rect = new URectangle(maxX - minX, maxY - minY);
|
||||
|
@ -372,10 +379,17 @@ public class Cluster implements Moveable {
|
|||
|
||||
}
|
||||
|
||||
private UStroke getStroke(ISkinParam skinParam, Stereotype stereo) {
|
||||
UStroke stroke = skinParam.getThickness(LineParam.packageBorder, stereo);
|
||||
private UStroke getStrokeInternal(ISkinParam skinParam) {
|
||||
final Colors colors = group.getColors(skinParam);
|
||||
if (colors.getSpecificLineStroke() != null) {
|
||||
return colors.getSpecificLineStroke();
|
||||
}
|
||||
if (group.getUSymbol() != null) {
|
||||
return group.getUSymbol().getSkinParameter().getStroke(skinParam, group.getStereotype());
|
||||
}
|
||||
UStroke stroke = skinParam.getThickness(LineParam.packageBorder, group.getStereotype());
|
||||
if (stroke == null) {
|
||||
stroke = new UStroke(2.0);
|
||||
stroke = new UStroke(1.5);
|
||||
}
|
||||
return stroke;
|
||||
}
|
||||
|
@ -721,6 +735,9 @@ public class Cluster implements Moveable {
|
|||
Line.appendTable(sblabel, getTitleAndAttributeWidth(), getTitleAndAttributeHeight() - 5, colorTitle);
|
||||
sblabel.append(">");
|
||||
label = sblabel.toString();
|
||||
final HorizontalAlignment align = skinParam
|
||||
.getHorizontalAlignment(AlignParam.PACKAGE_TITLE_ALIGNMENT, null);
|
||||
sb.append("labeljust=\"" + align.getGraphVizValue() + "\";");
|
||||
} else {
|
||||
label = "\"\"";
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
*/
|
||||
package net.sourceforge.plantuml.svek;
|
||||
|
||||
import net.sourceforge.plantuml.graphic.HorizontalAlignment;
|
||||
import net.sourceforge.plantuml.graphic.HtmlColor;
|
||||
import net.sourceforge.plantuml.graphic.SymbolContext;
|
||||
import net.sourceforge.plantuml.graphic.TextBlock;
|
||||
|
@ -85,15 +86,16 @@ public class ClusterDecoration {
|
|||
public final static int marginTitleY1 = 3;
|
||||
public final static int marginTitleY2 = 3;
|
||||
|
||||
public void drawU(UGraphic ug, HtmlColor backColor, HtmlColor borderColor, boolean shadowing, double roundCorner) {
|
||||
public void drawU(UGraphic ug, HtmlColor backColor, HtmlColor borderColor, boolean shadowing, double roundCorner,
|
||||
HorizontalAlignment titleAlignment) {
|
||||
final SymbolContext biColor = new SymbolContext(backColor, borderColor);
|
||||
if (symbol == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
final SymbolContext symbolContext = biColor.withShadow(shadowing).withStroke(defaultStroke)
|
||||
.withRoundCorner(roundCorner);
|
||||
symbol.asBig(title, stereo, maxX - minX, maxY - minY, symbolContext)
|
||||
.drawU(ug.apply(new UTranslate(minX, minY)));
|
||||
symbol.asBig(title, titleAlignment, stereo, maxX - minX, maxY - minY, symbolContext).drawU(
|
||||
ug.apply(new UTranslate(minX, minY)));
|
||||
// return;
|
||||
// }
|
||||
// if (style == PackageStyle.NODE) {
|
||||
|
|
|
@ -60,6 +60,7 @@ import net.sourceforge.plantuml.graphic.TextBlockUtils;
|
|||
import net.sourceforge.plantuml.graphic.USymbol;
|
||||
import net.sourceforge.plantuml.graphic.USymbolFolder;
|
||||
import net.sourceforge.plantuml.graphic.color.ColorType;
|
||||
import net.sourceforge.plantuml.graphic.color.Colors;
|
||||
import net.sourceforge.plantuml.svek.AbstractEntityImage;
|
||||
import net.sourceforge.plantuml.svek.Margins;
|
||||
import net.sourceforge.plantuml.svek.ShapeType;
|
||||
|
@ -104,7 +105,8 @@ public class EntityImageDescription extends AbstractEntityImage {
|
|||
|
||||
this.url = entity.getUrl99();
|
||||
|
||||
HtmlColor backcolor = getEntity().getColors(getSkinParam()).getColor(ColorType.BACK);
|
||||
final Colors colors = entity.getColors(skinParam);
|
||||
HtmlColor backcolor = colors.getColor(ColorType.BACK);
|
||||
if (backcolor == null) {
|
||||
backcolor = SkinParamUtils.getColor(getSkinParam(), symbol.getColorParamBack(), getStereo());
|
||||
}
|
||||
|
@ -112,7 +114,8 @@ public class EntityImageDescription extends AbstractEntityImage {
|
|||
assert getStereo() == stereotype;
|
||||
final HtmlColor forecolor = SkinParamUtils.getColor(getSkinParam(), symbol.getColorParamBorder(), stereotype);
|
||||
final double roundCorner = symbol.getSkinParameter().getRoundCorner(getSkinParam(), stereotype);
|
||||
final UStroke stroke = symbol.getSkinParameter().getStroke(getSkinParam(), stereotype);
|
||||
final UStroke stroke = colors.muteStroke(symbol.getSkinParameter().getStroke(getSkinParam(), stereotype));
|
||||
|
||||
final SymbolContext ctx = new SymbolContext(backcolor, forecolor).withStroke(stroke)
|
||||
.withShadow(getSkinParam().shadowing2(symbol.getSkinParameter())).withRoundCorner(roundCorner);
|
||||
|
||||
|
@ -162,19 +165,25 @@ public class EntityImageDescription extends AbstractEntityImage {
|
|||
|
||||
@Override
|
||||
public Margins getShield(StringBounder stringBounder) {
|
||||
if (hideText && (useRankSame == false || hasSomeHorizontalLink((ILeaf) getEntity(), links) == false)) {
|
||||
final Dimension2D dimStereo = stereo.calculateDimension(stringBounder);
|
||||
final Dimension2D dimDesc = desc.calculateDimension(stringBounder);
|
||||
final Dimension2D dimSmall = asSmall.calculateDimension(stringBounder);
|
||||
final double x = Math.max(dimStereo.getWidth(), dimDesc.getWidth());
|
||||
double suppX = x - dimSmall.getWidth();
|
||||
if (suppX < 1) {
|
||||
suppX = 1;
|
||||
}
|
||||
final double y = MathUtils.max(1, dimDesc.getHeight(), dimStereo.getHeight());
|
||||
return new Margins(suppX / 2, suppX / 2, y, y);
|
||||
if (hideText == false) {
|
||||
return Margins.NONE;
|
||||
}
|
||||
return Margins.NONE;
|
||||
if (useRankSame && hasSomeHorizontalLink((ILeaf) getEntity(), links)) {
|
||||
return Margins.NONE;
|
||||
}
|
||||
if (hasSomeHorizontalLinkDoubleDecorated((ILeaf) getEntity(), links)) {
|
||||
return Margins.NONE;
|
||||
}
|
||||
final Dimension2D dimStereo = stereo.calculateDimension(stringBounder);
|
||||
final Dimension2D dimDesc = desc.calculateDimension(stringBounder);
|
||||
final Dimension2D dimSmall = asSmall.calculateDimension(stringBounder);
|
||||
final double x = Math.max(dimStereo.getWidth(), dimDesc.getWidth());
|
||||
double suppX = x - dimSmall.getWidth();
|
||||
if (suppX < 1) {
|
||||
suppX = 1;
|
||||
}
|
||||
final double y = MathUtils.max(1, dimDesc.getHeight(), dimStereo.getHeight());
|
||||
return new Margins(suppX / 2, suppX / 2, y, y);
|
||||
}
|
||||
|
||||
private boolean hasSomeHorizontalLink(ILeaf leaf, Collection<Link> links) {
|
||||
|
@ -186,6 +195,15 @@ public class EntityImageDescription extends AbstractEntityImage {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean hasSomeHorizontalLinkDoubleDecorated(ILeaf leaf, Collection<Link> links) {
|
||||
for (Link link : links) {
|
||||
if (link.getLength() == 1 && link.contains(leaf) && link.getType().isDoubleDecorated()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final public void drawU(UGraphic ug) {
|
||||
ug.draw(new UComment("entity " + getEntity().getCode().getFullName()));
|
||||
if (url != null) {
|
||||
|
|
|
@ -37,6 +37,7 @@ package net.sourceforge.plantuml.svek.image;
|
|||
|
||||
import java.awt.geom.Dimension2D;
|
||||
|
||||
import net.sourceforge.plantuml.AlignParam;
|
||||
import net.sourceforge.plantuml.ColorParam;
|
||||
import net.sourceforge.plantuml.Dimension2DDouble;
|
||||
import net.sourceforge.plantuml.FontParam;
|
||||
|
@ -105,7 +106,7 @@ public class EntityImageEmptyPackage extends AbstractEntityImage {
|
|||
private UStroke getStroke() {
|
||||
UStroke stroke = getSkinParam().getThickness(LineParam.packageBorder, getStereo());
|
||||
if (stroke == null) {
|
||||
stroke = new UStroke(2.0);
|
||||
stroke = new UStroke(1.5);
|
||||
}
|
||||
return stroke;
|
||||
}
|
||||
|
@ -128,7 +129,8 @@ public class EntityImageEmptyPackage extends AbstractEntityImage {
|
|||
stereoBlock, 0, 0, widthTotal, heightTotal, getStroke());
|
||||
|
||||
decoration.drawU(ug, back, SkinParamUtils.getColor(getSkinParam(), ColorParam.packageBorder, getStereo()),
|
||||
getSkinParam().shadowing(), roundCorner);
|
||||
getSkinParam().shadowing(), roundCorner,
|
||||
getSkinParam().getHorizontalAlignment(AlignParam.PACKAGE_TITLE_ALIGNMENT, null));
|
||||
|
||||
if (url != null) {
|
||||
ug.closeAction();
|
||||
|
|
|
@ -296,43 +296,46 @@ public class SvgGraphics {
|
|||
}
|
||||
|
||||
public void closeLink() {
|
||||
if (pendingLink2.size() > 0) {
|
||||
final Element element = pendingLink2.get(0);
|
||||
pendingLink2.remove(0);
|
||||
getG().appendChild(element);
|
||||
if (pendingAction.size() > 0) {
|
||||
final Element element = pendingAction.get(0);
|
||||
pendingAction.remove(0);
|
||||
if (element.getFirstChild() != null) {
|
||||
// Empty link
|
||||
getG().appendChild(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Element> pendingLink2 = new ArrayList<Element>();
|
||||
private final List<Element> pendingAction = new ArrayList<Element>();
|
||||
|
||||
public void openLink(String url, String title, String target) {
|
||||
if (url == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (pendingLink2.size() > 0) {
|
||||
if (pendingAction.size() > 0) {
|
||||
closeLink();
|
||||
}
|
||||
|
||||
pendingLink2.add(0, (Element) document.createElement("a"));
|
||||
pendingLink2.get(0).setAttribute("target", target);
|
||||
pendingLink2.get(0).setAttribute("xlink:href", url);
|
||||
pendingLink2.get(0).setAttribute("xlink:type", "simple");
|
||||
pendingLink2.get(0).setAttribute("xlink:actuate", "onRequest");
|
||||
pendingLink2.get(0).setAttribute("xlink:show", "new");
|
||||
pendingAction.add(0, (Element) document.createElement("a"));
|
||||
pendingAction.get(0).setAttribute("target", target);
|
||||
pendingAction.get(0).setAttribute("xlink:href", url);
|
||||
pendingAction.get(0).setAttribute("xlink:type", "simple");
|
||||
pendingAction.get(0).setAttribute("xlink:actuate", "onRequest");
|
||||
pendingAction.get(0).setAttribute("xlink:show", "new");
|
||||
if (title == null) {
|
||||
pendingLink2.get(0).setAttribute("xlink:title", url);
|
||||
pendingAction.get(0).setAttribute("xlink:title", url);
|
||||
} else {
|
||||
title = title.replaceAll("\\\\n", "\n");
|
||||
pendingLink2.get(0).setAttribute("xlink:title", title);
|
||||
pendingAction.get(0).setAttribute("xlink:title", title);
|
||||
}
|
||||
}
|
||||
|
||||
public final Element getG() {
|
||||
if (pendingLink2.size() == 0) {
|
||||
if (pendingAction.size() == 0) {
|
||||
return gRoot;
|
||||
}
|
||||
return pendingLink2.get(0);
|
||||
return pendingAction.get(0);
|
||||
}
|
||||
|
||||
public void svgRectangle(double x, double y, double width, double height, double rx, double ry, double deltaShadow,
|
||||
|
|
|
@ -394,12 +394,13 @@ public class ImageBuilder {
|
|||
}
|
||||
final UGraphicSvg ug;
|
||||
if (mybackcolor instanceof HtmlColorGradient) {
|
||||
ug = new UGraphicSvg(dim, colorMapper, (HtmlColorGradient) mybackcolor, false, scale, svgLinkTarget, hover, seed);
|
||||
ug = new UGraphicSvg(dim, colorMapper, (HtmlColorGradient) mybackcolor, false, scale, svgLinkTarget, hover,
|
||||
seed);
|
||||
} else if (backColor == null || backColor.equals(Color.WHITE)) {
|
||||
ug = new UGraphicSvg(dim, colorMapper, false, scale, svgLinkTarget, hover, seed);
|
||||
} else {
|
||||
ug = new UGraphicSvg(dim, colorMapper, StringUtils.getAsHtml(backColor), false, scale, svgLinkTarget, hover,
|
||||
seed);
|
||||
ug = new UGraphicSvg(dim, colorMapper, StringUtils.getAsHtml(backColor), false, scale, svgLinkTarget,
|
||||
hover, seed);
|
||||
}
|
||||
return ug;
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ public class UText implements UShape {
|
|||
|
||||
private final String text;
|
||||
private final FontConfiguration font;
|
||||
private final String ariaLabel;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -51,9 +52,18 @@ public class UText implements UShape {
|
|||
}
|
||||
|
||||
public UText(String text, FontConfiguration font) {
|
||||
this(text, font, null);
|
||||
}
|
||||
|
||||
private UText(String text, FontConfiguration font, String ariaLabel) {
|
||||
assert text.indexOf('\t') == -1;
|
||||
this.text = text;
|
||||
this.font = font;
|
||||
this.ariaLabel = ariaLabel;
|
||||
}
|
||||
|
||||
public UText withAriaLabel(String newAriaLabel) {
|
||||
return new UText(text, font, newAriaLabel);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
|
@ -70,6 +80,10 @@ public class UText implements UShape {
|
|||
return descent;
|
||||
}
|
||||
|
||||
public String getAriaLabel() {
|
||||
return ariaLabel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -199,7 +199,9 @@ public class UGraphicG2d extends AbstractUGraphic<Graphics2D> implements EnsureV
|
|||
|
||||
public void ensureVisible(double x, double y) {
|
||||
for (Url u : urls) {
|
||||
u.ensureVisible(x, y);
|
||||
if (getClip() == null || getClip().isInside(x, y)) {
|
||||
u.ensureVisible(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,11 @@ import java.util.List;
|
|||
|
||||
import net.sourceforge.plantuml.StringUtils;
|
||||
import net.sourceforge.plantuml.code.AsciiEncoder;
|
||||
import net.sourceforge.plantuml.code.AsciiEncoderFinalZeros;
|
||||
import net.sourceforge.plantuml.code.CompressionZlib;
|
||||
import net.sourceforge.plantuml.code.CompressionZopfliZlib;
|
||||
import net.sourceforge.plantuml.code.PairInt;
|
||||
import net.sourceforge.plantuml.code.SpiralOnRectangle;
|
||||
import net.sourceforge.plantuml.ugraphic.ColorChangerMonochrome;
|
||||
|
||||
public enum SpriteGrayLevel {
|
||||
|
@ -113,7 +117,6 @@ public enum SpriteGrayLevel {
|
|||
// final int type = img.getType();
|
||||
|
||||
final List<String> result = new ArrayList<String>();
|
||||
final AsciiEncoder encoder = new AsciiEncoder();
|
||||
|
||||
for (int y = 0; y < height; y += 2) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
@ -123,7 +126,7 @@ public enum SpriteGrayLevel {
|
|||
final int level2 = getGrayOn16(img, x, y + 1) / 2;
|
||||
assert level2 >= 0 && level2 <= 7;
|
||||
final int v = level1 * 8 + level2;
|
||||
sb.append(encoder.encode6bit((byte) v));
|
||||
sb.append(AsciiEncoder.encode6bit((byte) v));
|
||||
}
|
||||
result.add(sb.toString());
|
||||
}
|
||||
|
@ -136,7 +139,6 @@ public enum SpriteGrayLevel {
|
|||
// final int type = img.getType();
|
||||
|
||||
final List<String> result = new ArrayList<String>();
|
||||
final AsciiEncoder encoder = new AsciiEncoder();
|
||||
|
||||
for (int y = 0; y < height; y += 3) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
@ -148,7 +150,7 @@ public enum SpriteGrayLevel {
|
|||
final int level3 = getGrayOn16(img, x, y + 2) / 4;
|
||||
assert level3 >= 0 && level3 <= 3;
|
||||
final int v = level1 * 16 + level2 * 4 + level3;
|
||||
sb.append(encoder.encode6bit((byte) v));
|
||||
sb.append(AsciiEncoder.encode6bit((byte) v));
|
||||
}
|
||||
result.add(sb.toString());
|
||||
}
|
||||
|
@ -198,14 +200,13 @@ public enum SpriteGrayLevel {
|
|||
}
|
||||
|
||||
private Sprite buildSprite8(int width, int height, List<CharSequence> strings) {
|
||||
final AsciiEncoder encoder = new AsciiEncoder();
|
||||
final SpriteMonochrome result = new SpriteMonochrome(width, height, 8);
|
||||
for (int col = 0; col < result.getWidth(); col++) {
|
||||
for (int line = 0; line < strings.size(); line++) {
|
||||
if (col >= strings.get(line).length()) {
|
||||
continue;
|
||||
}
|
||||
final int v = encoder.decode6bit(strings.get(line).charAt(col));
|
||||
final int v = AsciiEncoder.decode6bit(strings.get(line).charAt(col));
|
||||
final int w1 = v / 8;
|
||||
final int w2 = v % 8;
|
||||
result.setPixel(col, line * 2, w1);
|
||||
|
@ -217,14 +218,13 @@ public enum SpriteGrayLevel {
|
|||
}
|
||||
|
||||
private Sprite buildSprite4(int width, int height, List<CharSequence> strings) {
|
||||
final AsciiEncoder encoder = new AsciiEncoder();
|
||||
final SpriteMonochrome result = new SpriteMonochrome(width, height, 4);
|
||||
for (int col = 0; col < result.getWidth(); col++) {
|
||||
for (int line = 0; line < strings.size(); line++) {
|
||||
if (col >= strings.get(line).length()) {
|
||||
continue;
|
||||
}
|
||||
int v = encoder.decode6bit(strings.get(line).charAt(col));
|
||||
int v = AsciiEncoder.decode6bit(strings.get(line).charAt(col));
|
||||
final int w1 = v / 16;
|
||||
v = v % 16;
|
||||
final int w2 = v / 4;
|
||||
|
@ -242,18 +242,33 @@ public enum SpriteGrayLevel {
|
|||
final int width = img.getWidth();
|
||||
final int height = img.getHeight();
|
||||
final byte raw[] = new byte[width * height];
|
||||
final BufferedImage grayImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
int cpt = 0;
|
||||
final int coef = 16 / nbColor;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
final int color = getGrayOn16(img, x, y) / coef;
|
||||
raw[cpt++] = (byte) color;
|
||||
grayImg.setRGB(x, y, color * coef);
|
||||
}
|
||||
}
|
||||
final byte[] comp = new CompressionZlib().compress(raw);
|
||||
return cut(new AsciiEncoder().encode(comp));
|
||||
// final byte[] comp = new CompressionZlib().compress(raw);
|
||||
final byte[] comp = new CompressionZopfliZlib().compress(raw);
|
||||
return cut(new AsciiEncoderFinalZeros().encode(comp));
|
||||
}
|
||||
|
||||
private List<String> encodeZSpiral(BufferedImage img) {
|
||||
final int width = img.getWidth();
|
||||
final int height = img.getHeight();
|
||||
final byte raw[] = new byte[width * height];
|
||||
final int coef = 16 / nbColor;
|
||||
final SpiralOnRectangle spiral = new SpiralOnRectangle(width, height);
|
||||
for (int cpt = 0; cpt < width * height; cpt++) {
|
||||
final PairInt pt = spiral.nextPoint();
|
||||
final int color = getGrayOn16(img, pt.getX(), pt.getY()) / coef;
|
||||
raw[cpt] = (byte) color;
|
||||
}
|
||||
// final byte[] comp = new CompressionZlib().compress(raw);
|
||||
final byte[] comp = new CompressionZopfliZlib().compress(raw);
|
||||
return cut(new AsciiEncoderFinalZeros().encode(comp));
|
||||
}
|
||||
|
||||
private List<String> cut(String s) {
|
||||
|
|
|
@ -460,6 +460,7 @@ public enum License {
|
|||
text.add("Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites");
|
||||
text.add("ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen");
|
||||
text.add("ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman");
|
||||
text.add("CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli");
|
||||
text.add("");
|
||||
return text;
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ import net.sourceforge.plantuml.core.ImageData;
|
|||
import net.sourceforge.plantuml.cucadiagram.dot.GraphvizUtils;
|
||||
import net.sourceforge.plantuml.graphic.GraphicPosition;
|
||||
import net.sourceforge.plantuml.graphic.GraphicStrings;
|
||||
import net.sourceforge.plantuml.preproc.Preprocessor;
|
||||
import net.sourceforge.plantuml.preproc.PreprocessorInclude;
|
||||
import net.sourceforge.plantuml.preproc.Stdlib;
|
||||
import net.sourceforge.plantuml.svek.TextBlockBackcolored;
|
||||
import net.sourceforge.plantuml.ugraphic.ColorMapperIdentity;
|
||||
import net.sourceforge.plantuml.ugraphic.ImageBuilder;
|
||||
|
@ -173,6 +173,9 @@ public class PSystemVersion extends AbstractPSystem {
|
|||
strings.add("plantuml.include.path: " + PreprocessorInclude.getenv("plantuml.include.path"));
|
||||
}
|
||||
strings.add(" ");
|
||||
strings.add("<b>Stdlib:");
|
||||
Stdlib.addInfoVersion(strings);
|
||||
strings.add(" ");
|
||||
|
||||
strings.addAll(GraphvizUtils.getTestDotStrings(true));
|
||||
strings.add(" ");
|
||||
|
@ -206,7 +209,8 @@ public class PSystemVersion extends AbstractPSystem {
|
|||
add(strings, "<u>Original Eclipse Plugin</u>: Claude Durif & Anne Pecoil", withTag);
|
||||
add(strings, "<u>Servlet & XWiki</u>: Maxime Sinclair", withTag);
|
||||
add(strings, "<u>Docker</u>: David Ducatel", withTag);
|
||||
add(strings, "<u>AWS lib</u>: milo-minderbinder", withTag);
|
||||
add(strings, "<u>AWS lib</u>: Chris Passarello", withTag);
|
||||
add(strings, "<u>Stdlib Icons</u>: tupadr3", withTag);
|
||||
add(strings, "<u>Site design</u>: Raphael Cotisson", withTag);
|
||||
add(strings, "<u>Logo</u>: Benjamin Croizet", withTag);
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public class Version {
|
|||
private static final int MAJOR_SEPARATOR = 1000000;
|
||||
|
||||
public static int version() {
|
||||
return 1201719;
|
||||
return 1201720;
|
||||
}
|
||||
|
||||
public static int versionPatched() {
|
||||
|
@ -88,7 +88,7 @@ public class Version {
|
|||
}
|
||||
|
||||
public static long compileTime() {
|
||||
return 1510487182424L;
|
||||
return 1513011425649L;
|
||||
}
|
||||
|
||||
public static String compileTimeString() {
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* (C) Copyright 2009-2017, Arnaud Roques
|
||||
*
|
||||
* Project Info: http://plantuml.com
|
||||
*
|
||||
* If you like this project or if you find it useful, you can support us at:
|
||||
*
|
||||
* http://plantuml.com/patreon (only 1$ per month!)
|
||||
* http://plantuml.com/paypal
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*
|
||||
* Original Author: Arnaud Roques
|
||||
*
|
||||
*
|
||||
*/
|
||||
package net.sourceforge.plantuml.webp;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.plantuml.version.PSystemVersion;
|
||||
|
||||
public class Portraits {
|
||||
|
||||
private final static List<Portrait> all = new ArrayList<Portrait>();
|
||||
private final static AtomicInteger current = new AtomicInteger();
|
||||
|
||||
private static InputStream getInputStream() {
|
||||
return PSystemVersion.class.getResourceAsStream("out.png");
|
||||
}
|
||||
|
||||
static {
|
||||
final InputStream is = getInputStream();
|
||||
if (is != null) {
|
||||
try {
|
||||
read(is);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void read(InputStream is) throws IOException {
|
||||
final DataInputStream dis = new DataInputStream(is);
|
||||
final int nb = dis.readShort();
|
||||
final List<String> names = new ArrayList<String>();
|
||||
final List<Integer> ages = new ArrayList<Integer>();
|
||||
final List<String> quotes = new ArrayList<String>();
|
||||
for (int i = 0; i < nb; i++) {
|
||||
names.add(dis.readUTF());
|
||||
ages.add((int) dis.readByte());
|
||||
quotes.add(dis.readUTF());
|
||||
}
|
||||
for (int i = 0; i < nb; i++) {
|
||||
final int len = dis.readShort();
|
||||
final byte data[] = new byte[len];
|
||||
dis.readFully(data);
|
||||
all.add(new Portrait(names.get(i), ages.get(i), quotes.get(i), data));
|
||||
}
|
||||
Collections.shuffle(all);
|
||||
}
|
||||
|
||||
public static Portrait getOne() {
|
||||
if (all.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
final int nb = current.get() % all.size();
|
||||
return all.get(nb);
|
||||
}
|
||||
|
||||
public static void nextOne() {
|
||||
current.getAndIncrement();
|
||||
}
|
||||
|
||||
public static Portrait getOne(String line) {
|
||||
Portrait candidat = null;
|
||||
for (Portrait p : all) {
|
||||
final int dist = similar(p.getName(), line);
|
||||
if (dist <= 3) {
|
||||
if (candidat != null && dist < similar(candidat.getName(), line)) {
|
||||
continue;
|
||||
}
|
||||
candidat = p;
|
||||
}
|
||||
}
|
||||
return candidat;
|
||||
}
|
||||
|
||||
public static int similar(String s1, String s2) {
|
||||
final int[] tab1 = countLetter(s1);
|
||||
final int[] tab2 = countLetter(s2);
|
||||
int result = 0;
|
||||
for (int i = 0; i < tab1.length; i++) {
|
||||
result += Math.abs(tab1[i] - tab2[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String noAccent(String str) {
|
||||
final String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD);
|
||||
final Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
|
||||
return pattern.matcher(nfdNormalizedString).replaceAll("");
|
||||
}
|
||||
|
||||
private static int[] countLetter(String s) {
|
||||
s = noAccent(s).toLowerCase(Locale.US);
|
||||
final int[] result = new int[26];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
final char c = s.charAt(i);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
result[c - 'a']++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static final List<Portrait> getAll() {
|
||||
return all;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class BlockSplitter {
|
||||
|
||||
static int split(Cookie cookie, byte[] input, int from, int to) {
|
||||
LzStore store = cookie.store1;
|
||||
store.reset();
|
||||
Deflate.greedy(cookie, null, input, from, to, store);
|
||||
|
||||
int nPoints = splitLz(cookie, store.litLens, store.dists, store.size);
|
||||
|
||||
int pos = from;
|
||||
char[] dists = store.dists;
|
||||
char[] litLens = store.litLens;
|
||||
int[] points = cookie.splitPoints;
|
||||
|
||||
points[0] = pos;
|
||||
for (int i = 0, j = 1; j <= nPoints; ++j) {
|
||||
for (int pj = points[j]; i < pj; ++i) {
|
||||
pos += (dists[i] == 0) ? 1 : litLens[i];
|
||||
}
|
||||
points[j] = pos;
|
||||
}
|
||||
return nPoints;
|
||||
}
|
||||
|
||||
// TODO: May be use some kind of SORTED data-structure for splitPoints?
|
||||
static int splitLz(Cookie cookie, char[] litLens, char[] dists, int llSize) {
|
||||
int[] splitPoints = cookie.splitPoints;
|
||||
int[] splitSize = cookie.splitSize;
|
||||
splitPoints[0] = 0;
|
||||
splitSize[0] = Deflate.calculateBlockSize(cookie, litLens, dists, 0, llSize);
|
||||
splitPoints[1] = llSize;
|
||||
splitSize[1] = -1;
|
||||
int numBlocks = 1;
|
||||
int maxBlocks = cookie.blockSplittingMax;
|
||||
|
||||
if (llSize < 10) {
|
||||
return numBlocks;
|
||||
}
|
||||
|
||||
int lStart = 0;
|
||||
int lEnd = llSize;
|
||||
int blockN = 0;
|
||||
while (numBlocks < maxBlocks) {
|
||||
int llPos = findMinimum(cookie, litLens, dists, lStart, lEnd);
|
||||
|
||||
int splitL = Deflate.calculateBlockSize(cookie, litLens, dists, lStart, llPos);
|
||||
int splitR = Deflate.calculateBlockSize(cookie, litLens, dists, llPos, lEnd);
|
||||
|
||||
if (splitL + splitR > splitSize[blockN] || llPos == lStart + 1 || llPos == lEnd) {
|
||||
splitSize[blockN] = -1;
|
||||
} else {
|
||||
splitSize[blockN] = splitL;
|
||||
numBlocks++;
|
||||
blockN++;
|
||||
System.arraycopy(splitPoints, blockN, splitPoints, blockN + 1, numBlocks - blockN);
|
||||
System.arraycopy(splitSize, blockN, splitSize, blockN + 1, numBlocks - blockN);
|
||||
splitPoints[blockN] = llPos;
|
||||
splitSize[blockN] = splitR;
|
||||
}
|
||||
|
||||
int longest = 0;
|
||||
boolean found = false;
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
int start = splitPoints[i];
|
||||
int end = splitPoints[i + 1];
|
||||
if ((splitSize[i] != -1) && end - start > longest) {
|
||||
lStart = start;
|
||||
lEnd = end;
|
||||
found = true;
|
||||
longest = end - start;
|
||||
blockN = i;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (lEnd - lStart < 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numBlocks;
|
||||
}
|
||||
|
||||
private static int findMinimum(Cookie cookie, char[] litLens, char[] dists, int from, int to) {
|
||||
int start = from + 1;
|
||||
int end = to;
|
||||
if (end - start < 1024) {
|
||||
int best = Integer.MAX_VALUE;
|
||||
int result = start;
|
||||
for (int i = start; i < end; i++) {
|
||||
int v = Deflate.calculateBlockSize(cookie, litLens, dists, from, i)
|
||||
+ Deflate.calculateBlockSize(cookie, litLens, dists, i, to);
|
||||
if (v < best) {
|
||||
best = v;
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
int n = Cookie.SPLIT_PARTITIONS;
|
||||
int[] p = cookie.p;
|
||||
int[] vp = cookie.vp;
|
||||
int lastBest = Integer.MAX_VALUE;
|
||||
int pos = start;
|
||||
|
||||
while (true) {
|
||||
if (end - start <= n) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
p[i] = start + (i + 1) * ((end - start) / (n + 1));
|
||||
vp[i] = Deflate.calculateBlockSize(cookie, litLens, dists, from, p[i])
|
||||
+ Deflate.calculateBlockSize(cookie, litLens, dists, p[i], to);
|
||||
}
|
||||
int bestI = 0;
|
||||
int best = vp[0];
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (vp[i] < best) {
|
||||
best = vp[i];
|
||||
bestI = i;
|
||||
}
|
||||
}
|
||||
if (best > lastBest) {
|
||||
break;
|
||||
}
|
||||
|
||||
start = bestI == 0 ? start : p[bestI - 1];
|
||||
end = bestI == n - 1 ? end : p[bestI + 1];
|
||||
|
||||
pos = p[bestI];
|
||||
lastBest = best;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
public class Buffer {
|
||||
|
||||
byte[] data;
|
||||
int size;
|
||||
private int bp;
|
||||
|
||||
Buffer() {
|
||||
data = new byte[65536];
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] getResult() {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(data,0,copy,0,size);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
void append(byte value) {
|
||||
if (size == data.length) {
|
||||
byte[] copy = new byte[size * 2];
|
||||
System.arraycopy(data, 0, copy, 0, size);
|
||||
data = copy;
|
||||
}
|
||||
data[size++] = value;
|
||||
}
|
||||
|
||||
void addBits(int symbol, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (bp == 0) {
|
||||
append((byte) 0);
|
||||
}
|
||||
int bit = (symbol >> i) & 1;
|
||||
data[size - 1] |= bit << bp;
|
||||
bp = (bp + 1) & 7;
|
||||
}
|
||||
}
|
||||
|
||||
void addHuffmanBits(int symbol, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (bp == 0) {
|
||||
append((byte) 0);
|
||||
}
|
||||
int bit = (symbol >> (length - i - 1)) & 1;
|
||||
data[size - 1] |= bit << bp;
|
||||
bp = (bp + 1) & 7;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class Cookie {
|
||||
|
||||
static class Node {
|
||||
int weight;
|
||||
Node tail;
|
||||
int count;
|
||||
}
|
||||
|
||||
static final int SPLIT_PARTITIONS = 9;
|
||||
private static final int POOL_MAX = 10240;
|
||||
|
||||
final static int[] intZeroes = new int[65536];
|
||||
final static char[] charZeroes = new char[65536];
|
||||
final static byte[] byteZeroes = new byte[65536];
|
||||
final static int[] intMOnes = new int[65536];
|
||||
final static char[] charOnes = new char[65536];
|
||||
final static long[] costMax = new long[65536];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
intMOnes[i] = -1;
|
||||
charOnes[i] = 1;
|
||||
costMax[i] = Long.MAX_VALUE;
|
||||
}
|
||||
expand(intMOnes);
|
||||
expand(charOnes);
|
||||
expand(costMax);
|
||||
}
|
||||
|
||||
static void expand(Object array) {
|
||||
for (int i = 64; i < 65536; i = i + i) {
|
||||
System.arraycopy(array, 0, array, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
final Node[] list0 = new Node[15];
|
||||
final Node[] list1 = new Node[15];
|
||||
final Node[] leaves1 = new Node[288];
|
||||
final Node[] leaves2 = new Node[288];
|
||||
private final Node[] nodes = new Node[POOL_MAX];
|
||||
private int nextNode;
|
||||
|
||||
//final Node[] leaves2 = new Node[288];
|
||||
|
||||
/*private final static Comparator<Node> wc = new Comparator<Node>() {
|
||||
@Override
|
||||
public int compare(Node node, Node node2) {
|
||||
int r = node.weight - node2.weight;
|
||||
return r == 0 ? node.count - node2.count : r;
|
||||
}
|
||||
};*/
|
||||
|
||||
final int[] i320a = new int[320];
|
||||
final int[] i320b = new int[320];
|
||||
final int[] i320c = new int[320];
|
||||
final int[] i288a = new int[288];
|
||||
final int[] i288b = new int[288];
|
||||
final int[] i288c = new int[288];
|
||||
final int[] i289a = new int[289];
|
||||
final char[] c259a = new char[259];
|
||||
final int[] i32a = new int[32];
|
||||
final int[] i32b = new int[32];
|
||||
final int[] i32c = new int[32];
|
||||
final int[] i19a = new int[19];
|
||||
final int[] i19b = new int[19];
|
||||
final int[] i19c = new int[19];
|
||||
final int[] i16a = new int[16];
|
||||
final int[] i16b = new int[16];
|
||||
|
||||
|
||||
final int[] p = new int[SPLIT_PARTITIONS];
|
||||
final int[] vp = new int[SPLIT_PARTITIONS];
|
||||
|
||||
final char[] lengthArray; // unsigned short, but really values are 0..258 == MAX_MATCH
|
||||
final long[] costs;
|
||||
final char[] path;
|
||||
final int[] splitPoints;
|
||||
final int[] splitSize;
|
||||
|
||||
|
||||
final SymbolStats stats = new SymbolStats();
|
||||
final SymbolStats bestStats = new SymbolStats();
|
||||
final SymbolStats lastStats = new SymbolStats();
|
||||
final Hash h = new Hash();
|
||||
|
||||
|
||||
int lenVal;
|
||||
int distVal;
|
||||
int rnd = 42;
|
||||
|
||||
|
||||
final LzStore store1;
|
||||
final LzStore store2;
|
||||
final LongestMatchCache lmc;
|
||||
|
||||
|
||||
final int masterBlockSize;
|
||||
|
||||
final Node node(int weight, int count, Node tail) {
|
||||
Node result = nodes[nextNode++];
|
||||
result.weight = weight;
|
||||
result.count = count;
|
||||
result.tail = tail;
|
||||
return result;
|
||||
}
|
||||
|
||||
final void resetPool() {
|
||||
nextNode = 0;
|
||||
}
|
||||
|
||||
static void fill0(int[] array, int length) {
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
int j = i + 65536;
|
||||
if (j > length) {
|
||||
j = length;
|
||||
}
|
||||
System.arraycopy(intZeroes, 0, array, i, j - i);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill0(char[] array, int length) {
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
int j = i + 65536;
|
||||
if (j > length) {
|
||||
j = length;
|
||||
}
|
||||
System.arraycopy(charZeroes, 0, array, i, j - i);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
static void fillCostMax(long[] array, int length) {
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
int j = i + 65536;
|
||||
if (j > length) {
|
||||
j = length;
|
||||
}
|
||||
System.arraycopy(costMax, 0, array, i, j - i);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum amount of blocks to split into.
|
||||
* <p/>
|
||||
* {@code 0} for unlimited.
|
||||
*/
|
||||
final int blockSplittingMax = 15;
|
||||
|
||||
Cookie(int masterBlockSize) { // TODO: + maxBlockSize?
|
||||
this.masterBlockSize = masterBlockSize;
|
||||
|
||||
for (int i = 0; i < POOL_MAX; i++) {
|
||||
nodes[i] = new Node();
|
||||
}
|
||||
splitPoints = new int[blockSplittingMax + 1];
|
||||
splitSize = new int[blockSplittingMax + 1];
|
||||
|
||||
lengthArray = new char[masterBlockSize + 1]; // 2
|
||||
costs = new long[masterBlockSize + 1]; // 8
|
||||
path = new char[masterBlockSize + 1]; // 2
|
||||
lmc = new LongestMatchCache(masterBlockSize); // 28
|
||||
store1 = new LzStore(masterBlockSize); // 4
|
||||
store2 = new LzStore(masterBlockSize); // 4
|
||||
// 2 + 8 + 2 + 28 + 4 + 4 = 48
|
||||
}
|
||||
}
|
|
@ -0,0 +1,796 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class Deflate {
|
||||
|
||||
static enum BlockType {
|
||||
DYNAMIC,
|
||||
FIXED
|
||||
}
|
||||
|
||||
// final static int WINDOW_SIZE = 0x8000;
|
||||
// final static int WINDOW_MASK = 0x7FFF;
|
||||
// final static int MAX_MATCH = 258;
|
||||
// final static int MIN_MATCH = 3;
|
||||
// final static int MAX_CHAIN_HITS = 8192; // Should be less than WINDOW_SIZE
|
||||
|
||||
private static void getFixedTree(int[] llLengths, int[] dLengths) {
|
||||
for (int i = 0; i < 144; i++) {
|
||||
llLengths[i] = 8;
|
||||
}
|
||||
for (int i = 144; i < 256; i++) {
|
||||
llLengths[i] = 9;
|
||||
}
|
||||
for (int i = 256; i < 280; i++) {
|
||||
llLengths[i] = 7;
|
||||
}
|
||||
for (int i = 280; i < 288; i++) {
|
||||
llLengths[i] = 8;
|
||||
}
|
||||
for (int i = 0; i < 32; i++) {
|
||||
dLengths[i] = 5;
|
||||
}
|
||||
}
|
||||
|
||||
public static void greedy(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to, LzStore store) {
|
||||
Hash h = cookie.h;
|
||||
h.init(input, Math.max(from - 0x8000, 0), from, to);
|
||||
int prevLength = 0;
|
||||
int prevMatch = 0;
|
||||
char[] dummySubLen = cookie.c259a;
|
||||
boolean matchAvailable = false;
|
||||
|
||||
for (int i = from; i < to; i++) {
|
||||
h.updateHash(input, i, to);
|
||||
findLongestMatch(cookie, lmc, from, h, input, i, to, 258, dummySubLen);
|
||||
int len = cookie.lenVal;
|
||||
int dist = cookie.distVal;
|
||||
int lengthScore = dist > 1024 ? len - 1 : len;
|
||||
int prevLengthScore = prevMatch > 1024 ? prevLength - 1 : prevLength;
|
||||
|
||||
if (matchAvailable) {
|
||||
matchAvailable = false;
|
||||
if (lengthScore > prevLengthScore + 1) {
|
||||
store.append((char) (input[i - 1] & 0xFF), (char) 0);
|
||||
if (lengthScore >= 3 && len < 258) {
|
||||
matchAvailable = true;
|
||||
prevLength = len;
|
||||
prevMatch = dist;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
store.append((char) prevLength, (char) prevMatch);
|
||||
for (int j = 2; j < prevLength; j++) {
|
||||
i++;
|
||||
h.updateHash(input, i, to);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (lengthScore >= 3 && len < 258) {
|
||||
matchAvailable = true;
|
||||
prevLength = len;
|
||||
prevMatch = dist;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lengthScore >= 3) {
|
||||
store.append((char) len, (char) dist);
|
||||
} else {
|
||||
len = 1;
|
||||
store.append((char) (input[i] & 0xFF), (char) 0);
|
||||
}
|
||||
for (int j = 1; j < len; j++) {
|
||||
i++;
|
||||
h.updateHash(input, i, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void findLongestMatch(Cookie cookie, LongestMatchCache lmc, int blockStart, Hash h, byte[] array,
|
||||
int pos, int size, int limit, char[] subLen) {
|
||||
//# WINDOW_SIZE = 0x8000
|
||||
//# WINDOW_MASK = 0x7FFF
|
||||
//# MIN_MATCH = 3
|
||||
//# MAX_MATCH = 258
|
||||
|
||||
int offset = pos - blockStart;
|
||||
char[] lmcLength = lmc != null ? lmc.length : null;
|
||||
if (lmc != null && ((lmcLength[offset] == 0 || lmc.dist[offset] != 0))
|
||||
&& (limit == 258 || lmcLength[offset] <= limit
|
||||
|| subLen != null && lmc.maxCachedSubLen(offset) >= limit)) {
|
||||
if (subLen == null || lmcLength[offset] <= lmc.maxCachedSubLen(offset)) {
|
||||
cookie.lenVal = lmcLength[offset];
|
||||
if (cookie.lenVal > limit) {
|
||||
cookie.lenVal = limit;
|
||||
}
|
||||
if (subLen != null) {
|
||||
lmc.cacheToSubLen(offset, cookie.lenVal, subLen);
|
||||
cookie.distVal = subLen[cookie.lenVal];
|
||||
} else {
|
||||
cookie.distVal = lmc.dist[offset];
|
||||
}
|
||||
return;
|
||||
}
|
||||
limit = lmcLength[offset];
|
||||
}
|
||||
|
||||
if (size - pos < 3) {
|
||||
cookie.lenVal = 0;
|
||||
cookie.distVal = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos + limit > size) {
|
||||
limit = size - pos;
|
||||
}
|
||||
|
||||
int bestDist = 0;
|
||||
int bestLength = 1;
|
||||
int arrayEnd = pos + limit;
|
||||
int chainCounter = 8192;
|
||||
int[] hPrev = h.prev;
|
||||
int[] hPrev2 = h.prev2;
|
||||
int pp = h.head[h.val];
|
||||
int threshold = h.same[pp];
|
||||
int[] hashVal2 = h.hashVal2;
|
||||
int marker = hashVal2[pp];
|
||||
int p = hPrev[pp];
|
||||
pp -= p;
|
||||
int dist = pp > 0 ? pp : pp + 0x8000;
|
||||
|
||||
while (dist < 0x8000 && chainCounter > 0) {
|
||||
int scan = pos;
|
||||
int match = pos - dist;
|
||||
|
||||
if (array[scan + bestLength] == array[match + bestLength]) {
|
||||
int same0 = h.same[pos & 0x7FFF];
|
||||
if (same0 > 2 && array[scan] == array[match]) {
|
||||
int same1 = h.same[match & 0x7FFF];
|
||||
int same = same0 < same1 ? same0 : same1;
|
||||
if (same > limit) {
|
||||
same = limit;
|
||||
}
|
||||
scan += same;
|
||||
match += same;
|
||||
}
|
||||
while (scan != arrayEnd && array[scan] == array[match]) {
|
||||
scan++;
|
||||
match++;
|
||||
}
|
||||
scan -= pos;
|
||||
|
||||
if (scan > bestLength) {
|
||||
if (subLen != null) {
|
||||
for (int j = bestLength + 1; j <= scan; j++) {
|
||||
subLen[j] = (char) dist;
|
||||
}
|
||||
}
|
||||
bestDist = dist;
|
||||
bestLength = scan;
|
||||
if (scan >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hPrev != hPrev2 && bestLength >= threshold && marker == hashVal2[p]) {
|
||||
hPrev = hPrev2;
|
||||
}
|
||||
|
||||
pp = p;
|
||||
p = hPrev[p];
|
||||
if (p == pp) {
|
||||
break;
|
||||
}
|
||||
pp -= p;
|
||||
dist += pp > 0 ? pp : 0x8000 + pp;
|
||||
|
||||
--chainCounter;
|
||||
}
|
||||
|
||||
if (lmc != null && limit == 258 && subLen != null && lmcLength[offset] != 0 && lmc.dist[offset] == 0) {
|
||||
if (bestLength < 3) {
|
||||
lmc.dist[offset] = 0;
|
||||
lmcLength[offset] = 0;
|
||||
} else {
|
||||
lmc.dist[offset] = (char)bestDist;
|
||||
lmcLength[offset] = (char)bestLength;
|
||||
}
|
||||
lmc.subLenToCache(subLen, offset, bestLength);
|
||||
}
|
||||
|
||||
cookie.distVal = bestDist;
|
||||
cookie.lenVal = bestLength;
|
||||
}
|
||||
|
||||
private static void deflatePart(Cookie cookie, Options options, byte[] input, int from, int to, boolean flush,
|
||||
Buffer output) {
|
||||
// assert from != to
|
||||
switch (options.blockSplitting) {
|
||||
case FIRST:
|
||||
deflateSplittingFirst(cookie, options, flush, input, from, to, output);
|
||||
break;
|
||||
|
||||
case LAST:
|
||||
deflateSplittingLast(cookie, options, flush, input, from, to, output);
|
||||
break;
|
||||
|
||||
case NONE:
|
||||
deflateDynamicBlock(cookie, options, flush, input, from, to, output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void deflateDynamicBlock(Cookie cookie, Options options, boolean flush, byte[] input,
|
||||
int from, int to, Buffer output) {
|
||||
// assert from != to
|
||||
LongestMatchCache lmc = cookie.lmc;
|
||||
lmc.init(to - from);
|
||||
|
||||
BlockType type = BlockType.DYNAMIC;
|
||||
LzStore store = Squeeze.optimal(cookie, options.numIterations, lmc, input, from, to);
|
||||
|
||||
if (store.size < 1000) {
|
||||
LzStore fixedStore = cookie.store1;
|
||||
fixedStore.reset();
|
||||
Squeeze.bestFixedLengths(cookie, lmc, input, from, to, cookie.lengthArray, cookie.costs);
|
||||
Squeeze.optimalRun(cookie, lmc, input, from, to, cookie.lengthArray, fixedStore);
|
||||
int dynCost = calculateBlockSize(cookie, store.litLens, store.dists, 0, store.size);
|
||||
int fixedCost = calculateFixedBlockSize(cookie, fixedStore.litLens,
|
||||
fixedStore.dists, fixedStore.size);
|
||||
if (fixedCost < dynCost) {
|
||||
type = BlockType.FIXED;
|
||||
store = fixedStore;
|
||||
}
|
||||
}
|
||||
|
||||
addLzBlock(cookie, type, flush, store.litLens, store.dists, 0, store.size, output);
|
||||
}
|
||||
|
||||
private static void deflateSplittingLast(Cookie cookie, Options options, boolean flush,
|
||||
byte[] input, int from, int to, Buffer output) {
|
||||
// assert from != to
|
||||
LongestMatchCache lmc = cookie.lmc;
|
||||
lmc.init(to - from);
|
||||
|
||||
LzStore store = Squeeze.optimal(cookie, options.numIterations, lmc, input, from, to);
|
||||
|
||||
int nPoints = BlockSplitter.splitLz(cookie, store.litLens, store.dists, store.size);
|
||||
|
||||
int[] splitPoints = cookie.splitPoints;
|
||||
for (int i = 1; i <= nPoints; i++) {
|
||||
int start = splitPoints[i - 1];
|
||||
int end = splitPoints[i];
|
||||
addLzBlock(cookie, BlockType.DYNAMIC, i == nPoints && flush, store.litLens, store.dists, start, end, output);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deflateSplittingFirst(Cookie cookie, Options options, boolean flush,
|
||||
byte[] input, int from, int to, Buffer output) {
|
||||
// assert from != to
|
||||
int nPoints = BlockSplitter.split(cookie, input, from, to);
|
||||
int[] splitPoints = cookie.splitPoints;
|
||||
for (int i = 1; i <= nPoints; ++i) {
|
||||
deflateDynamicBlock(cookie, options, i == nPoints && flush, input, splitPoints[i - 1], splitPoints[i], output);
|
||||
}
|
||||
}
|
||||
|
||||
static int calculateBlockSize(Cookie cookie, char[] litLens, char[] dists, int lStart, int lEnd) {
|
||||
int[] llLengths = cookie.i288a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, llLengths, 0, 288);
|
||||
int[] dLengths = cookie.i32a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, dLengths, 0, 32);
|
||||
|
||||
int result = 3;
|
||||
|
||||
int[] llCounts = cookie.i288b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, llCounts, 0, 288);
|
||||
int[] dCounts = cookie.i32b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, dCounts, 0, 32);
|
||||
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;
|
||||
int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS;
|
||||
for (int i = lStart; i < lEnd; i++) {
|
||||
int d = dists[i];
|
||||
int l = litLens[i];
|
||||
if (d == 0) {
|
||||
llCounts[l]++;
|
||||
} else {
|
||||
llCounts[lengthSymbol[l]]++;
|
||||
int distSymbol = cachedDistSymbol[d];
|
||||
dCounts[distSymbol]++;
|
||||
result += lengthExtraBits[l];
|
||||
if (distSymbol > 3) {
|
||||
result += (distSymbol / 2) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
llCounts[256] = 1;
|
||||
|
||||
|
||||
int[] llCountsCopy = cookie.i288c;
|
||||
System.arraycopy(llCounts, 0, llCountsCopy, 0, 288);
|
||||
optimizeHuffmanForRle(cookie, llCountsCopy);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, llCountsCopy, 15, llLengths);
|
||||
|
||||
int[] dCountsCopy = cookie.i32c;
|
||||
System.arraycopy(dCounts, 0, dCountsCopy, 0, 32);
|
||||
optimizeHuffmanForRle(cookie, dCountsCopy);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, dCountsCopy, 15, dLengths);
|
||||
patchDistanceCodesForBuggyDecoders(dLengths);
|
||||
|
||||
result += simulateAddDynamicTree(cookie, llLengths, dLengths);
|
||||
|
||||
for (int i = 0; i < 288; ++i) {
|
||||
result += llCounts[i] * llLengths[i];
|
||||
}
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
result += dCounts[i] * dLengths[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int calculateFixedBlockSize(Cookie cookie, char[] litLens, char[] dists, int size) {
|
||||
int[] llLengths = cookie.i288a;
|
||||
int[] dLengths = cookie.i32a;
|
||||
getFixedTree(llLengths, dLengths);
|
||||
|
||||
int result = 3;
|
||||
|
||||
int[] cachedDistExtraBits = Util.CACHED_DIST_EXTRA_BITS;
|
||||
int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS;
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int d = dists[i];
|
||||
int l = litLens[i];
|
||||
if (d == 0) {
|
||||
result += llLengths[l];
|
||||
} else {
|
||||
result += llLengths[lengthSymbol[l]];
|
||||
result += lengthExtraBits[l];
|
||||
result += 5;
|
||||
result += d < 4097 ? cachedDistExtraBits[d] : d < 16385 ? d < 8193 ? 11 : 12 : 13;
|
||||
}
|
||||
}
|
||||
result += llLengths[256];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void lzCounts(char[] litLens, char[] dists, int start, int end, int[] llCount, int[] dCount) {
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;
|
||||
for (int i = start; i < end; i++) {
|
||||
int d = dists[i];
|
||||
int l = litLens[i];
|
||||
if (d == 0) {
|
||||
llCount[l]++;
|
||||
} else {
|
||||
llCount[lengthSymbol[l]]++;
|
||||
dCount[cachedDistSymbol[d]]++;
|
||||
}
|
||||
}
|
||||
|
||||
llCount[256] = 1;
|
||||
}
|
||||
|
||||
static void compress(Cookie cookie, Options options, byte[] input, Buffer output) {
|
||||
int i = 0;
|
||||
while (i < input.length) {
|
||||
int j = Math.min(i + cookie.masterBlockSize, input.length);
|
||||
deflatePart(cookie, options, input, i, j, j == input.length, output);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
private static void patchDistanceCodesForBuggyDecoders(int[] dLengths) {
|
||||
int numDistCodes = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
if (dLengths[i] != 0) {
|
||||
numDistCodes++;
|
||||
if (numDistCodes == 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numDistCodes == 0) {
|
||||
dLengths[0] = 1;
|
||||
dLengths[1] = 1;
|
||||
} else if (numDistCodes == 1) {
|
||||
dLengths[dLengths[0] != 0 ? 1 : 0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addDynamicTree(Cookie cookie, int[] llLengths, int[] dLengths, Buffer output) {
|
||||
int best = 0;
|
||||
int bestSize = Integer.MAX_VALUE;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
int size = simulateEncodeTree(cookie, llLengths, dLengths, (i & 1) != 0, (i & 2) != 0, (i & 4) != 0);
|
||||
if (size < bestSize) {
|
||||
bestSize = size;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
encodeTree(cookie, llLengths, dLengths, (best & 1) != 0, (best & 2) != 0, (best & 4) != 0, output);
|
||||
}
|
||||
|
||||
private static void encodeTree(Cookie cookie, int[] llLengths, int[] dLengths,
|
||||
boolean use16, boolean use17, boolean use18, Buffer output) {
|
||||
int hLit = 29;
|
||||
int hDist = 29;
|
||||
|
||||
while (hLit > 0 && llLengths[256 + hLit] == 0) {
|
||||
hLit--;
|
||||
}
|
||||
while (hDist > 0 && dLengths[hDist] == 0) {
|
||||
hDist--;
|
||||
}
|
||||
|
||||
int lldTotal = hLit + 258 + hDist;
|
||||
int[] lldLengths = cookie.i320b;
|
||||
System.arraycopy(llLengths, 0, lldLengths, 0, 257 + hLit);
|
||||
System.arraycopy(dLengths, 0, lldLengths, 257 + hLit, hDist + 1);
|
||||
|
||||
int rleSize = 0;
|
||||
int[] rle = cookie.i320a;
|
||||
int[] rleBits = cookie.i320c;
|
||||
|
||||
for (int i = 0; i < lldTotal; i++) {
|
||||
int count = 1;
|
||||
int symbol = lldLengths[i];
|
||||
if (use16 || (symbol == 0 && (use17 || use18))) {
|
||||
for (int j = i + 1; j < lldTotal && symbol == lldLengths[j]; j++) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
i += count - 1;
|
||||
|
||||
if (symbol == 0 && count > 2) {
|
||||
if (use18) {
|
||||
while (count > 10) {
|
||||
int delta = count > 138 ? 138 : count;
|
||||
rle[rleSize] = 18;
|
||||
rleBits[rleSize++] = delta - 11;
|
||||
count -= delta;
|
||||
}
|
||||
}
|
||||
if (use17) {
|
||||
while (count > 2) {
|
||||
int delta = count > 10 ? 10 : count;
|
||||
rle[rleSize] = 17;
|
||||
rleBits[rleSize++] = delta - 3;
|
||||
count -= delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use16 && count > 3) {
|
||||
count--;
|
||||
rle[rleSize] = symbol;
|
||||
rleBits[rleSize++] = 0;
|
||||
while (count > 2) {
|
||||
int delta = count > 6 ? 6 : count;
|
||||
rle[rleSize] = 16;
|
||||
rleBits[rleSize++] = delta - 3;
|
||||
count -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
while (count != 0) {
|
||||
rle[rleSize] = symbol;
|
||||
rleBits[rleSize++] = 0;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
int[] clCounts = cookie.i19a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, clCounts, 0, 19);
|
||||
for (int i = 0; i < rleSize; ++i) {
|
||||
clCounts[rle[i]]++;
|
||||
}
|
||||
|
||||
int[] clCl = cookie.i19b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, clCl, 0, 19);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, clCounts, 7, clCl);
|
||||
int[] clSymbols = cookie.i19c;
|
||||
lengthsToSymbols(clCl, 19, 7, clSymbols, cookie.i16a, cookie.i16b);
|
||||
|
||||
int[] order = Util.ORDER;
|
||||
int hcLen = 15;
|
||||
while (hcLen > 0 && clCounts[order[hcLen + 3]] == 0) {
|
||||
hcLen--;
|
||||
}
|
||||
|
||||
output.addBits(hLit, 5);
|
||||
output.addBits(hDist, 5);
|
||||
output.addBits(hcLen, 4);
|
||||
|
||||
for (int i = 0; i < hcLen + 4; i++) {
|
||||
output.addBits(clCl[order[i]], 3);
|
||||
}
|
||||
|
||||
for (int i = 0; i < rleSize; i++) {
|
||||
int symbol = clSymbols[rle[i]];
|
||||
output.addHuffmanBits(symbol, clCl[rle[i]]);
|
||||
if (rle[i] == 16) {
|
||||
output.addBits(rleBits[i], 2);
|
||||
} else if (rle[i] == 17) {
|
||||
output.addBits(rleBits[i], 3);
|
||||
} else if (rle[i] == 18) {
|
||||
output.addBits(rleBits[i], 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int simulateAddDynamicTree(Cookie cookie, int[] llLengths, int[] dLengths) {
|
||||
int bestSize = Integer.MAX_VALUE;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
int size = simulateEncodeTree(cookie, llLengths, dLengths, (i & 1) != 0, (i & 2) != 0, (i & 4) != 0);
|
||||
if (size < bestSize) {
|
||||
bestSize = size;
|
||||
}
|
||||
}
|
||||
return bestSize;
|
||||
}
|
||||
|
||||
// TODO: GetRid of RLE
|
||||
private static int simulateEncodeTree(Cookie cookie, int[] llLengths, int[] dLengths,
|
||||
boolean use16, boolean use17, boolean use18) {
|
||||
int hLit = 29;
|
||||
int hDist = 29;
|
||||
|
||||
while (hLit > 0 && llLengths[256 + hLit] == 0) {
|
||||
hLit--;
|
||||
}
|
||||
while (hDist > 0 && dLengths[hDist] == 0) {
|
||||
hDist--;
|
||||
}
|
||||
|
||||
int lldTotal = hLit + 258 + hDist;
|
||||
int[] lldLengths = cookie.i320b;
|
||||
System.arraycopy(llLengths, 0, lldLengths, 0, 257 + hLit);
|
||||
System.arraycopy(dLengths, 0, lldLengths, 257 + hLit, hDist + 1);
|
||||
|
||||
int[] rle = cookie.i320a;
|
||||
int rleSize = 0;
|
||||
|
||||
for (int i = 0; i < lldTotal; i++) {
|
||||
int count = 1;
|
||||
int symbol = lldLengths[i];
|
||||
if (use16 || (symbol == 0 && (use17 || use18))) {
|
||||
for (int j = i + 1; j < lldTotal && symbol == lldLengths[j]; j++) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
i += count - 1;
|
||||
|
||||
if (symbol == 0 && count > 2) {
|
||||
if (use18) {
|
||||
while (count > 10) {
|
||||
rle[rleSize++] = 18;
|
||||
count -= count > 138 ? 138 : count;
|
||||
}
|
||||
}
|
||||
if (use17) {
|
||||
while (count > 2) {
|
||||
rle[rleSize++] = 17;
|
||||
count -= count > 10 ? 10 : count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use16 && count > 3) {
|
||||
count--;
|
||||
rle[rleSize++] = symbol;
|
||||
while (count > 2) {
|
||||
rle[rleSize++] = 16;
|
||||
count -= count > 6 ? 6 : count;
|
||||
}
|
||||
}
|
||||
|
||||
while (count != 0) {
|
||||
rle[rleSize++] = symbol;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
int[] clCounts = cookie.i19a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, clCounts, 0, 19);
|
||||
for (int i = 0; i < rleSize; ++i) {
|
||||
clCounts[rle[i]]++;
|
||||
}
|
||||
|
||||
int[] clCl = cookie.i19b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, clCl, 0, 19);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, clCounts, 7, clCl);
|
||||
clCl[16] += 2;
|
||||
clCl[17] += 3;
|
||||
clCl[18] += 7;
|
||||
|
||||
int[] order = Util.ORDER;
|
||||
int hcLen = 15;
|
||||
while (hcLen > 0 && clCounts[order[hcLen + 3]] == 0) {
|
||||
hcLen--;
|
||||
}
|
||||
|
||||
int result = 5 + 5 + 4 + (hcLen + 4) * 3;
|
||||
for (int i = 0; i < 19; i++) {
|
||||
result += clCl[i] * clCounts[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void addLzBlock(Cookie cookie, BlockType type, boolean last, char[] litLens, char[] dists,
|
||||
int lStart, int lEnd, Buffer output) {
|
||||
int[] llLengths = cookie.i288a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, llLengths, 0, 288);
|
||||
int[] dLengths = cookie.i32a;
|
||||
System.arraycopy(Cookie.intZeroes, 0, dLengths, 0, 32);
|
||||
int[] llCounts = cookie.i288b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, llCounts, 0, 288);
|
||||
int[] dCounts = cookie.i32b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, dCounts, 0, 32);
|
||||
|
||||
output.addHuffmanBits(last ? 1 : 0, 1);
|
||||
if (type == BlockType.FIXED) {
|
||||
output.addHuffmanBits(2, 2); // 1, 0
|
||||
} else { // DYNAMIC
|
||||
output.addHuffmanBits(1, 2); // 0, 1
|
||||
}
|
||||
|
||||
if (type == BlockType.FIXED) {
|
||||
getFixedTree(llLengths, dLengths);
|
||||
} else { // DYNAMIC
|
||||
lzCounts(litLens, dists, lStart, lEnd, llCounts, dCounts);
|
||||
optimizeHuffmanForRle(cookie, llCounts);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, llCounts, 15, llLengths);
|
||||
optimizeHuffmanForRle(cookie, dCounts);
|
||||
Katajainen.lengthLimitedCodeLengths(cookie, dCounts, 15, dLengths);
|
||||
patchDistanceCodesForBuggyDecoders(dLengths);
|
||||
addDynamicTree(cookie, llLengths, dLengths, output);
|
||||
}
|
||||
|
||||
int[] llSymbols = cookie.i288c;
|
||||
System.arraycopy(Cookie.intZeroes, 0, llSymbols, 0, 288);
|
||||
lengthsToSymbols(llLengths, 288, 15, llSymbols, cookie.i16a, cookie.i16b);
|
||||
|
||||
int[] dSymbols = cookie.i32b;
|
||||
System.arraycopy(Cookie.intZeroes, 0, dSymbols, 0, 32);
|
||||
lengthsToSymbols(dLengths, 32, 15, dSymbols, cookie.i16a, cookie.i16b);
|
||||
|
||||
addLzData(litLens, dists, lStart, lEnd, llSymbols, llLengths, dSymbols, dLengths, output);
|
||||
output.addHuffmanBits(llSymbols[256], llLengths[256]);
|
||||
}
|
||||
|
||||
private static void addLzData(char[] litLens, char[] dists, int lStart, int lEnd,
|
||||
int[] llSymbols, int[] llLengths, int[] dSymbols, int[] dLengths, Buffer output) {
|
||||
int[] cachedDistExtraBits = Util.CACHED_DIST_EXTRA_BITS;
|
||||
int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS;
|
||||
int[] lengthExtraBitsValue = Util.LENGTH_EXTRA_BITS_VALUE;
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;
|
||||
for (int i = lStart; i < lEnd; i++) {
|
||||
int dist = dists[i];
|
||||
int litLen = litLens[i];
|
||||
if (dist == 0) {
|
||||
output.addHuffmanBits(llSymbols[litLen], llLengths[litLen]);
|
||||
} else {
|
||||
int lls = lengthSymbol[litLen];
|
||||
int ds = cachedDistSymbol[dist];
|
||||
output.addHuffmanBits(llSymbols[lls], llLengths[lls]);
|
||||
output.addBits(lengthExtraBitsValue[litLen], lengthExtraBits[litLen]);
|
||||
output.addHuffmanBits(dSymbols[ds], dLengths[ds]);
|
||||
output.addBits(Util.distExtraBitsValue(dist),
|
||||
dist < 4097 ? cachedDistExtraBits[dist] : dist < 16385 ? dist < 8193 ? 11 : 12 : 13);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void lengthsToSymbols(int[] lengths, int n, int maxBits, int[] symbols, int[] blCount, int[] nextCode) {
|
||||
System.arraycopy(Cookie.intZeroes, 0, blCount, 0, maxBits + 1);
|
||||
System.arraycopy(Cookie.intZeroes, 0, nextCode, 0, maxBits + 1);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
blCount[lengths[i]]++;
|
||||
}
|
||||
int code = 0;
|
||||
blCount[0] = 0;
|
||||
for (int bits = 1; bits <= maxBits; bits++) {
|
||||
code = (code + blCount[bits - 1]) << 1;
|
||||
nextCode[bits] = code;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int len = lengths[i];
|
||||
if (len != 0) {
|
||||
symbols[i] = nextCode[len];
|
||||
nextCode[len]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void optimizeHuffmanForRle(Cookie cookie, int[] counts) {
|
||||
int[] goodForRle = cookie.i289a;
|
||||
int length = counts.length;
|
||||
for (; length >= 0; --length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
if (counts[length - 1] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.arraycopy(Cookie.intZeroes, 0, goodForRle, 0, length + 1);
|
||||
|
||||
int symbol = counts[0];
|
||||
int stride = 0;
|
||||
for (int i = 0; i < length + 1; ++i) {
|
||||
if (i == length || counts[i] != symbol) {
|
||||
if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7)) {
|
||||
for (int k = 0; k < stride; ++k) {
|
||||
goodForRle[i - k - 1] = 1;
|
||||
}
|
||||
}
|
||||
stride = 1;
|
||||
if (i != length) {
|
||||
symbol = counts[i];
|
||||
}
|
||||
} else {
|
||||
++stride;
|
||||
}
|
||||
}
|
||||
|
||||
stride = 0;
|
||||
int limit = counts[0];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length + 1; ++i) {
|
||||
if ((i == length) || (goodForRle[i] != 0) || (counts[i] - limit >= 4) || (limit - counts[i] >= 4)) {
|
||||
if ((stride >= 4) || ((stride >= 3) && (sum == 0))) {
|
||||
int count = (sum + stride / 2) / stride;
|
||||
if (count < 1) count = 1;
|
||||
if (sum == 0) {
|
||||
count = 0;
|
||||
}
|
||||
for (int k = 0; k < stride; ++k) {
|
||||
counts[i - k - 1] = count;
|
||||
}
|
||||
}
|
||||
stride = 0;
|
||||
sum = 0;
|
||||
if (i < length - 3) {
|
||||
limit = (counts[i] + counts[i + 1] + counts[i + 2] + counts[i + 3] + 2) / 4;
|
||||
} else if (i < length) {
|
||||
limit = counts[i];
|
||||
} else {
|
||||
limit = 0;
|
||||
}
|
||||
}
|
||||
++stride;
|
||||
if (i != length) {
|
||||
sum += counts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class Hash {
|
||||
|
||||
// HASH_SHIFT = 5;
|
||||
// HASH_MASK = 32767;
|
||||
|
||||
private static final int[] seq = new int[0x8000];
|
||||
|
||||
static {
|
||||
int[] seq = Hash.seq;
|
||||
for (int i = 0, l = 0x8000; i < l; ++i) {
|
||||
seq[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
final int[] head = new int[0x10000];
|
||||
final int[] prev = new int[0x8000];
|
||||
private final int[] hashVal = new int[0x8000];
|
||||
final int[] same = new int[0x8000];
|
||||
int val;
|
||||
|
||||
private final int[] head2 = new int[0x10000];
|
||||
final int[] prev2 = new int[0x8000];
|
||||
final int[] hashVal2 = new int[0x8000];
|
||||
|
||||
public Hash() {
|
||||
}
|
||||
|
||||
public void init(byte[] input, int windowStart, int from, int to) {
|
||||
int[] hashVal = this.hashVal;
|
||||
int[] head = this.head;
|
||||
int[] same = this.same;
|
||||
int[] prev = this.prev;
|
||||
int[] hashVal2 = this.hashVal2;
|
||||
int[] head2 = this.head2;
|
||||
int[] prev2 = this.prev2;
|
||||
|
||||
System.arraycopy(Cookie.intMOnes, 0, head, 0, 0x10000);
|
||||
System.arraycopy(Cookie.intMOnes, 0, hashVal, 0, 0x8000);
|
||||
System.arraycopy(Cookie.intZeroes, 0, same, 0x8000, 0);
|
||||
System.arraycopy(seq, 0, prev, 0, 0x8000);
|
||||
|
||||
System.arraycopy(Cookie.intMOnes, 0, head2, 0, 0x10000);
|
||||
System.arraycopy(Cookie.intMOnes, 0, hashVal2, 0, 0x8000);
|
||||
System.arraycopy(seq, 0, prev2, 0, 0x8000);
|
||||
|
||||
int val = (((input[windowStart] & 0xFF) << 5) ^ input[windowStart + 1] & 0xFF) & 0x7FFF;
|
||||
|
||||
for (int i = windowStart; i < from; ++i) {
|
||||
int hPos = i & 0x7FFF;
|
||||
val = ((val << 5) ^ (i + 2 < to ? input[i + 2] & 0xFF : 0)) & 0x7FFF;
|
||||
|
||||
hashVal[hPos] = val;
|
||||
int tmp = head[val];
|
||||
prev[hPos] = tmp != -1 && hashVal[tmp] == val ? tmp : hPos;
|
||||
head[val] = hPos;
|
||||
|
||||
tmp = same[(i - 1) & 0x7FFF];
|
||||
if (tmp < 1) {
|
||||
tmp = 1;
|
||||
}
|
||||
tmp += i;
|
||||
byte b = input[i];
|
||||
while (tmp < to && b == input[tmp]) {
|
||||
tmp++;
|
||||
}
|
||||
tmp -= i;
|
||||
tmp--;
|
||||
same[hPos] = tmp;
|
||||
|
||||
tmp = ((tmp - 3) & 0xFF) ^ val;
|
||||
hashVal2[hPos] = tmp;
|
||||
int h = head2[tmp];
|
||||
prev2[hPos] = h != -1 && hashVal2[h] == tmp ? h : hPos;
|
||||
head2[tmp] = hPos;
|
||||
}
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
/*private void updateHashValue(int c) {
|
||||
val = ((val << HASH_SHIFT) ^ c) & HASH_MASK;
|
||||
}*/
|
||||
|
||||
public void updateHash(byte[] input, int pos, int end) {
|
||||
// WINDOW_MASK
|
||||
int hPos = pos & 0x7FFF;
|
||||
int val = this.val;
|
||||
|
||||
val = ((val << 5) ^ (pos + 2 < end ? input[pos + 2] & 0xFF : 0)) & 0x7FFF;
|
||||
|
||||
hashVal[hPos] = val;
|
||||
int tmp = head[val];
|
||||
prev[hPos] = (tmp != -1 && hashVal[tmp] == val) ? tmp : hPos;
|
||||
head[val] = hPos;
|
||||
|
||||
tmp = same[(pos - 1) & 0x7FFF];
|
||||
if (tmp < 1) {
|
||||
tmp = 1;
|
||||
}
|
||||
tmp += pos;
|
||||
byte b = input[pos];
|
||||
while (tmp < end && b == input[tmp]) {
|
||||
tmp++;
|
||||
}
|
||||
tmp -= pos;
|
||||
tmp--;
|
||||
same[hPos] = tmp;
|
||||
|
||||
tmp = ((tmp - 3) & 0xFF) ^ val;
|
||||
hashVal2[hPos] = tmp;
|
||||
int h = head2[tmp];
|
||||
prev2[hPos] = h != -1 && hashVal2[h] == tmp ? h : hPos;
|
||||
head2[tmp] = hPos;
|
||||
|
||||
this.val = val;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
import net.sourceforge.plantuml.zopfli.Cookie.Node;
|
||||
|
||||
class Katajainen {
|
||||
|
||||
static void lengthLimitedCodeLengths(Cookie cookie, int[] frequencies, int maxBits,
|
||||
int[] bitLengths) {
|
||||
cookie.resetPool();
|
||||
int n = frequencies.length;
|
||||
int nn = 0;
|
||||
Node[] leaves = cookie.leaves1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (frequencies[i] != 0) {
|
||||
leaves[nn] = cookie.node(frequencies[i], i, null);
|
||||
nn++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nn == 0) {
|
||||
return;
|
||||
}
|
||||
if (nn == 1) {
|
||||
bitLengths[leaves[0].count] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Node[] leaves2 = cookie.leaves2;
|
||||
System.arraycopy(leaves, 0, leaves2, 0, nn);
|
||||
sort(leaves2, leaves, 0, nn);
|
||||
|
||||
Node[] list0 = cookie.list0;
|
||||
Node node0 = cookie.node(leaves[0].weight, 1, null);
|
||||
|
||||
Node[] list1 = cookie.list1;
|
||||
Node node1 = cookie.node(leaves[1].weight, 2, null);
|
||||
|
||||
for (int i = 0; i < maxBits; ++i) {
|
||||
list0[i] = node0;
|
||||
list1[i] = node1;
|
||||
}
|
||||
|
||||
int numBoundaryPmRuns = 2 * nn - 4;
|
||||
for (int i = 0; i < numBoundaryPmRuns; i++) {
|
||||
boolean last = i == numBoundaryPmRuns - 1;
|
||||
boundaryPm(cookie, leaves, list0, list1, nn, maxBits - 1, last);
|
||||
}
|
||||
|
||||
for (Node node = list1[maxBits - 1]; node != null; node = node.tail) {
|
||||
for (int i = node.count - 1; i >= 0; --i) {
|
||||
bitLengths[leaves[i].count]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void boundaryPm(Cookie cookie, Node[] leaves, Node[] list0, Node[] list1, int numSymbols, int index,
|
||||
boolean last) {
|
||||
int lastCount = list1[index].count;
|
||||
|
||||
if (index == 0 && lastCount >= numSymbols) {
|
||||
return;
|
||||
}
|
||||
|
||||
list0[index] = list1[index];
|
||||
|
||||
if (index == 0) {
|
||||
list1[index] = cookie.node(leaves[lastCount].weight, lastCount + 1, null);
|
||||
} else {
|
||||
int sum = list0[index - 1].weight + list1[index - 1].weight;
|
||||
if (lastCount < numSymbols && sum > leaves[lastCount].weight) {
|
||||
list1[index] = cookie.node(leaves[lastCount].weight, lastCount + 1, list1[index].tail);
|
||||
} else {
|
||||
list1[index] = cookie.node(sum, lastCount, list1[index - 1]);
|
||||
if (!last) {
|
||||
boundaryPm(cookie, leaves, list0, list1, numSymbols, index - 1, false);
|
||||
boundaryPm(cookie, leaves, list0, list1, numSymbols, index - 1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void sort(Node[] src, Node[] dest, int low, int high) {
|
||||
int length = high - low;
|
||||
|
||||
if (length < 7) {
|
||||
for (int i = low + 1; i < high; i++)
|
||||
for (int j = i, k = i - 1; j > low && (dest[k].weight > dest[j].weight); --j, --k) {
|
||||
Node t = dest[j];
|
||||
dest[j] = dest[k];
|
||||
dest[k] = t;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int mid = (low + high) >>> 1;
|
||||
sort(dest, src, low, mid);
|
||||
sort(dest, src, mid, high);
|
||||
|
||||
for (int i = low, p = low, q = mid; i < high; i++) {
|
||||
if (q >= high || p < mid && (src[p].weight <= src[q].weight))
|
||||
dest[i] = src[p++];
|
||||
else
|
||||
dest[i] = src[q++];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class LongestMatchCache {
|
||||
|
||||
private final static int CACHE_LENGTH = 8;
|
||||
|
||||
public final char[] length;
|
||||
public final char[] dist;
|
||||
private final char[] subLenPos;
|
||||
private final byte[] subLenLen;
|
||||
|
||||
LongestMatchCache(int maxBlockSize) {
|
||||
length = new char[maxBlockSize];
|
||||
dist = new char[maxBlockSize];
|
||||
subLenPos = new char[CACHE_LENGTH * maxBlockSize];
|
||||
subLenLen = new byte[CACHE_LENGTH * maxBlockSize];
|
||||
}
|
||||
|
||||
void init(int blockSize) {
|
||||
Cookie.fill0(dist, blockSize);
|
||||
int n = blockSize << 3; // * CACHE_LENGTH
|
||||
char[] subLenPos = this.subLenPos;
|
||||
byte[] subLenLen = this.subLenLen;
|
||||
char[] length = this.length;
|
||||
|
||||
char[] charZeroes = Cookie.charZeroes;
|
||||
byte[] byteZeroes = Cookie.byteZeroes;
|
||||
char[] charOnes = Cookie.charOnes;
|
||||
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
int j = i + 65536;
|
||||
if (j > n) {
|
||||
j = n;
|
||||
}
|
||||
int l = j - i;
|
||||
System.arraycopy(byteZeroes, 0, subLenLen, i, l);
|
||||
System.arraycopy(charZeroes, 0, subLenPos, i, l);
|
||||
i = j;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (i < blockSize) {
|
||||
int j = i + 65536;
|
||||
if (j > blockSize) {
|
||||
j = blockSize;
|
||||
}
|
||||
int l = j - i;
|
||||
System.arraycopy(charOnes, 0, length, i, l);
|
||||
System.arraycopy(charZeroes, 0, subLenPos, i, l);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
void subLenToCache(char[] input, int pos, int len) {
|
||||
if (len < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
int bestLength = 0;
|
||||
int j = pos * CACHE_LENGTH;
|
||||
int last = j + CACHE_LENGTH - 1;
|
||||
for (int i = 3; i <= len; ++i) {
|
||||
if (i == len || input[i] != input[i + 1]) {
|
||||
subLenPos[j] = input[i];
|
||||
subLenLen[j] = (byte) (i - 3);
|
||||
bestLength = i;
|
||||
j++;
|
||||
if (j > last) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j <= last) {
|
||||
subLenLen[last] = (byte) (bestLength - 3);
|
||||
}
|
||||
}
|
||||
|
||||
void cacheToSubLen(int pos, int len, char[] output) {
|
||||
if (len < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxLength = maxCachedSubLen(pos);
|
||||
int prevLength = 0;
|
||||
int j = CACHE_LENGTH * pos;
|
||||
int last = j + CACHE_LENGTH;
|
||||
for (; j < last; ++j) {
|
||||
int cLen = (subLenLen[j] & 0xFF) + 3;
|
||||
char dist = subLenPos[j];
|
||||
for (int i = prevLength; i <= cLen; ++i) {
|
||||
output[i] = dist;
|
||||
}
|
||||
if (cLen == maxLength) {
|
||||
break;
|
||||
}
|
||||
prevLength = cLen + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int maxCachedSubLen(int pos) {
|
||||
pos = pos * CACHE_LENGTH;
|
||||
if (subLenPos[pos] == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (subLenLen[pos + CACHE_LENGTH - 1] & 0xFF) + 3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
final class LzStore {
|
||||
final char[] litLens;
|
||||
final char[] dists;
|
||||
int size;
|
||||
|
||||
LzStore(final int maxBlockSize) {
|
||||
litLens = new char[maxBlockSize];
|
||||
dists = new char[maxBlockSize];
|
||||
}
|
||||
|
||||
final void append(final char length, final char dist) {
|
||||
litLens[size] = length;
|
||||
dists[size++] = dist;
|
||||
}
|
||||
|
||||
final void reset() {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
final void copy(final LzStore source) {
|
||||
size = source.size;
|
||||
System.arraycopy(source.litLens, 0, litLens, 0, size);
|
||||
System.arraycopy(source.dists, 0, dists, 0, size);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
public class Options {
|
||||
public static enum BlockSplitting {
|
||||
FIRST,
|
||||
LAST,
|
||||
NONE
|
||||
}
|
||||
|
||||
public static enum OutputFormat {
|
||||
DEFLATE,
|
||||
GZIP,
|
||||
ZLIB
|
||||
}
|
||||
|
||||
public final int numIterations;
|
||||
public final BlockSplitting blockSplitting;
|
||||
public final OutputFormat outputType;
|
||||
|
||||
public Options(OutputFormat outputType, BlockSplitting blockSplitting,
|
||||
int numIterations) {
|
||||
this.outputType = outputType;
|
||||
this.blockSplitting = blockSplitting;
|
||||
this.numIterations = numIterations;
|
||||
}
|
||||
|
||||
public Options() {
|
||||
this(OutputFormat.GZIP, BlockSplitting.FIRST, 15);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class Squeeze {
|
||||
|
||||
static LzStore optimal(Cookie cookie, int numIterations, LongestMatchCache lmc, byte[] input, int from, int to) {
|
||||
LzStore currentStore = cookie.store1;
|
||||
currentStore.reset();
|
||||
LzStore store = cookie.store2;
|
||||
Deflate.greedy(cookie, lmc, input, from, to, currentStore);
|
||||
SymbolStats stats = cookie.stats;
|
||||
SymbolStats bestStats = cookie.bestStats;
|
||||
SymbolStats lastStats = cookie.lastStats;
|
||||
stats.getFreqs(currentStore);
|
||||
|
||||
char[] lengthArray = cookie.lengthArray;
|
||||
long[] costs = cookie.costs;
|
||||
|
||||
int cost;
|
||||
int bestCost = Integer.MAX_VALUE;
|
||||
int lastCost = 0;
|
||||
int lastRandomStep = -1;
|
||||
|
||||
for (int i = 0; i < numIterations; i++) {
|
||||
currentStore.reset();
|
||||
bestLengths(cookie, lmc, from, input, from, to, stats.minCost(), stats, lengthArray, costs);
|
||||
optimalRun(cookie, lmc, input, from, to, lengthArray, currentStore);
|
||||
cost = Deflate.calculateBlockSize(cookie, currentStore.litLens, currentStore.dists, 0, currentStore.size);
|
||||
if (cost < bestCost) {
|
||||
store.copy(currentStore);
|
||||
bestStats.copy(stats);
|
||||
bestCost = cost;
|
||||
}
|
||||
lastStats.copy(stats);
|
||||
stats.getFreqs(currentStore);
|
||||
if (lastRandomStep != -1) {
|
||||
stats.alloy(lastStats);
|
||||
stats.calculate();
|
||||
}
|
||||
if (i > 5 && cost == lastCost) {
|
||||
stats.copy(bestStats);
|
||||
cookie.rnd = stats.randomizeFreqs(cookie.rnd);
|
||||
stats.calculate();
|
||||
lastRandomStep = i;
|
||||
}
|
||||
lastCost = cost;
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
static void optimalRun(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to,
|
||||
char[] lengthArray, LzStore store) {
|
||||
// assert from != to
|
||||
char[] path = cookie.path;
|
||||
int pathSize = 0;
|
||||
int size = to - from;
|
||||
do {
|
||||
char las = lengthArray[size];
|
||||
path[pathSize++] = las;
|
||||
size -= las;
|
||||
} while (size != 0);
|
||||
|
||||
int windowStart = Math.max(from - 0x8000, 0);
|
||||
Hash h = cookie.h;
|
||||
h.init(input, windowStart, from, to);
|
||||
int pos = from;
|
||||
|
||||
do {
|
||||
h.updateHash(input, pos, to);
|
||||
int length = path[--pathSize];
|
||||
if (length >= 3) {
|
||||
Deflate.findLongestMatch(cookie, lmc, from, h, input, pos, to, length, null);
|
||||
store.append((char) length, (char) cookie.distVal);
|
||||
} else {
|
||||
length = 1;
|
||||
store.append((char) (input[pos] & 0xFF), (char) 0);
|
||||
}
|
||||
|
||||
for (int j = 1; j < length; ++j) {
|
||||
h.updateHash(input, pos + j, to);
|
||||
}
|
||||
pos += length;
|
||||
} while (pathSize != 0);
|
||||
}
|
||||
|
||||
private static long fixedCost(int litLen, int dist) {
|
||||
if (dist == 0) {
|
||||
if (litLen <= 143) {
|
||||
return 8;
|
||||
}
|
||||
return 9;
|
||||
} else {
|
||||
long cost = 12 + (dist < 4097 ? Util.CACHED_DIST_EXTRA_BITS[dist] : dist < 16385 ? dist < 8193 ? 11 : 12 : 13)
|
||||
+ Util.LENGTH_EXTRA_BITS[litLen];
|
||||
if (Util.LENGTH_SYMBOL[litLen] > 279) {
|
||||
return cost + 1;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
private static void bestLengths(Cookie cookie, LongestMatchCache lmc, int blockStart, byte[] input, int from, int to,
|
||||
long minCost, SymbolStats stats, char[] lengthArray, long[] costs) {
|
||||
//# WINDOW_SIZE = 0x8000
|
||||
//# WINDOW_MASK = 0x7FFF
|
||||
//# MAX_MATCH = 258
|
||||
|
||||
int windowStart = Math.max(from - 0x8000, 0);
|
||||
Hash h = cookie.h;
|
||||
h.init(input, windowStart, from, to);
|
||||
Cookie.fillCostMax(costs, to - from + 1);
|
||||
costs[0] = 0L;
|
||||
lengthArray[0] = 0;
|
||||
int[] same = h.same;
|
||||
|
||||
char[] subLen = cookie.c259a;
|
||||
System.arraycopy(Cookie.charZeroes, 0, subLen, 0, 259);
|
||||
|
||||
long[] slLiterals = stats.lLiterals;
|
||||
long[] slLengths = stats.lLengths;
|
||||
long[] sdSymbols = stats.dSymbols;
|
||||
long stepCost = slLengths[258] + sdSymbols[0];
|
||||
|
||||
int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;
|
||||
|
||||
int i = from;
|
||||
int j = 0;
|
||||
while (i < to) {
|
||||
h.updateHash(input, i, to);
|
||||
|
||||
if (same[i & 0x7FFF] > 516 && i > from + 259 && i + 517 < to && same[(i - 258) & 0x7FFF] > 258) {
|
||||
for (int k = 0; k < 258; ++k) {
|
||||
costs[j + 258] = costs[j] + stepCost;
|
||||
lengthArray[j + 258] = 258;
|
||||
i++;
|
||||
j++;
|
||||
h.updateHash(input, i, to);
|
||||
}
|
||||
}
|
||||
|
||||
Deflate.findLongestMatch(cookie, lmc, blockStart, h, input, i, to, 258, subLen);
|
||||
|
||||
long costsJ = costs[j];
|
||||
if (i + 1 <= to) {
|
||||
long newCost = costsJ + slLiterals[input[i] & 0xFF];
|
||||
if (newCost < costs[j + 1]) {
|
||||
costs[j + 1] = newCost;
|
||||
lengthArray[j + 1] = 1;
|
||||
}
|
||||
}
|
||||
int lenValue = cookie.lenVal;
|
||||
long baseCost = minCost + costsJ;
|
||||
if (lenValue > to - i) {
|
||||
lenValue = to - i;
|
||||
}
|
||||
int jpk = j + 3;
|
||||
for (char k = 3; k <= lenValue; k++, jpk++) {
|
||||
if (costs[jpk] > baseCost) {
|
||||
long newCost = costsJ + (slLengths[k] + sdSymbols[cachedDistSymbol[subLen[k]]]);
|
||||
if (costs[jpk] > newCost) {
|
||||
costs[jpk] = newCost;
|
||||
lengthArray[jpk] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bestFixedLengths(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to,
|
||||
char[] lengthArray, long[] costs) {
|
||||
int windowStart = Math.max(from - 0x8000, 0);
|
||||
Hash h = cookie.h;
|
||||
h.init(input, windowStart, from, to);
|
||||
Cookie.fillCostMax(costs, to - from + 1);
|
||||
costs[0] = 0L;
|
||||
lengthArray[0] = 0;
|
||||
|
||||
char[] subLen = cookie.c259a;
|
||||
for (int i = from; i < to; i++) {
|
||||
int j = i - from;
|
||||
h.updateHash(input, i, to);
|
||||
|
||||
if (h.same[i & 0x7FFF] > 258 * 2
|
||||
&& i > from + 258 + 1
|
||||
&& i + 258 * 2 + 1 < to
|
||||
&& h.same[(i - 258) & 0x7FFF]
|
||||
> 258) {
|
||||
long symbolCost = fixedCost(258, 1);
|
||||
for (int k = 0; k < 258; k++) {
|
||||
costs[j + 258] = costs[j] + symbolCost;
|
||||
lengthArray[j + 258] = 258;
|
||||
i++;
|
||||
j++;
|
||||
h.updateHash(input, i, to);
|
||||
}
|
||||
}
|
||||
|
||||
Deflate.findLongestMatch(cookie, lmc, from, h, input, i, to, 258, subLen);
|
||||
|
||||
if (i + 1 <= to) {
|
||||
long newCost = costs[j] + fixedCost(input[i] & 0xFF, 0);
|
||||
if (newCost < costs[j + 1]) {
|
||||
costs[j + 1] = newCost;
|
||||
lengthArray[j + 1] = 1;
|
||||
}
|
||||
}
|
||||
int lenValue = cookie.lenVal;
|
||||
for (char k = 3; k <= lenValue && i + k <= to; k++) {
|
||||
if (costs[j + k] - costs[j] <= 12.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long newCost = costs[j] + fixedCost(k, subLen[k]);
|
||||
if (newCost < costs[j + k]) {
|
||||
costs[j + k] = newCost;
|
||||
lengthArray[j + k] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
final class SymbolStats {
|
||||
private final static double INV_LOG_2 = 1.4426950408889 * 0x10000L; /* 1.0 / log(2.0) */
|
||||
private final int[] litLens = new int[288];
|
||||
private final int[] dists = new int[32]; // Why 32? Expect 30.
|
||||
final long[] lLiterals = new long[288];
|
||||
final long[] lLengths = new long[259];
|
||||
final long[] dSymbols = new long[32];
|
||||
|
||||
void getFreqs(LzStore store) {
|
||||
int[] sLitLens = this.litLens;
|
||||
int[] sDists = this.dists;
|
||||
System.arraycopy(Cookie.intZeroes, 0, sLitLens, 0, 288);
|
||||
System.arraycopy(Cookie.intZeroes, 0, sDists, 0, 32);
|
||||
|
||||
int size = store.size;
|
||||
char[] litLens = store.litLens;
|
||||
char[] dists = store.dists;
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;
|
||||
for (int i = 0; i < size; i++) {
|
||||
int d = dists[i];
|
||||
int l = litLens[i];
|
||||
if (d == 0) {
|
||||
sLitLens[l]++;
|
||||
} else {
|
||||
sLitLens[lengthSymbol[l]]++;
|
||||
sDists[cachedDistSymbol[d]]++;
|
||||
}
|
||||
}
|
||||
sLitLens[256] = 1;
|
||||
calculate();
|
||||
}
|
||||
|
||||
final void copy(final SymbolStats source) {
|
||||
System.arraycopy(source.litLens, 0, litLens, 0, 288);
|
||||
System.arraycopy(source.dists, 0, dists, 0, 32);
|
||||
System.arraycopy(source.lLiterals, 0, lLiterals, 0, 288);
|
||||
System.arraycopy(source.lLengths, 0, lLengths, 0, 259);
|
||||
System.arraycopy(source.dSymbols, 0, dSymbols, 0, 32);
|
||||
}
|
||||
|
||||
final void calculate() {
|
||||
calculateLens();
|
||||
calculateDists();
|
||||
}
|
||||
|
||||
final void calculateLens() {
|
||||
int sum = 0;
|
||||
int[] litLens = this.litLens;
|
||||
for (int i = 0; i < 288; ++i) {
|
||||
sum += litLens[i];
|
||||
}
|
||||
double log2sum = (sum == 0 ? Math.log(288) : Math.log(sum)) * INV_LOG_2;
|
||||
long[] lLiterals = this.lLiterals;
|
||||
for (int i = 0; i < 288; ++i) {
|
||||
if (litLens[i] == 0) {
|
||||
lLiterals[i] = (long)log2sum;
|
||||
} else {
|
||||
lLiterals[i] = (long)(log2sum - Math.log(litLens[i]) * INV_LOG_2);
|
||||
}
|
||||
if (lLiterals[i] < 0) {
|
||||
lLiterals[i] = 0;
|
||||
}
|
||||
}
|
||||
long[] lLengths = this.lLengths;
|
||||
int[] lengthSymbol = Util.LENGTH_SYMBOL;
|
||||
int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS;
|
||||
for (int i = 0; i < 259; ++i) {
|
||||
lLengths[i] = lLiterals[lengthSymbol[i]] + (lengthExtraBits[i] * 0x10000L);
|
||||
}
|
||||
}
|
||||
|
||||
final void calculateDists() {
|
||||
int sum = 0;
|
||||
int[] dists = this.dists;
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
sum += dists[i];
|
||||
}
|
||||
double log2sum = (sum == 0 ? Math.log(32) : Math.log(sum)) * INV_LOG_2;
|
||||
long[] dSymbols = this.dSymbols;
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
if (dists[i] == 0) {
|
||||
dSymbols[i] = (long)log2sum;
|
||||
} else {
|
||||
dSymbols[i] = (long)(log2sum - Math.log(dists[i]) * INV_LOG_2);
|
||||
}
|
||||
if (dSymbols[i] < 0) {
|
||||
dSymbols[i] = 0;
|
||||
}
|
||||
}
|
||||
for (int i = 4; i < 30; ++i) {
|
||||
dSymbols[i] += 0x10000L * ((i / 2) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
final void alloy(final SymbolStats ligand) {
|
||||
int[] ligandLitLens = ligand.litLens;
|
||||
for (int i = 0; i < 288; i++) {
|
||||
litLens[i] += ligandLitLens[i] / 2;
|
||||
}
|
||||
litLens[256] = 1;
|
||||
|
||||
int[] ligandDists = ligand.dists;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
dists[i] += ligandDists[i] / 2;
|
||||
}
|
||||
}
|
||||
|
||||
final int randomizeFreqs(int z) {
|
||||
int[] data = litLens;
|
||||
int n = data.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
z = 0x7FFFFFFF & (1103515245 * z + 12345);
|
||||
if ((z >>> 4) % 3 == 0) {
|
||||
z = 0x7FFFFFFF & (1103515245 * z + 12345);
|
||||
int p = z % n;
|
||||
if (data[i] < data[p]) {
|
||||
data[i] = data[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
data[256] = 1;
|
||||
|
||||
data = dists;
|
||||
n = data.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
z = 0x7FFFFFFF & (1103515245 * z + 12345);
|
||||
if ((z >>> 4) % 3 == 0) {
|
||||
z = 0x7FFFFFFF & (1103515245 * z + 12345);
|
||||
int p = z % n;
|
||||
if (data[i] < data[p]) {
|
||||
data[i] = data[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
final long minCost() {
|
||||
long[] lLengths = this.lLengths;
|
||||
long minLengthCost = lLengths[3];
|
||||
for (int i = 4; i < 259; i++) {
|
||||
long c = lLengths[i];
|
||||
if (c < minLengthCost) {
|
||||
minLengthCost = c;
|
||||
}
|
||||
}
|
||||
|
||||
long[] dSymbols = this.dSymbols;
|
||||
long minDistCost = dSymbols[0];
|
||||
for (int i = 1; i < 30; i++) {
|
||||
long c = dSymbols[i];
|
||||
if (c < minDistCost) {
|
||||
minDistCost = c;
|
||||
}
|
||||
}
|
||||
|
||||
return minDistCost + minLengthCost;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
class Util {
|
||||
|
||||
static final int[] LENGTH_SYMBOL = new int[]{
|
||||
0, 0, 0,
|
||||
257, 258, 259, 260, 261, 262, 263, 264,
|
||||
265, 265,
|
||||
266, 266,
|
||||
267, 267,
|
||||
268, 268,
|
||||
269, 269, 269, 269,
|
||||
270, 270, 270, 270,
|
||||
271, 271, 271, 271,
|
||||
272, 272, 272, 272,
|
||||
273, 273, 273, 273, 273, 273, 273, 273,
|
||||
274, 274, 274, 274, 274, 274, 274, 274,
|
||||
275, 275, 275, 275, 275, 275, 275, 275,
|
||||
276, 276, 276, 276, 276, 276, 276, 276,
|
||||
277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
||||
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
|
||||
279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279,
|
||||
280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
|
||||
281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
|
||||
282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,
|
||||
283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
|
||||
284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
|
||||
285
|
||||
};
|
||||
|
||||
static final int[] LENGTH_EXTRA_BITS = new int[]{
|
||||
0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1,
|
||||
1, 1,
|
||||
1, 1,
|
||||
1, 1,
|
||||
2, 2, 2, 2,
|
||||
2, 2, 2, 2,
|
||||
2, 2, 2, 2,
|
||||
2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
0
|
||||
};
|
||||
|
||||
static final int[] LENGTH_EXTRA_BITS_VALUE = new int[]{
|
||||
0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1,
|
||||
0, 1,
|
||||
0, 1,
|
||||
0, 1,
|
||||
0, 1, 2, 3,
|
||||
0, 1, 2, 3,
|
||||
0, 1, 2, 3,
|
||||
0, 1, 2, 3,
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
0
|
||||
};
|
||||
|
||||
static final int[] ORDER = new int[]{
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||
};
|
||||
|
||||
static final int[] CACHED_DIST_SYMBOL = cacheDistSymbol();
|
||||
|
||||
private static int[] cacheDistSymbol() {
|
||||
int[] r = new int[32768];
|
||||
for (int i = 0; i < 32768; ++i) {
|
||||
r[i] = distSymbol(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static int distSymbol(int dist) {
|
||||
if (dist < 193) {
|
||||
if (dist < 13) { /* dist 0..13. */
|
||||
if (dist < 5) return dist - 1;
|
||||
else if (dist < 7) return 4;
|
||||
else if (dist < 9) return 5;
|
||||
else return 6;
|
||||
} else { /* dist 13..193. */
|
||||
if (dist < 17) return 7;
|
||||
else if (dist < 25) return 8;
|
||||
else if (dist < 33) return 9;
|
||||
else if (dist < 49) return 10;
|
||||
else if (dist < 65) return 11;
|
||||
else if (dist < 97) return 12;
|
||||
else if (dist < 129) return 13;
|
||||
else return 14;
|
||||
}
|
||||
} else {
|
||||
if (dist < 2049) { /* dist 193..2049. */
|
||||
if (dist < 257) return 15;
|
||||
else if (dist < 385) return 16;
|
||||
else if (dist < 513) return 17;
|
||||
else if (dist < 769) return 18;
|
||||
else if (dist < 1025) return 19;
|
||||
else if (dist < 1537) return 20;
|
||||
else return 21;
|
||||
} else { /* dist 2049..32768. */
|
||||
if (dist < 3073) return 22;
|
||||
else if (dist < 4097) return 23;
|
||||
else if (dist < 6145) return 24;
|
||||
else if (dist < 8193) return 25;
|
||||
else if (dist < 12289) return 26;
|
||||
else if (dist < 16385) return 27;
|
||||
else if (dist < 24577) return 28;
|
||||
else return 29;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int[] CACHED_DIST_EXTRA_BITS = precacheDistExtraBits();
|
||||
|
||||
private static int[] precacheDistExtraBits() {
|
||||
int[] r = new int[4097];
|
||||
for (int i = 0; i < 4097; ++i) {
|
||||
r[i] = distExtraBits(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static int distExtraBits(int dist) {
|
||||
if (dist < 5) {
|
||||
return 0;
|
||||
} else if (dist < 9) {
|
||||
return 1;
|
||||
} else if (dist < 17) {
|
||||
return 2;
|
||||
} else if (dist < 33) {
|
||||
return 3;
|
||||
} else if (dist < 65) {
|
||||
return 4;
|
||||
} else if (dist < 129) {
|
||||
return 5;
|
||||
} else if (dist < 257) {
|
||||
return 6;
|
||||
} else if (dist < 513) {
|
||||
return 7;
|
||||
} else if (dist < 1025) {
|
||||
return 8;
|
||||
} else if (dist < 2049) {
|
||||
return 9;
|
||||
} else if (dist < 4097) {
|
||||
return 10;
|
||||
} else if (dist < 8193) {
|
||||
return 11;
|
||||
} else if (dist < 16385) {
|
||||
return 12;
|
||||
}
|
||||
return 13;
|
||||
}
|
||||
|
||||
static int distExtraBitsValue(int dist) {
|
||||
if (dist < 5) {
|
||||
return 0;
|
||||
} else if (dist < 9) {
|
||||
return (dist - 5) & 1;
|
||||
} else if (dist < 17) {
|
||||
return (dist - 9) & 3;
|
||||
} else if (dist < 33) {
|
||||
return (dist - 17) & 7;
|
||||
} else if (dist < 65) {
|
||||
return (dist - 33) & 15;
|
||||
} else if (dist < 129) {
|
||||
return (dist - 65) & 31;
|
||||
} else if (dist < 257) {
|
||||
return (dist - 129) & 63;
|
||||
} else if (dist < 513) {
|
||||
return (dist - 257) & 127;
|
||||
} else if (dist < 1025) {
|
||||
return (dist - 513) & 255;
|
||||
} else if (dist < 2049) {
|
||||
return (dist - 1025) & 511;
|
||||
} else if (dist < 4097) {
|
||||
return (dist - 2049) & 1023;
|
||||
} else if (dist < 8193) {
|
||||
return (dist - 4097) & 2047;
|
||||
} else if (dist < 16385) {
|
||||
return (dist - 8193) & 4095;
|
||||
}
|
||||
return (dist - 16385) & 8191;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. 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.
|
||||
|
||||
Author: eustas.ru@gmail.com (Eugene Klyuchnikov)
|
||||
*/
|
||||
|
||||
package net.sourceforge.plantuml.zopfli;
|
||||
|
||||
public class Zopfli {
|
||||
|
||||
private static class Crc {
|
||||
|
||||
private final static int[] table = makeTable();
|
||||
|
||||
private static int[] makeTable() {
|
||||
int[] result = new int[256];
|
||||
|
||||
for (int n = 0; n < 256; ++n) {
|
||||
int c = n;
|
||||
for (int k = 0; k < 8; ++k) {
|
||||
if ((c & 1) == 1) {
|
||||
c = 0xEDB88320 ^ (c >>> 1);
|
||||
} else {
|
||||
c = c >>> 1;
|
||||
}
|
||||
}
|
||||
result[n] = c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int calculate(byte[] input) {
|
||||
int c = ~0;
|
||||
for (int i = 0, n = input.length; i < n; ++i) {
|
||||
c = table[(c ^ input[i]) & 0xFF] ^ (c >>> 8);
|
||||
}
|
||||
return ~c;
|
||||
}
|
||||
}
|
||||
|
||||
private final Cookie cookie;
|
||||
|
||||
public synchronized Buffer compress(Options options, byte[] input) {
|
||||
Buffer output = new Buffer();
|
||||
switch (options.outputType) {
|
||||
case GZIP:
|
||||
compressGzip(options, input, output);
|
||||
break;
|
||||
|
||||
case ZLIB:
|
||||
compressZlib(options, input, output);
|
||||
break;
|
||||
case DEFLATE:
|
||||
Deflate.compress(cookie, options, input, output);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected output format: " + options.outputType);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the adler32 checksum of the data
|
||||
*/
|
||||
private static int adler32(byte[] data) {
|
||||
int s1 = 1;
|
||||
int s2 = 1 >> 16;
|
||||
int i = 0;
|
||||
while (i < data.length) {
|
||||
int tick = Math.min(data.length, i + 1024);
|
||||
while (i < tick) {
|
||||
s1 += data[i++];
|
||||
s2 += s1;
|
||||
}
|
||||
s1 %= 65521;
|
||||
s2 %= 65521;
|
||||
}
|
||||
|
||||
return (s2 << 16) | s1;
|
||||
}
|
||||
|
||||
|
||||
private void compressZlib(Options options, byte[] input,
|
||||
Buffer output) {
|
||||
output.append((byte) 0x78);
|
||||
output.append((byte) 0x1E);
|
||||
|
||||
Deflate.compress(cookie, options, input, output);
|
||||
|
||||
int checksum = adler32(input);
|
||||
output.append((byte) ((checksum >> 24) & 0xFF));
|
||||
output.append((byte) ((checksum >> 16) & 0xFF));
|
||||
output.append((byte) ((checksum >> 8) & 0xFF));
|
||||
output.append((byte) (checksum & 0xFF));
|
||||
}
|
||||
|
||||
private void compressGzip(Options options, byte[] input,
|
||||
Buffer output) {
|
||||
output.append((byte) 31);
|
||||
output.append((byte) 139);
|
||||
output.append((byte) 8);
|
||||
output.append((byte) 0);
|
||||
|
||||
output.append((byte) 0);
|
||||
output.append((byte) 0);
|
||||
output.append((byte) 0);
|
||||
output.append((byte) 0);
|
||||
|
||||
output.append((byte) 2);
|
||||
output.append((byte) 3);
|
||||
|
||||
Deflate.compress(cookie, options, input, output);
|
||||
|
||||
int crc = Crc.calculate(input);
|
||||
output.append((byte) (crc & 0xFF));
|
||||
output.append((byte) ((crc >> 8) & 0xFF));
|
||||
output.append((byte) ((crc >> 16) & 0xFF));
|
||||
output.append((byte) ((crc >> 24) & 0xFF));
|
||||
|
||||
int size = input.length;
|
||||
output.append((byte) (size & 0xFF));
|
||||
output.append((byte) ((size >> 8) & 0xFF));
|
||||
output.append((byte) ((size >> 16) & 0xFF));
|
||||
output.append((byte) ((size >> 24) & 0xFF));
|
||||
}
|
||||
|
||||
public Zopfli(int masterBlockSize) {
|
||||
cookie = new Cookie(masterBlockSize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
/**
|
||||
* Bit reading helpers.
|
||||
*/
|
||||
final class BitReader {
|
||||
|
||||
// Added by Arnaud... not very beautifull
|
||||
private static final boolean CHECK_UNUSED_BYTES_AFTER_END = false;
|
||||
|
||||
// Possible values: {5, 6}. 5 corresponds to 32-bit build, 6 to 64-bit. This value is used for
|
||||
// conditional compilation -> produced artifacts might be binary INCOMPATIBLE (JLS 13.2).
|
||||
private static final int LOG_BITNESS = 6;
|
||||
private static final int BITNESS = 1 << LOG_BITNESS;
|
||||
|
||||
private static final int BYTENESS = BITNESS / 8;
|
||||
private static final int CAPACITY = 4096;
|
||||
// After encountering the end of the input stream, this amount of zero bytes will be appended.
|
||||
private static final int SLACK = 64;
|
||||
private static final int BUFFER_SIZE = CAPACITY + SLACK;
|
||||
// Don't bother to replenish the buffer while this number of bytes is available.
|
||||
private static final int SAFEGUARD = 36;
|
||||
private static final int WATERLINE = CAPACITY - SAFEGUARD;
|
||||
|
||||
// "Half" refers to "half of native integer type", i.e. on 64-bit machines it is 32-bit type,
|
||||
// on 32-bit machines it is 16-bit.
|
||||
private static final int HALF_BITNESS = BITNESS / 2;
|
||||
private static final int HALF_SIZE = BYTENESS / 2;
|
||||
private static final int HALVES_CAPACITY = CAPACITY / HALF_SIZE;
|
||||
private static final int HALF_BUFFER_SIZE = BUFFER_SIZE / HALF_SIZE;
|
||||
private static final int HALF_WATERLINE = WATERLINE / HALF_SIZE;
|
||||
|
||||
private static final int LOG_HALF_SIZE = LOG_BITNESS - 4;
|
||||
|
||||
/**
|
||||
* Fills up the input buffer.
|
||||
*
|
||||
* <p> No-op if there are at least 36 bytes present after current position.
|
||||
*
|
||||
* <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
|
||||
* buffer.
|
||||
*/
|
||||
static void readMoreInput(State s) {
|
||||
if (s.halfOffset > HALF_WATERLINE) {
|
||||
doReadMoreInput(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void doReadMoreInput(State s) {
|
||||
if (s.endOfStreamReached != 0) {
|
||||
if (halfAvailable(s) >= -2) {
|
||||
return;
|
||||
}
|
||||
throw new BrotliRuntimeException("No more input");
|
||||
}
|
||||
int readOffset = s.halfOffset << LOG_HALF_SIZE;
|
||||
int bytesInBuffer = CAPACITY - readOffset;
|
||||
// Move unused bytes to the head of the buffer.
|
||||
Utils.copyBytesWithin(s.byteBuffer, 0, readOffset, CAPACITY);
|
||||
s.halfOffset = 0;
|
||||
while (bytesInBuffer < CAPACITY) {
|
||||
int spaceLeft = CAPACITY - bytesInBuffer;
|
||||
int len = Utils.readInput(s.input, s.byteBuffer, bytesInBuffer, spaceLeft);
|
||||
// EOF is -1 in Java, but 0 in C#.
|
||||
if (len <= 0) {
|
||||
s.endOfStreamReached = 1;
|
||||
s.tailBytes = bytesInBuffer;
|
||||
bytesInBuffer += HALF_SIZE - 1;
|
||||
break;
|
||||
}
|
||||
bytesInBuffer += len;
|
||||
}
|
||||
bytesToNibbles(s, bytesInBuffer);
|
||||
}
|
||||
|
||||
static void checkHealth(State s, int endOfStream) {
|
||||
if (s.endOfStreamReached == 0) {
|
||||
return;
|
||||
}
|
||||
int byteOffset = (s.halfOffset << LOG_HALF_SIZE) + ((s.bitOffset + 7) >> 3) - BYTENESS;
|
||||
if (byteOffset > s.tailBytes) {
|
||||
throw new BrotliRuntimeException("Read after end");
|
||||
}
|
||||
if (CHECK_UNUSED_BYTES_AFTER_END && (endOfStream != 0) && (byteOffset != s.tailBytes)) {
|
||||
throw new BrotliRuntimeException("Unused bytes after end");
|
||||
}
|
||||
}
|
||||
|
||||
static void fillBitWindow(State s) {
|
||||
if (s.bitOffset >= HALF_BITNESS) {
|
||||
// Same as doFillBitWindow. JVM fails to inline it.
|
||||
if (BITNESS == 64) {
|
||||
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
|
||||
| (s.accumulator64 >>> HALF_BITNESS);
|
||||
} else {
|
||||
s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
|
||||
| (s.accumulator32 >>> HALF_BITNESS);
|
||||
}
|
||||
s.bitOffset -= HALF_BITNESS;
|
||||
}
|
||||
}
|
||||
|
||||
private static void doFillBitWindow(State s) {
|
||||
if (BITNESS == 64) {
|
||||
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
|
||||
| (s.accumulator64 >>> HALF_BITNESS);
|
||||
} else {
|
||||
s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
|
||||
| (s.accumulator32 >>> HALF_BITNESS);
|
||||
}
|
||||
s.bitOffset -= HALF_BITNESS;
|
||||
}
|
||||
|
||||
static int peekBits(State s) {
|
||||
if (BITNESS == 64) {
|
||||
return (int) (s.accumulator64 >>> s.bitOffset);
|
||||
} else {
|
||||
return s.accumulator32 >>> s.bitOffset;
|
||||
}
|
||||
}
|
||||
|
||||
static int readFewBits(State s, int n) {
|
||||
int val = peekBits(s) & ((1 << n) - 1);
|
||||
s.bitOffset += n;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int readBits(State s, int n) {
|
||||
if (HALF_BITNESS >= 24) {
|
||||
return readFewBits(s, n);
|
||||
} else {
|
||||
return (n <= 16) ? readFewBits(s, n) : readManyBits(s, n);
|
||||
}
|
||||
}
|
||||
|
||||
private static int readManyBits(State s, int n) {
|
||||
int low = readFewBits(s, 16);
|
||||
doFillBitWindow(s);
|
||||
return low | (readFewBits(s, n - 16) << 16);
|
||||
}
|
||||
|
||||
static void initBitReader(State s) {
|
||||
s.byteBuffer = new byte[BUFFER_SIZE];
|
||||
if (BITNESS == 64) {
|
||||
s.accumulator64 = 0;
|
||||
s.intBuffer = new int[HALF_BUFFER_SIZE];
|
||||
} else {
|
||||
s.accumulator32 = 0;
|
||||
s.shortBuffer = new short[HALF_BUFFER_SIZE];
|
||||
}
|
||||
s.bitOffset = BITNESS;
|
||||
s.halfOffset = HALVES_CAPACITY;
|
||||
s.endOfStreamReached = 0;
|
||||
prepare(s);
|
||||
}
|
||||
|
||||
private static void prepare(State s) {
|
||||
readMoreInput(s);
|
||||
checkHealth(s, 0);
|
||||
doFillBitWindow(s);
|
||||
doFillBitWindow(s);
|
||||
}
|
||||
|
||||
static void reload(State s) {
|
||||
if (s.bitOffset == BITNESS) {
|
||||
prepare(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void jumpToByteBoundary(State s) {
|
||||
int padding = (BITNESS - s.bitOffset) & 7;
|
||||
if (padding != 0) {
|
||||
int paddingBits = readFewBits(s, padding);
|
||||
if (paddingBits != 0) {
|
||||
throw new BrotliRuntimeException("Corrupted padding bits");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int halfAvailable(State s) {
|
||||
int limit = HALVES_CAPACITY;
|
||||
if (s.endOfStreamReached != 0) {
|
||||
limit = (s.tailBytes + (HALF_SIZE - 1)) >> LOG_HALF_SIZE;
|
||||
}
|
||||
return limit - s.halfOffset;
|
||||
}
|
||||
|
||||
static void copyBytes(State s, byte[] data, int offset, int length) {
|
||||
if ((s.bitOffset & 7) != 0) {
|
||||
throw new BrotliRuntimeException("Unaligned copyBytes");
|
||||
}
|
||||
|
||||
// Drain accumulator.
|
||||
while ((s.bitOffset != BITNESS) && (length != 0)) {
|
||||
data[offset++] = (byte) peekBits(s);
|
||||
s.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get data from shadow buffer with "sizeof(int)" granularity.
|
||||
int copyNibbles = Math.min(halfAvailable(s), length >> LOG_HALF_SIZE);
|
||||
if (copyNibbles > 0) {
|
||||
int readOffset = s.halfOffset << LOG_HALF_SIZE;
|
||||
int delta = copyNibbles << LOG_HALF_SIZE;
|
||||
System.arraycopy(s.byteBuffer, readOffset, data, offset, delta);
|
||||
offset += delta;
|
||||
length -= delta;
|
||||
s.halfOffset += copyNibbles;
|
||||
}
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read tail bytes.
|
||||
if (halfAvailable(s) > 0) {
|
||||
// length = 1..3
|
||||
fillBitWindow(s);
|
||||
while (length != 0) {
|
||||
data[offset++] = (byte) peekBits(s);
|
||||
s.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
checkHealth(s, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now it is possible to copy bytes directly.
|
||||
while (length > 0) {
|
||||
int len = Utils.readInput(s.input, data, offset, length);
|
||||
if (len == -1) {
|
||||
throw new BrotliRuntimeException("Unexpected end of input");
|
||||
}
|
||||
offset += len;
|
||||
length -= len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates bytes to halves (int/short).
|
||||
*/
|
||||
static void bytesToNibbles(State s, int byteLen) {
|
||||
byte[] byteBuffer = s.byteBuffer;
|
||||
int halfLen = byteLen >> LOG_HALF_SIZE;
|
||||
if (BITNESS == 64) {
|
||||
int[] intBuffer = s.intBuffer;
|
||||
for (int i = 0; i < halfLen; ++i) {
|
||||
intBuffer[i] = ((byteBuffer[i * 4] & 0xFF))
|
||||
| ((byteBuffer[(i * 4) + 1] & 0xFF) << 8)
|
||||
| ((byteBuffer[(i * 4) + 2] & 0xFF) << 16)
|
||||
| ((byteBuffer[(i * 4) + 3] & 0xFF) << 24);
|
||||
}
|
||||
} else {
|
||||
short[] shortBuffer = s.shortBuffer;
|
||||
for (int i = 0; i < halfLen; ++i) {
|
||||
shortBuffer[i] = (short) ((byteBuffer[i * 2] & 0xFF)
|
||||
| ((byteBuffer[(i * 2) + 1] & 0xFF) << 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* {@link InputStream} decorator that decompresses brotli data.
|
||||
*
|
||||
* <p> Not thread-safe.
|
||||
*/
|
||||
public class BrotliInputStream extends InputStream {
|
||||
|
||||
public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 16384;
|
||||
|
||||
/**
|
||||
* Internal buffer used for efficient byte-by-byte reading.
|
||||
*/
|
||||
private byte[] buffer;
|
||||
|
||||
/**
|
||||
* Number of decoded but still unused bytes in internal buffer.
|
||||
*/
|
||||
private int remainingBufferBytes;
|
||||
|
||||
/**
|
||||
* Next unused byte offset.
|
||||
*/
|
||||
private int bufferOffset;
|
||||
|
||||
/**
|
||||
* Decoder state.
|
||||
*/
|
||||
private final State state = new State();
|
||||
|
||||
/**
|
||||
* Creates a {@link InputStream} wrapper that decompresses brotli data.
|
||||
*
|
||||
* <p> For byte-by-byte reading ({@link #read()}) internal buffer with
|
||||
* {@link #DEFAULT_INTERNAL_BUFFER_SIZE} size is allocated and used.
|
||||
*
|
||||
* <p> Will block the thread until first kilobyte of data of source is available.
|
||||
*
|
||||
* @param source underlying data source
|
||||
* @throws IOException in case of corrupted data or source stream problems
|
||||
*/
|
||||
public BrotliInputStream(InputStream source) throws IOException {
|
||||
this(source, DEFAULT_INTERNAL_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link InputStream} wrapper that decompresses brotli data.
|
||||
*
|
||||
* <p> For byte-by-byte reading ({@link #read()}) internal buffer of specified size is
|
||||
* allocated and used.
|
||||
*
|
||||
* <p> Will block the thread until first kilobyte of data of source is available.
|
||||
*
|
||||
* @param source compressed data source
|
||||
* @param byteReadBufferSize size of internal buffer used in case of
|
||||
* byte-by-byte reading
|
||||
* @throws IOException in case of corrupted data or source stream problems
|
||||
*/
|
||||
public BrotliInputStream(InputStream source, int byteReadBufferSize) throws IOException {
|
||||
if (byteReadBufferSize <= 0) {
|
||||
throw new IllegalArgumentException("Bad buffer size:" + byteReadBufferSize);
|
||||
} else if (source == null) {
|
||||
throw new IllegalArgumentException("source is null");
|
||||
}
|
||||
this.buffer = new byte[byteReadBufferSize];
|
||||
this.remainingBufferBytes = 0;
|
||||
this.bufferOffset = 0;
|
||||
try {
|
||||
Decode.initState(state, source);
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
throw new IOException("Brotli decoder initialization failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Decode.close(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (bufferOffset >= remainingBufferBytes) {
|
||||
remainingBufferBytes = read(buffer, 0, buffer.length);
|
||||
bufferOffset = 0;
|
||||
if (remainingBufferBytes == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return buffer[bufferOffset++] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int read(byte[] destBuffer, int destOffset, int destLen) throws IOException {
|
||||
if (destOffset < 0) {
|
||||
throw new IllegalArgumentException("Bad offset: " + destOffset);
|
||||
} else if (destLen < 0) {
|
||||
throw new IllegalArgumentException("Bad length: " + destLen);
|
||||
} else if (destOffset + destLen > destBuffer.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.length);
|
||||
} else if (destLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
int copyLen = Math.max(remainingBufferBytes - bufferOffset, 0);
|
||||
if (copyLen != 0) {
|
||||
copyLen = Math.min(copyLen, destLen);
|
||||
System.arraycopy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
|
||||
bufferOffset += copyLen;
|
||||
destOffset += copyLen;
|
||||
destLen -= copyLen;
|
||||
if (destLen == 0) {
|
||||
return copyLen;
|
||||
}
|
||||
}
|
||||
try {
|
||||
state.output = destBuffer;
|
||||
state.outputOffset = destOffset;
|
||||
state.outputLength = destLen;
|
||||
state.outputUsed = 0;
|
||||
Decode.decompress(state);
|
||||
if (state.outputUsed == 0) {
|
||||
return -1;
|
||||
}
|
||||
return state.outputUsed + copyLen;
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
throw new IOException("Brotli stream decoding failed", ex);
|
||||
}
|
||||
|
||||
// <{[INJECTED CODE]}>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
/**
|
||||
* Unchecked exception used internally.
|
||||
*/
|
||||
class BrotliRuntimeException extends RuntimeException {
|
||||
|
||||
BrotliRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
BrotliRuntimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
/**
|
||||
* Common context lookup table for all context modes.
|
||||
*/
|
||||
final class Context {
|
||||
|
||||
static final int[] LOOKUP = new int[2048];
|
||||
|
||||
private static final String UTF_MAP = " !! ! \"#$##%#$&'##(#)#+++++++++"
|
||||
+ "+((&*'##,---,---,-----,-----,-----&#'###.///.///./////./////./////&#'# ";
|
||||
private static final String UTF_RLE = "A/* ': & : $ \u0081 @";
|
||||
|
||||
private static void unpackLookupTable(int[] lookup, String map, String rle) {
|
||||
// LSB6, MSB6, SIGNED
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
lookup[i] = i & 0x3F;
|
||||
lookup[512 + i] = i >> 2;
|
||||
lookup[1792 + i] = 2 + (i >> 6);
|
||||
}
|
||||
// UTF8
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
lookup[1024 + i] = 4 * (map.charAt(i) - 32);
|
||||
}
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
lookup[1152 + i] = i & 1;
|
||||
lookup[1216 + i] = 2 + (i & 1);
|
||||
}
|
||||
int offset = 1280;
|
||||
for (int k = 0; k < 19; ++k) {
|
||||
int value = k & 3;
|
||||
int rep = rle.charAt(k) - 32;
|
||||
for (int i = 0; i < rep; ++i) {
|
||||
lookup[offset++] = value;
|
||||
}
|
||||
}
|
||||
// SIGNED
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
lookup[1792 + i] = 1;
|
||||
lookup[2032 + i] = 6;
|
||||
}
|
||||
lookup[1792] = 0;
|
||||
lookup[2047] = 7;
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
lookup[1536 + i] = lookup[1792 + i] << 3;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
unpackLookupTable(LOOKUP, UTF_MAP, UTF_RLE);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue