diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5395019c3..0744a53fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,7 +90,7 @@ See [docs/contributing/release-process.md](./docs/contributing/release-process.m - `src/main/java/com/demcha/compose/document/layout` Canonical functional layout pipeline: `LayoutCompiler`, `BuiltInNodeDefinitions`, `TableLayoutSupport`, `PreparedNode`, `PlacedFragment` - `src/main/java/com/demcha/compose/document/backend/fixed/pdf` - PDF backend: `PdfFixedLayoutBackend`, fragment handlers, and the option translators that bridge canonical types to PDFBox + PDF backend: `PdfFixedLayoutBackend`, fragment handlers, the option translators that bridge canonical types to PDFBox, and `FontShowcase` (bundled-font preview renderer) - `src/main/java/com/demcha/compose/document/backend/semantic` Semantic exporters: `DocxSemanticBackend` (Apache POI based), `PptxSemanticBackend` (manifest skeleton) - `src/main/java/com/demcha/compose/document/templates/*` @@ -98,7 +98,7 @@ See [docs/contributing/release-process.md](./docs/contributing/release-process.m - `src/main/java/com/demcha/compose/engine/*` Internal shared engine foundation under the canonical surface (measure, paginate, place, render). Not part of the recommended public API - `src/main/java/com/demcha/compose/font` - Public font registry, `FontName`, default fonts, `FontShowcase` + Public font registry, `FontName`, default fonts - `src/test/java/com/demcha/documentation/*` Examples used to keep README/documentation snippets honest - `src/test/java/com/demcha/compose/engine/integration/*` diff --git a/src/main/java/com/demcha/compose/GraphCompose.java b/src/main/java/com/demcha/compose/GraphCompose.java index c9f877f8c..d27bce5b1 100644 --- a/src/main/java/com/demcha/compose/GraphCompose.java +++ b/src/main/java/com/demcha/compose/GraphCompose.java @@ -2,7 +2,7 @@ import com.demcha.compose.font.FontFamilyDefinition; import com.demcha.compose.font.FontName; -import com.demcha.compose.font.FontShowcase; +import com.demcha.compose.document.backend.fixed.pdf.FontShowcase; import com.demcha.compose.font.DefaultFonts; import com.demcha.compose.document.api.DocumentPageSize; import com.demcha.compose.document.api.DocumentSession; diff --git a/src/main/java/com/demcha/compose/font/FontShowcase.java b/src/main/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcase.java similarity index 80% rename from src/main/java/com/demcha/compose/font/FontShowcase.java rename to src/main/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcase.java index 65b5f661f..8164ad9ad 100644 --- a/src/main/java/com/demcha/compose/font/FontShowcase.java +++ b/src/main/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcase.java @@ -1,4 +1,4 @@ -package com.demcha.compose.font; +package com.demcha.compose.document.backend.fixed.pdf; import com.demcha.compose.GraphCompose; import com.demcha.compose.document.api.DocumentPageSize; @@ -9,6 +9,8 @@ import com.demcha.compose.document.style.DocumentInsets; import com.demcha.compose.document.style.DocumentTextDecoration; import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.font.DefaultFonts; +import com.demcha.compose.font.FontName; import java.awt.Color; import java.nio.file.Path; @@ -28,6 +30,12 @@ public final class FontShowcase { private FontShowcase() { } + /** + * Renders a preview of every bundled font family to a PDF file on disk. + * + * @param outputFile destination path for the generated preview PDF + * @throws Exception if the document cannot be built or written + */ public static void renderAvailableFontsPreview(Path outputFile) throws Exception { try (DocumentSession document = GraphCompose.document(outputFile) .pageSize(DocumentPageSize.A4) @@ -40,6 +48,12 @@ public static void renderAvailableFontsPreview(Path outputFile) throws Exception } } + /** + * Renders a preview of every bundled font family and returns the PDF bytes. + * + * @return the generated preview document as PDF bytes + * @throws Exception if the document cannot be built or rendered + */ public static byte[] renderAvailableFontsPreview() throws Exception { try (DocumentSession document = GraphCompose.document() .pageSize(DocumentPageSize.A4) @@ -52,6 +66,14 @@ public static byte[] renderAvailableFontsPreview() throws Exception { } } + /** + * Renders a preview of the given font families and returns the PDF bytes. + * + * @param fonts logical font names to include; when {@code null} the full + * bundled catalog is used + * @return the generated preview document as PDF bytes + * @throws Exception if the document cannot be built or rendered + */ public static byte[] renderFontsPreview(Collection fonts) throws Exception { try (DocumentSession document = GraphCompose.document() .pageSize(DocumentPageSize.A4) @@ -64,6 +86,13 @@ public static byte[] renderFontsPreview(Collection fonts) throws Excep } } + /** + * Builds the font-preview content into an existing document session. + * + * @param document open document session to populate; must not be {@code null} + * @param fonts logical font names to showcase; when {@code null} the full + * bundled catalog is used + */ public static void buildShowcase(DocumentSession document, Collection fonts) { Objects.requireNonNull(document, "document").pageFlow(flow -> { flow.name("AvailableFontsPreview") diff --git a/src/main/java/com/demcha/compose/font/DefaultFonts.java b/src/main/java/com/demcha/compose/font/DefaultFonts.java index 9a8dda790..66223c07f 100644 --- a/src/main/java/com/demcha/compose/font/DefaultFonts.java +++ b/src/main/java/com/demcha/compose/font/DefaultFonts.java @@ -1,10 +1,8 @@ package com.demcha.compose.font; import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; -import org.apache.pdfbox.pdmodel.PDDocument; import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -76,27 +74,6 @@ public static FontLibrary standardLibrary() { return PdfFontLibraryFactory.standardLibrary(); } - /** - * Creates a PDF-backed font library containing bundled families. - * - * @param document PDF document that owns loaded fonts - * @return font library - */ - public static FontLibrary library(PDDocument document) { - return PdfFontLibraryFactory.library(document); - } - - /** - * Creates a PDF-backed font library containing bundled and custom families. - * - * @param document PDF document that owns loaded fonts - * @param customFamilies document-local custom font families - * @return font library - */ - public static FontLibrary library(PDDocument document, Collection customFamilies) { - return PdfFontLibraryFactory.library(document, customFamilies); - } - /** * Returns bundled font family definitions. * diff --git a/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java b/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java index cbfe6894f..f273924a7 100644 --- a/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java +++ b/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java @@ -4,7 +4,7 @@ import com.demcha.compose.document.backend.fixed.pdf.options.PdfHeaderFooterOptions; import com.demcha.compose.document.backend.fixed.pdf.options.PdfMetadataOptions; import com.demcha.compose.document.backend.fixed.pdf.options.PdfWatermarkOptions; -import com.demcha.compose.font.DefaultFonts; +import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; import com.demcha.compose.font.FontLibrary; import com.demcha.compose.engine.components.content.table.TableCellContent; import com.demcha.compose.engine.components.content.table.TableCellLayoutStyle; @@ -1097,7 +1097,7 @@ private CountingPrepareContext(NodeRegistry registry, PDRectangle pageSize, Marg this.registry = registry; this.canvas = LayoutCanvas.from(pageSize.getWidth(), pageSize.getHeight(), margin); this.measurementDocument = new PDDocument(); - this.fonts = DefaultFonts.library(measurementDocument); + this.fonts = PdfFontLibraryFactory.library(measurementDocument); this.textMeasurement = new CountingTextMeasurementSystem( new FontLibraryTextMeasurementSystem(fonts, PdfFont.class)); } diff --git a/src/test/java/com/demcha/compose/font/FontShowcaseLayoutSnapshotTest.java b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseLayoutSnapshotTest.java similarity index 93% rename from src/test/java/com/demcha/compose/font/FontShowcaseLayoutSnapshotTest.java rename to src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseLayoutSnapshotTest.java index 502559821..6cfafc037 100644 --- a/src/test/java/com/demcha/compose/font/FontShowcaseLayoutSnapshotTest.java +++ b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseLayoutSnapshotTest.java @@ -1,4 +1,4 @@ -package com.demcha.compose.font; +package com.demcha.compose.document.backend.fixed.pdf; import com.demcha.compose.GraphCompose; import com.demcha.compose.document.api.DocumentPageSize; diff --git a/src/test/java/com/demcha/compose/font/FontShowcaseRenderTest.java b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseRenderTest.java similarity index 91% rename from src/test/java/com/demcha/compose/font/FontShowcaseRenderTest.java rename to src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseRenderTest.java index eb273220e..2316fa154 100644 --- a/src/test/java/com/demcha/compose/font/FontShowcaseRenderTest.java +++ b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/FontShowcaseRenderTest.java @@ -1,6 +1,7 @@ -package com.demcha.compose.font; +package com.demcha.compose.document.backend.fixed.pdf; import com.demcha.compose.GraphCompose; +import com.demcha.compose.font.FontName; import com.demcha.testing.VisualTestOutputs; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; diff --git a/src/test/java/com/demcha/compose/engine/integration/BlockTextIntegrationTest.java b/src/test/java/com/demcha/compose/engine/integration/BlockTextIntegrationTest.java index 1eee72876..ce3a7e9fe 100644 --- a/src/test/java/com/demcha/compose/engine/integration/BlockTextIntegrationTest.java +++ b/src/test/java/com/demcha/compose/engine/integration/BlockTextIntegrationTest.java @@ -1,7 +1,7 @@ package com.demcha.compose.engine.integration; import com.demcha.compose.GraphCompose; -import com.demcha.compose.font.DefaultFonts; +import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; import com.demcha.compose.testsupport.engine.assembly.ComponentBuilder; import com.demcha.compose.engine.components.content.text.TextIndentStrategy; import com.demcha.compose.engine.components.content.text.BlockTextData; @@ -167,7 +167,7 @@ void shouldIncreaseVerticalGapAroundMarkdownHeading() throws Exception { double baseStep; try (PDDocument fontDocument = new PDDocument()) { - PdfFont font = (PdfFont) DefaultFonts.library(fontDocument) + PdfFont font = (PdfFont) PdfFontLibraryFactory.library(fontDocument) .getFont(style.fontName(), PdfFont.class) .orElseThrow(); baseStep = font.getLineHeight(style) + 2.0; @@ -206,7 +206,7 @@ void shouldRespectOwnPaddingWhenPositioningBlockTextLines() throws Exception { double baselineOffsetFromBottom; try (PDDocument fontDocument = new PDDocument()) { - PdfFont font = (PdfFont) DefaultFonts.library(fontDocument) + PdfFont font = (PdfFont) PdfFontLibraryFactory.library(fontDocument) .getFont(style.fontName(), PdfFont.class) .orElseThrow(); baselineOffsetFromBottom = font.verticalMetrics(style).baselineOffsetFromBottom(); diff --git a/src/test/java/com/demcha/compose/engine/integration/SingleLineTextIntegrationTest.java b/src/test/java/com/demcha/compose/engine/integration/SingleLineTextIntegrationTest.java index b5aee5dd0..eae373195 100644 --- a/src/test/java/com/demcha/compose/engine/integration/SingleLineTextIntegrationTest.java +++ b/src/test/java/com/demcha/compose/engine/integration/SingleLineTextIntegrationTest.java @@ -1,7 +1,7 @@ package com.demcha.compose.engine.integration; import com.demcha.compose.GraphCompose; -import com.demcha.compose.font.DefaultFonts; +import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; import com.demcha.compose.engine.components.content.text.TextStyle; import com.demcha.compose.engine.components.core.Entity; import com.demcha.compose.engine.components.layout.Anchor; @@ -48,7 +48,7 @@ void shouldRenderAutosizedSingleLineTextAtPaddingAdjustedBaseline() throws Excep Placement placement = textEntity.getComponent(Placement.class).orElseThrow(); PdfFont.VerticalMetrics metrics; try (PDDocument fontDocument = new PDDocument()) { - PdfFont pdfFont = (PdfFont) DefaultFonts.library(fontDocument) + PdfFont pdfFont = (PdfFont) PdfFontLibraryFactory.library(fontDocument) .getFont(style.fontName(), PdfFont.class) .orElseThrow(); metrics = pdfFont.verticalMetrics(style); diff --git a/src/test/java/com/demcha/compose/font/FontLibraryIntegrationTest.java b/src/test/java/com/demcha/compose/font/FontLibraryIntegrationTest.java index 6e6d4b759..e81b4664a 100644 --- a/src/test/java/com/demcha/compose/font/FontLibraryIntegrationTest.java +++ b/src/test/java/com/demcha/compose/font/FontLibraryIntegrationTest.java @@ -1,6 +1,7 @@ package com.demcha.compose.font; import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; import com.demcha.compose.testsupport.EngineComposerHarness; import com.demcha.compose.engine.render.pdf.PdfFont; import com.demcha.compose.engine.render.word.WordFont; @@ -24,7 +25,7 @@ void shouldExposeBundledGoogleFontsInEngineComposerHarness() throws Exception { } try (PDDocument document = new PDDocument()) { - FontLibrary fonts = DefaultFonts.library(document); + FontLibrary fonts = PdfFontLibraryFactory.library(document); assertThat(fonts.getFont(FontName.LATO, PdfFont.class)).isPresent(); assertThat(fonts.getFont(FontName.LATO, WordFont.class)).isPresent(); assertThat(fonts.getFont(FontName.KANIT, PdfFont.class)).isPresent(); @@ -61,7 +62,7 @@ void shouldRegisterCustomFontFamilyFromFilePaths() throws Exception { .build(); try (PDDocument document = new PDDocument()) { - FontLibrary fonts = DefaultFonts.library(document, List.of(customDefinition)); + FontLibrary fonts = PdfFontLibraryFactory.library(document, List.of(customDefinition)); assertThat(fonts.getFont(customFamily, PdfFont.class)).isPresent(); assertThat(fonts.getFont(customFamily, WordFont.class)).isPresent(); } diff --git a/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java b/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java index 1d69ccd4e..03acf456a 100644 --- a/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java +++ b/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java @@ -1,6 +1,6 @@ package com.demcha.compose.testsupport; -import com.demcha.compose.font.DefaultFonts; +import com.demcha.compose.document.backend.fixed.pdf.PdfFontLibraryFactory; import com.demcha.compose.font.FontFamilyDefinition; import com.demcha.compose.font.FontName; import com.demcha.compose.testsupport.engine.assembly.ComponentBuilder; @@ -72,7 +72,7 @@ private EngineComposerHarness(Path outputFile, Margin margin, Collection customFontFamilies) { this.doc = new PDDocument(); - this.entityManager = new EntityManager(DefaultFonts.library(doc, customFontFamilies), markdown); + this.entityManager = new EntityManager(PdfFontLibraryFactory.library(doc, customFontFamilies), markdown); this.entityManager.setGuideLines(guideLines); this.componentBuilder = createComponentBuilder(entityManager); this.canvas = new PdfCanvas(pageSize, 0.0f, 0.0f); diff --git a/src/test/java/com/demcha/documentation/PdfBackendIsolationGuardTest.java b/src/test/java/com/demcha/documentation/PdfBackendIsolationGuardTest.java index d69f60ed7..8e4989ab9 100644 --- a/src/test/java/com/demcha/documentation/PdfBackendIsolationGuardTest.java +++ b/src/test/java/com/demcha/documentation/PdfBackendIsolationGuardTest.java @@ -16,9 +16,12 @@ /** * Architectural guard that keeps PDFBox out of the canonical document API, the - * layered template surface, and non-PDF fixed-layout contracts. Templates - * compose against the canonical DSL and must stay backend-neutral, so a preset - * or component that reached for a PDFBox type would fail here. + * layered template surface, non-PDF fixed-layout contracts, and the public font + * catalog. Templates compose against the canonical DSL and must stay + * backend-neutral, so a preset or component that reached for a PDFBox type would + * fail here. The {@code compose.font} package is the logical font catalog — it + * names fonts, it never loads them (PDFBox loading belongs to the pdf backend + * factory), so it is guarded too. */ class PdfBackendIsolationGuardTest { @@ -37,7 +40,12 @@ class PdfBackendIsolationGuardTest { PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/layout"), PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/snapshot"), PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/templates"), - PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/backend/fixed")); + PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/backend/fixed"), + // The public font catalog names logical fonts; it never loads them. + // PDFBox loading lives in the pdf backend factory, so compose.font + // must stay PDFBox-free (FontShowcase, which renders a preview PDF, + // lives under backend/fixed/pdf and is exempted like the rest of it). + PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/font")); @Test void pdfboxShouldStayOutOfCanonicalAndNonPdfBackendContracts() throws IOException {