Skip to content

Commit

Permalink
🐛 Escape image path spaces; homebrew fluff
Browse files Browse the repository at this point in the history
  • Loading branch information
ebullient committed Aug 13, 2023
1 parent 54b62e2 commit cc0208e
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 61 deletions.
2 changes: 1 addition & 1 deletion docs/ImageRef.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ Descriptive title (or caption) for the image. This can be long.

### vaultPath

Path of the image in the vault.
Path of the image in the vault or url for external images.
2 changes: 1 addition & 1 deletion examples/config/sourceMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,12 @@ _Support content creators. Only use or include sources that you own._
| FRP2 | Fists of the Ruby Phoenix #2: Ready? Fight! |
| FRP3 | Fists of the Ruby Phoenix #3: King of the Mountain |
| FoP | The Fall of Plaguestone |
| G&G | Guns & Gears |
| GMG | Gamemastery Guide |
| GW0 | Gatewalkers Player's Guide |
| GW1 | Gatewalkers #1: The Seventh Arch |
| GW2 | Gatewalkers #2: They Watched the Stars |
| GW3 | Gatewalkers #3: Dreamers of the Nameless Spires |
| GnG | Guns & Gears |
| HPD | Hero Point Deck |
| LOACLO | Lost Omens: Absalom, City of Lost Omens |
| LOAG | Lost Omens: Ancestry Guide |
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/dev/ebullient/convert/qute/ImageRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ public class ImageRef {
final Path targetFilePath;
final String url;
final Integer width;
final String vaultPath;

/** Descriptive title (or caption) for the image. This can be long. */
public final String title;
/** Path of the image in the vault. */
public final String vaultPath;

private ImageRef(String title, String url, Integer width) {
this.url = url.replace(" ", "%20");
this.sourcePath = null;
this.targetFilePath = null;
this.title = title == null ? "" : title;
this.title = title == null ? ""
: title.replaceAll("\\[(.+?)]\\(.+?\\)", "$1");
this.vaultPath = null;
this.width = width;
this.url = url;
}

private ImageRef(Path sourcePath, Path targetFilePath, String title, String vaultPath, Integer width) {
Expand All @@ -70,6 +70,11 @@ private String titleAttribute() {
return title.length() > 50 ? " \"" + title + "\"" : "";
}

/** Path of the image in the vault or url for external images. */
public String getVaultPath() {
return url == null ? vaultPath : url;
}

public String getEmbeddedLink(String anchor) {
return String.format("![%s](%s%s%s)",
getShortTitle(),
Expand Down Expand Up @@ -153,7 +158,7 @@ public Builder setStreamSource(String glyph) {
}

public Builder setTitle(String title) {
this.title = title.replaceAll("\\[(.+?)]\\(.+?\\)", "$1");
this.title = title;
return this;
}

Expand Down Expand Up @@ -186,7 +191,6 @@ public ImageRef build() {
if (url != null) {
return new ImageRef(title, url, width);
}

if (sourcePath == null || relativeTarget == null || vaultRoot == null || rootFilePath == null) {
throw new IllegalStateException("Set paths first (source, relative, vaultRoot, fileRoot) first");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,59 +58,59 @@ public String getText(String heading) {
return text.isEmpty() ? null : String.join("\n", text);
}

public String getFluffDescription(Tools5eIndexType fluffType, String heading, List<ImageRef> imageRef) {
List<String> text = getFluff(fluffType, heading, imageRef);
public String getFluffDescription(Tools5eIndexType fluffType, String heading, List<ImageRef> images) {
List<String> text = getFluff(fluffType, heading, images);
return text.isEmpty() ? null : String.join("\n", text);
}

public List<String> getFluff(Tools5eIndexType fluffType, String heading, List<ImageRef> imageRef) {
public List<String> getFluff(Tools5eIndexType fluffType, String heading, List<ImageRef> images) {
List<String> text = new ArrayList<>();
if (booleanOrDefault(rootNode, "hasFluff", false) || booleanOrDefault(rootNode, "hasFluffImages", false)) {
JsonNode fluffNode = null;
if (rootNode.has("fluff")) {
fluffNode = rootNode.get("fluff");
if (fluffNode.has("_monsterFluff")) {
String fluffKey = fluffType.createKey(fluffNode.get(""));
fluffNode = index.getNode(fluffKey);
}
} else if (booleanOrDefault(rootNode, "hasFluff", false) || booleanOrDefault(rootNode, "hasFluffImages", false)) {
String fluffKey = fluffType.createKey(rootNode);
JsonNode fluffNode = index.getNode(fluffKey);
if (fluffNode != null) {
JsonSourceCopier copier = new JsonSourceCopier(index);
fluffNode = copier.handleCopy(fluffType, fluffNode);

if (fluffNode.has("entries")) {
appendToText(text, fluffNode.get("entries"), heading);
}
fluffNode = index.getNode(fluffKey);
}

JsonNode images = fluffNode.get("images");
if (images != null && images.isArray()) {
for (Iterator<JsonNode> i = images.elements(); i.hasNext();) {
ImageRef ir = readImageRef(i.next());
if (ir != null) {
imageRef.add(ir);
}
}
}
if (fluffNode != null) {
JsonSourceCopier copier = new JsonSourceCopier(index);
fluffNode = copier.handleCopy(fluffType, fluffNode);
if (fluffNode.has("entries")) {
appendToText(text, fluffNode.get("entries"), heading);
}
getImages(fluffNode.get("images"), images);
}
return text;
}

public List<ImageRef> getFluffImages(Tools5eIndexType fluffType) {
List<ImageRef> imageRef = new ArrayList<>();
List<ImageRef> images = new ArrayList<>();
if (booleanOrDefault(rootNode, "hasFluffImages", false)) {
String fluffKey = fluffType.createKey(rootNode);
JsonNode fluffNode = index.getNode(fluffKey);
if (fluffNode != null) {
JsonSourceCopier copier = new JsonSourceCopier(index);
fluffNode = copier.handleCopy(fluffType, fluffNode);
getImages(fluffNode.get("images"), images);
}
}
return images;
}

JsonNode images = fluffNode.get("images");
if (images != null && images.isArray()) {
for (Iterator<JsonNode> i = images.elements(); i.hasNext();) {
ImageRef ir = readImageRef(i.next());
if (ir != null) {
imageRef.add(ir);
}
}
public void getImages(JsonNode imageNode, List<ImageRef> images) {
if (imageNode != null && imageNode.isArray()) {
for (Iterator<JsonNode> i = imageNode.elements(); i.hasNext();) {
ImageRef ir = readImageRef(i.next());
if (ir != null) {
images.add(ir);
}
}
}
return imageRef;
}

public final Tools5eQuteBase build() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ ImageRef getSymbolImage() {
JsonNode symbolImg = rootNode.get("symbolImg");
try {
JsonMediaHref mediaHref = mapper().treeToValue(symbolImg, JsonMediaHref.class);
return buildImageRef(index, mediaHref, getImagePath());
return buildImageRef(mediaHref, getImagePath());
} catch (JsonProcessingException | IllegalArgumentException e) {
tui().errorf("Unable to read media reference from %s", symbolImg.toPrettyString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static boolean isNpc(JsonNode source) {
return false;
}

String type;
String creatureType;
String subtype;
Integer ac;
String acText;
Expand All @@ -61,7 +61,7 @@ public static boolean isNpc(JsonNode source) {

Json2QuteMonster(Tools5eIndex index, Tools5eIndexType type, JsonNode jsonNode) {
super(index, type, jsonNode);
findType();
findCreatureType();
findAc();
findHp();
isNpc = isNpc(rootNode);
Expand All @@ -75,12 +75,11 @@ protected Tools5eQuteBase buildQuteResource() {
String pb = monsterPb(cr);

Tags tags = new Tags(getSources());

tags.add("monster", "size", slugify(size));
if (subtype == null || subtype.isEmpty()) {
tags.add("monster", "type", slugify(type));
tags.add("monster", "type", slugify(creatureType));
} else {
String sType = slugify(type);
String sType = slugify(creatureType);
for (String detail : subtype.split("\\s*,\\s*")) {
tags.add("monster", "type", sType, slugify(detail));
}
Expand All @@ -101,7 +100,7 @@ protected Tools5eQuteBase buildQuteResource() {
decoratedMonsterName(sources),
getSourceText(sources),
isNpc,
size, type, subtype, monsterAlignment(),
size, creatureType, subtype, monsterAlignment(),
ac, acText, hp, hpText, hitDice,
monsterSpeed(), monsterScores(),
monsterSavesAndSkills(),
Expand All @@ -127,19 +126,19 @@ size, type, subtype, monsterAlignment(),
cfg().alwaysUseDiceRoller());
}

void findType() {
void findCreatureType() {
JsonNode typeNode = rootNode.get("type");
if (typeNode == null) {
tui().warn("Empty type for " + getSources());
return;
}
if (typeNode.isTextual()) {
type = typeNode.asText();
creatureType = typeNode.asText();
return;
}

// We have an object: type + tags
type = typeNode.get("type").asText();
creatureType = typeNode.get("type").asText();
List<String> tags = new ArrayList<>();
typeNode.withArray("tags").forEach(tag -> {
if (tag.isTextual()) {
Expand Down Expand Up @@ -484,6 +483,7 @@ Collection<NamedText> monsterTraits(String field) {
}

ImageRef getToken() {
String tokenString = MonsterFields.tokenUrl.getTextOrNull(rootNode);
if (booleanOrDefault(rootNode, "hasToken", false)) {
// const imgLink = Renderer.monster.getTokenUrl(mon);
// return mon.tokenUrl || UrlUtil.link(`${Renderer.get().baseMediaUrls["img"] || Renderer.get().baseUrl}img/${Parser.sourceJsonToAbv(mon.source)}/${Parser.nameToTokenName(mon.name)}.png`);
Expand All @@ -505,14 +505,16 @@ ImageRef getToken() {
"token",
slugify(filename) + ".png");

return buildImageRef(index, sourcePath, target);
return buildImageRef(sourcePath, target);
} else if (tokenString != null) {
return getSources().buildImageRef(null, tokenString);
}
return null;
}

@Override
public String getImagePath() {
return Tools5eQuteBase.monsterPath(isNpc, type);
return Tools5eQuteBase.monsterPath(isNpc, creatureType);
}

public static List<Tuple> findConjuredMonsterVariants(Tools5eIndex index, Tools5eIndexType type,
Expand Down Expand Up @@ -703,6 +705,7 @@ public MonsterHp(int level, String hpString, JsonNode jsonSource) {

enum MonsterFields implements JsonNodeReader {
alignment,
alignmentPrefix
alignmentPrefix,
tokenUrl,
}
}
10 changes: 5 additions & 5 deletions src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ default String getLabeledSource(Tools5eSources currentSource) {
return "_Source: " + getSourceText(currentSource) + "_";
}

default ImageRef buildImageRef(Tools5eIndex index, Path sourcePath, Path target) {
return getSources().buildImageRef(index, sourcePath, target, useCompendium());
default ImageRef buildImageRef(Path sourcePath, Path target) {
return getSources().buildImageRef(index(), sourcePath, target, useCompendium());
}

default ImageRef buildImageRef(Tools5eIndex index, JsonMediaHref mediaHref, String imageBasePath) {
return getSources().buildImageRef(index, mediaHref, imageBasePath, useCompendium());
default ImageRef buildImageRef(JsonMediaHref mediaHref, String imageBasePath) {
return getSources().buildImageRef(index(), mediaHref, imageBasePath, useCompendium());
}

/**
Expand Down Expand Up @@ -680,7 +680,7 @@ default String findTable(Tools5eIndexType keyType, JsonNode matchTable) {
default ImageRef readImageRef(JsonNode imageNode) {
try {
JsonMediaHref mediaHref = mapper().treeToValue(imageNode, JsonMediaHref.class);
return buildImageRef(index(), mediaHref, getImagePath());
return buildImageRef(mediaHref, getImagePath());
} catch (JsonProcessingException | IllegalArgumentException e) {
tui().errorf(e, "Unable to read media reference from %s: %s", imageNode, e.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ JsonNode mergeNodes(String originKey, JsonNode baseNode, JsonNode overlayNode) {
default:
if (overlayField == null) {
target.remove(f);
} else if (_preserve == null || !_preserve.has(f)) {
} else if (_preserve == null || _preserve.has(f)) {
target.replace(f, copyNode(overlayField));
} else {
tui().debugf("Copy/Merge: Skip field %s (from %s)", f, originKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,10 @@ public String getAliasOrDefault(String key) {
return value;
}

public boolean isHomebrew() {
return homebrew != null;
}

/**
* For subclasses, class features, and subclass features,
* cross references come directly from the class definition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ public String alternateSource() {
return i.next();
}

public ImageRef buildImageRef(String title, String hrefString) {
return new ImageRef.Builder()
.setTitle(title)
.setUrl(hrefString)
.build();
}

public ImageRef buildImageRef(Tools5eIndex index, Path sourcePath, Path target, boolean useCompendium) {
ImageRef imageRef = new ImageRef.Builder()
.setRelativePath(target)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ public String get5eStatblockYaml() {
addUnlessEmpty(map, "legendary_actions", legendary);
addUnlessEmpty(map, "source", books);
if (token != null) {
map.put("image", token.vaultPath);
map.put("image", token.getVaultPath());
}

// De-markdown-ify
Expand Down
7 changes: 5 additions & 2 deletions src/test/java/dev/ebullient/convert/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ public static void checkMarkdownLinks(String baseDir, Path p, String line, List<
Path resource = p;
int hash = path.indexOf('#');

// vaultPath is used for template examples
if (path.startsWith("http") || path.contains("vaultPath")) {
if (path.startsWith("http") && path.contains(" ")) {
e.add(String.format("HTTP path with space in %s: %s ", p, m.group(0)));
return;
} else if (path.startsWith("http") || path.contains("vaultPath")) {
// vaultPath is used for template examples
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/test/resources/sources-homebrew.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"TalentPsionicsOpenPlaytestRound2",
"TBHA",
"TDCSR",
"ToB3",
"TheLostLands",
"arkadia",
"dndwiki_bestbackgrounds"
Expand Down

0 comments on commit cc0208e

Please sign in to comment.