diff --git a/src/ymprint/cli/config.py b/src/ymprint/cli/config.py index 0c5b8f8..1e8efa4 100644 --- a/src/ymprint/cli/config.py +++ b/src/ymprint/cli/config.py @@ -1,15 +1,13 @@ from pathlib import Path from typing import Optional -CONFIG_FILENAMES = ['doctemplate.yml', 'textstyles.yml', 'tablestyles.yml'] -def locate_config_dir(cwd: Path) -> Optional[Path]: - config_dir = None +def locate_config_file(cwd: Path) -> Optional[Path]: + config_file = None for parent in cwd.parents: - filenames = [path.name for path in parent.glob("*.yml")] - intersection = set(CONFIG_FILENAMES) & set(filenames) - if intersection!= set(): - config_dir = parent - return config_dir + filenames = [path.name for path in parent.glob("*.ymprint.yml")] + if filenames: + config_file = filenames[0] + return config_file diff --git a/src/ymprint/cli/main.py b/src/ymprint/cli/main.py index 737040d..68f2da3 100644 --- a/src/ymprint/cli/main.py +++ b/src/ymprint/cli/main.py @@ -10,7 +10,7 @@ from typer import Typer from ..report_reader import load_report -from .config import locate_config_dir, CONFIG_FILENAMES +from .config import locate_config_file from .okular import ensure_okular from ..config.config_loaders import load_config_directory @@ -63,7 +63,7 @@ def convert( # Identify config files and content files to watch here # ensure_demo_file() if config_dir is None: - config_dir = locate_config_dir(Path.cwd()) + config_dir = locate_config_file(Path.cwd()) load_report(source, destination, config_dir) console = Console() @@ -81,7 +81,7 @@ def convert( def live( src: Annotated[str, "YAML file path to render to PDF"], dest: Annotated[Optional[str], "File path of output PDF file. If not provided file name and path of source file will be used (wtih .pdf extension)."] = None, - config_dir: Annotated[Optional[str], "Directory of optional config files (doctemplate.yml, textstyles.yml, tablestyles.yml)"] = None, + config_file: Annotated[Optional[str], "Location of optional document config *.ymprint.yml file"] = None, ): source = Path(src) if dest is None: @@ -90,8 +90,8 @@ def live( destination = Path(dest) # Identify config files and content files to watch here # ensure_demo_file() - if config_dir is None: - config_dir = locate_config_dir(Path.cwd()) + if config_file is None: + config_dir = locate_config_file(Path.cwd()) file_watchers = [FileWatcher(Path(source))] if config_dir is not None: diff --git a/src/ymprint/config/config_loaders.py b/src/ymprint/config/config_loaders.py index efa1f46..70ce385 100644 --- a/src/ymprint/config/config_loaders.py +++ b/src/ymprint/config/config_loaders.py @@ -98,7 +98,10 @@ def build_current_config(default_config: dict, config_data: DeepChainMap): for key in default_config: value = config_data[key] if isinstance(value, DeepChainMap): - value = build_current_config(default_config[key], value) + default_value = default_config[key] + # This condition only exists for the background key which is None in the defaults + if default_value is not None: + value = build_current_config(default_config[key], value) style_map.update({key: value}) return style_map diff --git a/src/ymprint/config/doctemplate.py b/src/ymprint/config/doctemplate.py index a7ed2dd..fb27b41 100644 --- a/src/ymprint/config/doctemplate.py +++ b/src/ymprint/config/doctemplate.py @@ -1,3 +1,4 @@ +from enum import StrEnum import pathlib from typing import Optional from pydantic import BaseModel, Field, ConfigDict @@ -12,10 +13,18 @@ class Margins(BaseModel): right: float bottom: float +class RelativeTo(StrEnum): + CONFIG = 'config' + SOURCE = 'source' + +class PDFBackground(BaseModel): + filepath: str + relative_to: Optional[RelativeTo] = Field(alias='relative-to', default=None) + class PageConfig(BaseModel): # model_config = ConfigDict(populate_by_name=True) margins: Margins - background: Optional[str] = None + background: Optional[PDFBackground] = None class PageSizeMixin: page_size: str = Field(alias='page-size') diff --git a/src/ymprint/config/pdf_postprocessing.py b/src/ymprint/config/pdf_postprocessing.py index e43af96..94137bb 100644 --- a/src/ymprint/config/pdf_postprocessing.py +++ b/src/ymprint/config/pdf_postprocessing.py @@ -109,15 +109,30 @@ def fill_forms_and_bake(vars: dict, pdf_backgrounds: dict[str, io.BytesIO | None def load_pdf_backgrounds(context: dict) -> dict[str, io.BytesIO | None]: source_path = pathlib.Path(context['source_path']) source_parent = source_path.parent - first_page = context['doctemplate']['yaml']['_doc'].get('first-page') - if isinstance(first_page, dict): - first_page_background = first_page.get("background") - if first_page_background is not None: - first_page_background = source_parent / first_page_background + print(f"{source_parent=}") + + if context['config_path'] is not None: + config_path = pathlib.Path(context['config_path']) + config_parent = config_path.parent + else: + config_path = source_path + config_parent = source_parent + + first_page_bg = context['doctemplate']['yaml']['_doc'].get('first-page', {}).get('background') + if isinstance(first_page_bg, dict): + print(f"{first_page.get('background', {})=}") + first_page_background_filepath = first_page_bg.get('filepath') + relative_to = first_page_bg.get('relative_to') + if relative_to == 'source': + first_page_background = source_parent / first_page_background_filepath + elif relative_to == 'config': + first_page_background = config_parent / first_page_background_filepath + else: + first_page_background = first_page_background_filepath else: first_page_background = None - remaining = context['doctemplate']['yaml']['_doc'].get('background') + remaining = context['doctemplate']['yaml']['_doc'].get('background', {}) first_page_pdf = remaining_pdf = None first_page_data = remaining_page_data = None if first_page_background is not None: @@ -126,7 +141,16 @@ def load_pdf_backgrounds(context: dict) -> dict[str, io.BytesIO | None]: first_page_pdf.save(first_page_data) first_page_data.seek(0) if remaining is not None: - remaining_page_background = source_parent / remaining + relative_to = remaining.get('relative-to') + print(f"{relative_to=}") + if relative_to == 'source': + remaining_page_background = source_parent / remaining.get('filepath') + elif relative_to == 'config': + remaining_page_background = config_parent / remaining.get('filepath') + else: + remaining_page_background = remaining.get('filepath') + + print(f"{remaining_page_background=}") remaining_pdf = mu.open(remaining_page_background) remaining_page_data = io.BytesIO() remaining_pdf.save(remaining_page_data) diff --git a/src/ymprint/context_builder.py b/src/ymprint/context_builder.py index be79c6e..1c459f7 100644 --- a/src/ymprint/context_builder.py +++ b/src/ymprint/context_builder.py @@ -10,7 +10,8 @@ def build_context( tablestyles_yaml: dict, document_vars: dict, source_path: str | pathlib.Path, - destination_path: str | pathlib.Path + destination_path: str | pathlib.Path, + config_path: str | pathlib.Path ) -> dict: # inline_styles = {} if "_style" not in content_yaml else content_yaml.pop("_style") report_styles = ReportStyles.model_validate(text_styles_yaml['_style']) @@ -65,6 +66,7 @@ def build_context( } }, "source_path": source_path, + "config_path": config_path, "destination_path": destination_path, } return context \ No newline at end of file diff --git a/src/ymprint/report_reader.py b/src/ymprint/report_reader.py index 2713adc..919a6de 100644 --- a/src/ymprint/report_reader.py +++ b/src/ymprint/report_reader.py @@ -45,8 +45,6 @@ def load_report(source_yaml: str | pathlib.Path, destination_pdf: str | pathlib. textstyles, tablestyles, doc_data = load_report_config(source_data, report_config_path) doctemplate = DocConfig.model_validate(doc_data['_doc']) document_vars = extract_vars(source_data) - - context = build_context( source_data, textstyles, @@ -54,8 +52,10 @@ def load_report(source_yaml: str | pathlib.Path, destination_pdf: str | pathlib. tablestyles, document_vars, source_yaml, - destination_pdf + destination_pdf, + report_config_path, ) + print(f"{doc_data=}") story = build_story(source_data, context) if context['doctemplate']['yaml']['_doc'].get('first-page') is not None: story = [NextPageTemplate(1)] + story @@ -74,10 +74,6 @@ def load_report(source_yaml: str | pathlib.Path, destination_pdf: str | pathlib. ) - - - - def extract_vars(source_data: dict) -> dict: if "_vars" in source_data: return source_data.pop("_vars") diff --git a/test.pdf b/test.pdf new file mode 100644 index 0000000..807ea06 Binary files /dev/null and b/test.pdf differ diff --git a/test.yml b/test.yml new file mode 100644 index 0000000..3b57c01 --- /dev/null +++ b/test.yml @@ -0,0 +1,19 @@ +_doc: + margins: + top: 1.0 + left: 1.0 + right: 1.0 + bottom: 1.0 + first-page: + margins: + top: 1.0 + left: 1.0 + right: 1.0 + bottom: 1.0 +yep: > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. diff --git a/tests/test-data/example_1_config/textstyles.yml b/tests/test-data/example_1_config/config.ymprint.yml similarity index 66% rename from tests/test-data/example_1_config/textstyles.yml rename to tests/test-data/example_1_config/config.ymprint.yml index a37e962..3925ddc 100644 --- a/tests/test-data/example_1_config/textstyles.yml +++ b/tests/test-data/example_1_config/config.ymprint.yml @@ -1,3 +1,12 @@ +_doc: + page-size: a4 + landscape: false + margins: + top: 84 + left: 84 + right: 84 + bottom: 84 + _style: headings: font: Helvetica diff --git a/tests/test-data/example_1_config/doctemplate.yml b/tests/test-data/example_1_config/doctemplate.yml deleted file mode 100644 index 81b30f7..0000000 --- a/tests/test-data/example_1_config/doctemplate.yml +++ /dev/null @@ -1,8 +0,0 @@ -_doc: - page-size: a4 - landscape: false - margins: - top: 84 - left: 84 - right: 84 - bottom: 84 \ No newline at end of file diff --git a/tests/test-data/example_2_config/config.ymprint.yml b/tests/test-data/example_2_config/config.ymprint.yml new file mode 100644 index 0000000..d000eaa --- /dev/null +++ b/tests/test-data/example_2_config/config.ymprint.yml @@ -0,0 +1,69 @@ +_doc: + page-size: letter + landscape: true + margins: + top: 84 + left: 84 + right: 84 + bottom: 84 + background: + filepath: background_other_pages.pdf + relative-to: source + first-page: + margins: + top: 240 + left: 84 + right: 84 + bottom: 84 + background: + filepath: background_first_page.pdf + relative-to: source + +_tablestyle: + cell-padding: + top: 5 + left: 5 + right: 5 + bottom: 5 + + headers: + text: + font: Helvetica + size: 11 + color: "#000000" + row: + color: "#ffffff" + lines: + - above + - below + - between + body: + text: + font: "Helvetica" + size: 11 + color: "black" + rows: + color: + even: "#ffffff" + odd: "#eeeeee" + lines: + - above + - below + - between + + +_style: + headings: + font: Helvetica + color: "#111111" + ratio: minor second + body: + # font: Times + color: black + size: 10 # pts + bullets: + font: Helvetica + size: 12 + color: black + symbol: "-" + spacing: 1.1 diff --git a/tests/test-data/example_2_config/doctemplate.yml b/tests/test-data/example_2_config/doctemplate.yml deleted file mode 100644 index 1827a6c..0000000 --- a/tests/test-data/example_2_config/doctemplate.yml +++ /dev/null @@ -1,16 +0,0 @@ -_doc: - page-size: letter - landscape: true - margins: - top: 84 - left: 84 - right: 84 - bottom: 84 - background: background_other_pages.pdf - first-page: - margins: - top: 240 - left: 84 - right: 84 - bottom: 84 - background: background_first_page.pdf \ No newline at end of file diff --git a/tests/test-data/example_2_config/tablestyles.yml b/tests/test-data/example_2_config/tablestyles.yml deleted file mode 100644 index 57fc9f1..0000000 --- a/tests/test-data/example_2_config/tablestyles.yml +++ /dev/null @@ -1,34 +0,0 @@ -_tablestyle: - cell-padding: - top: 5 - left: 5 - right: 5 - bottom: 5 - - headers: - text: - font: Helvetica - size: 11 - color: "#000000" - row: - color: "#ffffff" - lines: - - above - - below - - between - body: - text: - font: "Helvetica" - size: 11 - color: "black" - rows: - color: - even: "#ffffff" - odd: "#eeeeee" - lines: - - above - - below - - between - - - \ No newline at end of file diff --git a/tests/test-data/example_2_config/textstyles.yml b/tests/test-data/example_2_config/textstyles.yml deleted file mode 100644 index 957e43c..0000000 --- a/tests/test-data/example_2_config/textstyles.yml +++ /dev/null @@ -1,15 +0,0 @@ -_style: - headings: - font: Helvetica - color: "#111111" - ratio: minor second - body: - # font: Times - color: black - size: 10 # pts - bullets: - font: Helvetica - size: 12 - color: black - symbol: "-" - spacing: 1.1 diff --git a/tests/test-data/example_output1.pdf b/tests/test-data/example_output1.pdf index 99296fe..94418d2 100644 Binary files a/tests/test-data/example_output1.pdf and b/tests/test-data/example_output1.pdf differ diff --git a/tests/test-data/example_output2.pdf b/tests/test-data/example_output2.pdf index 0e59709..0af00b7 100644 Binary files a/tests/test-data/example_output2.pdf and b/tests/test-data/example_output2.pdf differ diff --git a/tests/test-data/example_output3.pdf b/tests/test-data/example_output3.pdf index aecd515..6aeaefc 100644 Binary files a/tests/test-data/example_output3.pdf and b/tests/test-data/example_output3.pdf differ diff --git a/tests/test-data/filled_forms.pdf b/tests/test-data/filled_forms.pdf index fe7516d..fe5d2eb 100644 Binary files a/tests/test-data/filled_forms.pdf and b/tests/test-data/filled_forms.pdf differ diff --git a/tests/test-data/report_example_2.yml b/tests/test-data/report_example_2.yml index 7d148b9..57af91a 100644 --- a/tests/test-data/report_example_2.yml +++ b/tests/test-data/report_example_2.yml @@ -7,7 +7,9 @@ _vars: time: 13:00 region: Level 1 columns _doc: # TODO: this background is not loading - background: background_other_pages.pdf + background: + filepath: background_other_pages.pdf + relative-to: source title: first topic: - > diff --git a/tests/test_content_converters.py b/tests/test_content_converters.py index 0dc00e4..85d5339 100644 --- a/tests/test_content_converters.py +++ b/tests/test_content_converters.py @@ -25,6 +25,7 @@ def default_context(default_config): tablestyles, {}, pathlib.Path.cwd(), + pathlib.Path.cwd(), pathlib.Path.cwd() ) return context @@ -32,7 +33,7 @@ def default_context(default_config): def test_convert_paragraph(report_ex1, default_context): data = report_ex1 assert con.convert_paragraph(data['title']['first topic'], default_context) - assert con.convert_paragraph(data['title']['fourth topic']['first sub topic'], default_context) + assert con.convert_paragraph(data['title']['An actual topic']['first sub topic'], default_context) def test_convert_ul(report_ex1, default_context): data = report_ex1 diff --git a/tests/test_context_builder.py b/tests/test_context_builder.py index 66c32e2..09f578c 100644 --- a/tests/test_context_builder.py +++ b/tests/test_context_builder.py @@ -16,6 +16,7 @@ def test_context_builder_executes(default_config): tablestyles, {}, pathlib.Path.cwd(), + pathlib.Path.cwd(), pathlib.Path.cwd() ) assert context['styles']['rl']['_style']['body'] diff --git a/tests/test_loadconfig.py b/tests/test_loadconfig.py index 1749091..c03756a 100644 --- a/tests/test_loadconfig.py +++ b/tests/test_loadconfig.py @@ -30,10 +30,10 @@ def test_load_report_config(): assert styles['_style']['body']['size'] == 10 # from config dir # assert styles['_style']['body']['spacing'] == 1.5 # from default config - assert tablestyles['_tablestyle']['cell-padding']['top'] == 5 # from config dir + assert tablestyles['_tablestyle']['cell-padding']['top'] == 7.5 # from config dir assert tablestyles['_tablestyle']['headers']['text']['bold'] == True # from default config - assert doc['_doc']['page-size'] == 'letter' + assert doc['_doc']['page-size'] == 'a4' # assert doc['_doc']['first-page']['background'] is None # This key is intentionally not included in the model. See next test. diff --git a/tests/test_pdf_postprocessing.py b/tests/test_pdf_postprocessing.py index 1f89110..7f1e742 100644 --- a/tests/test_pdf_postprocessing.py +++ b/tests/test_pdf_postprocessing.py @@ -23,4 +23,3 @@ def test_fill_forms_and_bake(): filled_data = fill_forms_and_bake(vars, {"first": bg_data, "remaining": None}) filled = pymupdf.open(stream=filled_data['first']) filled.save(TEST_DATA / "filled_forms.pdf") - assert False diff --git a/tests/test_report_reader.py b/tests/test_report_reader.py index 8838081..f884c1a 100644 --- a/tests/test_report_reader.py +++ b/tests/test_report_reader.py @@ -29,6 +29,7 @@ def default_context(default_config): tablestyles, {}, pathlib.Path.cwd(), + pathlib.Path.cwd(), pathlib.Path.cwd() ) return context @@ -37,5 +38,3 @@ def test_load_yaml(report_ex1, report_ex2): load_report(TEST_DATA / "report_example_1.yml", TEST_DATA / "example_output1.pdf", TEST_DATA / "example_1_config") load_report(TEST_DATA / "report_example_2.yml", TEST_DATA / "example_output2.pdf", TEST_DATA / "example_2_config") load_report(TEST_DATA / "report_example_3.yml", TEST_DATA / "example_output3.pdf", TEST_DATA / "example_1_config") - - assert False diff --git a/uv.lock b/uv.lock index c681d3b..d51ff82 100644 --- a/uv.lock +++ b/uv.lock @@ -587,7 +587,7 @@ wheels = [ [[package]] name = "ymprint" -version = "0.1.0" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "jinja2" },