Merge pull request #1 from bw-mutley/bw-mutley-patch-1

Adaption to latex character sheet template
This commit is contained in:
bw-mutley
2021-08-09 18:58:15 -03:00
committed by GitHub
3 changed files with 228 additions and 12 deletions
+117
View File
@@ -709,6 +709,123 @@ class Character(Creature):
s = "(See Features Page)\n\n--" + s
s += "\n\n=================\n\n"
return s
@property
def features_summary(self):
# save space for informed features and traits
if hasattr(self, "features_and_traits"):
info_list = ["**Other Features**"]
info_list += [text.strip() for text in self.features_and_traits.split("\n")
if not(text.isspace())]
N = len(info_list)
for text in info_list:
if len(text) > 26: # 26 is just a guess for expected size of lines
N += 1
if N > 30:
return "\n".join(info_list[:30]) + "\n(...)"
N = 30 - N
else:
info_list = []
N = 30
if len(self.class_list) > 1:
featS = ["**Multiclass**:"]
for cl in self.class_list:
description = cl.name
if cl.subclass:
description += f"/{cl.subclass}"
description += f" {cl.level}"
featS.append(description)
else:
featS = []
featS += ["**Features**"]
featS += [f.name for f in self.features]
if len(featS) > N:
featS = featS[:N] + ["(...)"]
featS += info_list
return "\n\n".join(featS)
@property
def equipment_text(self):
eq_list = []
if hasattr(self, "magic_items"):
eq_list += ["**Magic Items**"]
eq_list += [item.name for item in self.magic_items]
if hasattr(self, "equipment"):
eq_list += ["**Other Equipment**"]
eq_list += [text.strip() for text in self.equipment.split("\n")
if not(text.isspace())]
return "\n\n".join(eq_list)
@property
def proficiencies_by_type(self):
prof_dict = {}
w_pro = set(self.weapon_proficiencies)
if weapons.MartialWeapon in w_pro:
prof_dict["Weapons"] = ["All weapons"]
elif weapons.SimpleWeapon in w_pro:
prof_dict["Weapons"] = ["Simple weapons"]
for w in w_pro:
if not(issubclass(w, weapons.SimpleWeapon)):
prof_dict["Weapons"] += [w.name]
else:
prof_dict["Weapons"] = [w.name for w in w_pro]
if "Weapons" in prof_dict.keys():
prof_dict["Weapons"] = ", ".join(prof_dict["Weapons"]) + "."
armor_types = ["all armor", "light armor", "medium armor",
"heavy armor"]
prof_set = set([prof.lower().strip().strip('.')
for prof in self.proficiencies_text.split(',')])
prof_dict["Armor"] = [ar for ar in armor_types if ar in prof_set]
if len(prof_dict["Armor"]) > 2 or 'all armor' in prof_set:
prof_dict["Armor"] = ["All armor"]
if 'shields' in prof_set:
prof_dict["Armor"] += ["shields"]
prof_dict["Armor"] = ", ".join(prof_dict["Armor"]) + "."
if hasattr(self, 'chosen_tools'):
prof_dict["Other"] = self.chosen_tools
return prof_dict
@property
def spell_casting_info(self):
"""Returns a ready-to-use dictionary for spellsheets."""
level_names = ["Cantrip",
'FirstLevelSpell',
'SecondLevelSpell',
'ThirdLevelSpell',
'FourthLevelSpell',
'FifthLevelSpell',
'SixthLevelSpell',
'SeventhLevelSpell',
'EighthLevelSpell',
'NinthLevelSpell']
spell_info = {'head':{
"classes_and_levels": " / ".join(
[c.name + " " + str(c.level) for c in self.spellcasting_classes]
),
"abilities": " / ".join(
[c.spellcasting_ability.upper()[:3]
for c in self.spellcasting_classes]
),
"DCs": " / ".join(
[str(self.spell_save_dc(c))
for c in self.spellcasting_classes]
),
"bonuses": " / ".join(
["{:+d}".format(self.spell_attack_bonus(c))
for c in self.spellcasting_classes]
),
}}
slots = {level_names[k]:self.spell_slots(k) for k in range(1, 10)
if self.spell_slots(k) > 0}
spell_info["slots"] = slots
spell_list = {}
for s in self.spells:
prepared = s in self.spells_prepared
level_info = level_names[s.level]
info_there = spell_list.get(level_info, [])
spell_list[level_info] = info_there + [(s.name, prepared)]
spell_info["list"] = spell_list
return spell_info
@property
def magic_items_text(self):
+67 -1
View File
@@ -49,6 +49,7 @@ def create_latex_pdf(
basename: str,
keep_temp_files: bool = False,
use_dnd_decorations: bool = False,
comm1: str = "pdflatex"
):
# Create tex document
tex_file = f"{basename}.tex"
@@ -59,7 +60,7 @@ def create_latex_pdf(
pdf_file = Path(f"{basename}.pdf")
output_dir = pdf_file.resolve().parent
tex_command_line = [
"pdflatex",
comm1,
"--output-directory",
str(output_dir),
"-halt-on-error",
@@ -200,3 +201,68 @@ def rst_to_latex(rst, top_heading_level=0):
tex_parts = latex_parts(rst)
tex = tex_parts["body"]
return tex
def rst_to_boxlatex(rst):
"""Adapted rst translation from dungeonsheets latex module, removing
dice parsing and indentation."""
if rst is None:
return ""
tex_parts = latex_parts(rst)
tex = tex_parts["body"]
tex = tex.replace('\n\n', ' \\\\\n')
return tex
def msavage_spell_info(char):
"""Generates the spellsheet for msavage template."""
headinfo = char.spell_casting_info["head"]
sc_classes = r"\SpellcastingClass{" \
+ headinfo["classes_and_levels"] \
+ "}"
sc_abilities = r"\SpellcastingAbility{" \
+ headinfo["abilities"] \
+ "}"
sc_savedc = r"\SpellSaveDC{" \
+ headinfo["DCs"] \
+ "}"
sc_atk = r"\SpellAttackBonus{" \
+ headinfo["bonuses"] \
+ "}"
tex1 = "\n".join([sc_classes, sc_abilities, sc_savedc, sc_atk]) + "\n"
spellslots = char.spell_casting_info["slots"]
texT = []
for k, v in spellslots.items():
texT.append("\\" + k + "SlotsTotal{" + str(v) + "}")
tex2 = "\n".join(texT) + "\n"
texT = []
level_names = level_names = ["Cantrip",
'FirstLevelSpell',
'SecondLevelSpell',
'ThirdLevelSpell',
'FourthLevelSpell',
'FifthLevelSpell',
'SixthLevelSpell',
'SeventhLevelSpell',
'EighthLevelSpell',
'NinthLevelSpell']
sheet_spaces = dict(zip(level_names,
[8, 13, 13, 13, 13, 9, 9, 9, 7, 7]))
comp_letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
"L", "M"]
spellList = char.spell_casting_info["list"]
for k, v in spellList.items():
slots_max = sheet_spaces[k]
if len(v) > slots_max:
vsel = sorted(v, key=lambda x: x[1], reverse=True)
else:
vsel = v[:]
for spinfo, slot in zip(vsel[:slots_max], comp_letters):
slot_command = "\\"+k+'Slot'+slot
slot_command_name = slot_command+"{"+spinfo[0]+"}"
if k == "Cantrip":
texT = texT + [slot_command_name]
continue
slot_command_prep = slot_command+"Prepared"+"{"+str(spinfo[1])+"}"
texT = texT + [slot_command_name, slot_command_prep]
tex3 = "\n".join(texT) + '\n'
return "\n".join([tex1, tex2, tex3])
+44 -11
View File
@@ -55,7 +55,8 @@ jinja_env = forms.jinja_environment()
jinja_env.filters["rst_to_latex"] = latex.rst_to_latex
jinja_env.filters["rst_to_html"] = epub.rst_to_html
jinja_env.filters["to_heading_id"] = epub.to_heading_id
jinja_env.filters["boxed"] = latex.rst_to_boxlatex
jinja_env.filters["spellsheetparser"] = latex.msavage_spell_info
# Custom types
File = Union[Path, str]
@@ -137,6 +138,7 @@ def make_sheet(
output_format: str = "pdf",
fancy_decorations: bool = False,
debug: bool = False,
use_tex_template: bool = False,
):
"""Make a character or GM sheet into a PDF.
Parameters
@@ -173,6 +175,7 @@ def make_sheet(
output_format=output_format,
fancy_decorations=fancy_decorations,
debug=debug,
use_tex_template=use_tex_template
)
return ret
@@ -406,6 +409,27 @@ def make_character_content(
)
return content
def msavage_sheet(character, basename, portrait_file="", debug=False):
"""Another adaption. All changes can be easily included as options
in the orignal functions, though."""
# Load image file if present
portrait_command=""
if character.portrait and portrait_file:
portrait_command = r"\includegraphics[width=5.75cm]{"+ \
portrait_file + "}"
tex = jinja_env.get_template("MSavage_template.tex").render(
char=character, portrait=portrait_command
)
latex.create_latex_pdf(
tex,
basename=basename,
keep_temp_files=debug,
use_dnd_decorations=True,
comm1="xelatex"
)
def make_character_sheet(
char_file: Union[str, Path],
@@ -414,6 +438,7 @@ def make_character_sheet(
output_format: str = "pdf",
fancy_decorations: bool = False,
debug: bool = False,
use_tex_template: bool = False,
):
"""Prepare a PDF character sheet from the given character file.
@@ -448,7 +473,7 @@ def make_character_sheet(
basename = char_file.stem
char_base = basename + "_char"
person_base = basename + "_person"
sheets = [char_base + ".pdf", person_base + ".pdf"]
sheets = [char_base + ".pdf"]
pages = []
# Prepare the tex/html content
content_suffix = format_suffixes[output_format]
@@ -458,16 +483,24 @@ def make_character_sheet(
fancy_decorations=fancy_decorations)
# Typeset combined LaTeX file
if output_format == "pdf":
if use_tex_template:
msavage_sheet(
character=character, basename=char_base,
portrait_file=portrait_file, debug=debug
)
# Fillable PDF forms
char_pdf = create_character_pdf_template(
else:
sheets.append(person_base + ".pdf")
char_pdf = create_character_pdf_template(
character=character, basename=char_base, flatten=flatten
)
pages.append(char_pdf)
person_pdf = create_personality_pdf_template(
character=character, basename=person_base, portrait_file=portrait_file, flatten=flatten
)
pages.append(person_pdf)
if character.is_spellcaster:
)
pages.append(char_pdf)
person_pdf = create_personality_pdf_template(
character=character, basename=person_base,
portrait_file=portrait_file, flatten=flatten
)
pages.append(person_pdf)
if character.is_spellcaster and not(use_tex_template):
# Create spell sheet
spell_base = "{:s}_spells".format(basename)
create_spells_pdf_template(
@@ -486,7 +519,7 @@ def make_character_sheet(
)
sheets.append(features_base + ".pdf")
final_pdf = f"{basename}.pdf"
merge_pdfs(sheets, final_pdf, clean_up=True)
merge_pdfs(sheets, final_pdf, clean_up=not(debug))
except exceptions.LatexNotFoundError:
log.warning(
f"``pdflatex`` not available. Skipping features for {character.name}"