diff --git a/.github/workflows/publish-emoji.yml b/.github/workflows/publish-emoji.yml new file mode 100644 index 00000000..f80dcefc --- /dev/null +++ b/.github/workflows/publish-emoji.yml @@ -0,0 +1,67 @@ +name: Publish emoji to Maven Central + +# Publishes the graph-compose-emoji companion artifact to Maven Central, +# independently of the engine. The bundled Noto Emoji glyphs change rarely, so +# they release on their OWN tag line (emoji-v*) — pushing an engine v* tag +# never touches them, and re-publishing emoji does not require an engine +# release. This mirrors publish-fonts.yml but targets emoji/pom.xml. +# +# Tagging: emoji-vX.Y.Z (e.g. emoji-v1.0.0). The emoji artifact carries its +# own independent version (see emoji/pom.xml) — keep it in sync with the tag. +# +# Human prerequisites are identical to publish.yml (GPG key + Central token +# secrets). See docs/contributing/release-process.md for the runbook. + +on: + push: + tags: + - 'emoji-v*' + workflow_dispatch: + inputs: + tag: + description: 'Existing emoji-v*-prefixed tag to (re-)publish' + required: true + type: string + +permissions: + contents: read + +jobs: + publish-emoji: + name: Publish ${{ github.ref_name }} to Maven Central + runs-on: ubuntu-latest + # Only ship plain semver tags (emoji-vX.Y.Z) to Central; pre-release + # suffixes ship nowhere from here. + if: | + github.event_name == 'workflow_dispatch' || + (!contains(github.ref, '-rc') && !contains(github.ref, '-alpha') && !contains(github.ref, '-beta') && !contains(github.ref, '-snapshot')) + env: + JAVA_TOOL_OPTIONS: -Djava.awt.headless=true + + steps: + - name: Check out repository at tag + uses: actions/checkout@v7 + with: + ref: ${{ github.event.inputs.tag || github.ref }} + + - name: Set up Temurin JDK 17 with Central credentials and GPG key + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '17' + cache: maven + server-id: central + server-username: CENTRAL_USERNAME + server-password: CENTRAL_TOKEN + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Publish emoji to Maven Central + # Activates the emoji module's release profile (sources + javadoc + + # gpg sign + central-publishing) and flips gpg.skip=false. Blocks + # until Sonatype's validator confirms validation. + run: ./mvnw -B -ntp -f emoji/pom.xml -P release -Dgpg.skip=false deploy + env: + CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} + CENTRAL_TOKEN: ${{ secrets.CENTRAL_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} diff --git a/bundle/pom.xml b/bundle/pom.xml index 06d5580b..c33c291e 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -67,7 +67,7 @@ 1.0.0 3.2.8 - 0.10.0 + 0.11.0 true diff --git a/docs/recipes.md b/docs/recipes.md index 28a8aa8d..821748f8 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -19,7 +19,7 @@ authoring API; public application code should not import | [Layered page design](recipes/layered-page-design.md) | Choosing between page backgrounds, rows, layer stacks, and canvases | | [Absolute placement](recipes/absolute-placement.md) | `addCanvas` + `position(x, y)` for pixel-precise certificates and badges | | [Tables](recipes/tables.md) | Row span, zebra rows, totals row, repeated header on page break | -| [Rich text](recipes/rich-text.md) | `RichText` mixed-style runs, inline links/images/shapes, checkboxes | +| [Rich text](recipes/rich-text.md) | `RichText` mixed-style runs, inline links/images/shapes, SVG icons, emoji shortcodes, checkboxes | | [Lists](recipes/lists.md) | `addList`, marker customisation, nested lists with per-depth markers | | [Timelines](recipes/timelines.md) | `addTimeline`: markers on a connector rail, geometry and text-style controls | | [Barcodes](recipes/barcodes.md) | QR / Code 128 / EAN / UPC and friends, tinting, quiet zone | diff --git a/docs/recipes/README.md b/docs/recipes/README.md index c1b5e110..ec41e9ca 100644 --- a/docs/recipes/README.md +++ b/docs/recipes/README.md @@ -8,7 +8,7 @@ API, with copy-pasteable snippets verified against the current release. | Recipe | Covers | |---|---| | [charts.md](charts.md) | Native vector bar / line / area / pie-donut charts: data–spec–style layers, axis & grid toggles, point markers, value-label halos, legend placement, translucent area fills | -| [rich-text.md](rich-text.md) | `RichText` mixed-style runs in one paragraph: bold/accent/styled segments, inline links, inline images, inline shapes and checkboxes | +| [rich-text.md](rich-text.md) | `RichText` mixed-style runs in one paragraph: bold/accent/styled segments, inline links, inline images, inline SVG icons, emoji shortcodes, inline shapes and checkboxes | | [lists.md](lists.md) | `addList`: quick bulleted lists, marker customisation, nested lists with per-depth markers, spacing and styled items | | [timelines.md](timelines.md) | `addTimeline`: markers (dot / circle / numbered / square) on a connector rail, geometry and text-style controls, pagination opt-ins | | [keep-together.md](keep-together.md) | `keepTogether()` / `keepEntriesTogether()` — blocks that relocate whole instead of orphaning a heading at a page break | diff --git a/docs/recipes/rich-text.md b/docs/recipes/rich-text.md index c0c9ecc7..d3085415 100644 --- a/docs/recipes/rich-text.md +++ b/docs/recipes/rich-text.md @@ -98,6 +98,52 @@ relative to the surrounding line (`CENTER` by default); the full overload adds a `baselineOffset` and `DocumentLinkOptions` for a clickable inline image. +## Inline SVG icons + +A parsed `SvgIcon` sits on the text baseline like a word, drawn as crisp +vector layers that carry their own colours — so it renders independently +of the active font's glyph coverage. `size` is the glyph height in points; +the width follows the icon's aspect ratio. + +```java +import com.demcha.compose.document.svg.SvgIcon; + +SvgIcon star = SvgIcon.parse( + "" + + " " + + ""); + +section.addRich(rich -> rich + .plain("Rated ") + .svgIcon(star, 11) + .plain(" by reviewers.")); +``` + +On `ParagraphBuilder` the equivalent call is `inlineSvgIcon(icon, size)`; +both take `alignment` / `baselineOffset` / link overloads and a clickable +form via `DocumentLinkOptions`. `SvgIcon.parse(String)` reads inline SVG +markup; `SvgIcon.read(Path)` loads it from a file. + +## Emoji / shortcodes + +`emoji(":code:")` resolves a GitHub-style shortcode to an inline colour +glyph through `EmojiLibrary`. Resolution is lenient: an unknown shortcode — +or no emoji set on the classpath — falls back to the literal text, the way +GitHub renders an unrecognised `:code:`. + +```java +section.addRich(rich -> rich + .plain("Deploy ") + .emoji(":white_check_mark:", 11) + .plain(" succeeded ") + .emoji(":rocket:", 11)); +``` + +On `ParagraphBuilder` the call is `inlineEmoji(":code:", size)`. Glyphs ship +in the optional, independently-versioned `graph-compose-emoji` companion +artifact (Noto Emoji, SIL OFL 1.1) — add it to the classpath to resolve +shortcodes; the engine itself carries no emoji art. + ## Inline shapes and checkboxes Geometric figures drawn from geometry — not font glyphs — so they render diff --git a/emoji/pom.xml b/emoji/pom.xml index 14bc8871..de83840d 100644 --- a/emoji/pom.xml +++ b/emoji/pom.xml @@ -80,7 +80,7 @@ 3.4.0 3.12.0 3.2.8 - 0.10.0 + 0.11.0 true diff --git a/fonts/pom.xml b/fonts/pom.xml index 136d7a40..30fdc035 100644 --- a/fonts/pom.xml +++ b/fonts/pom.xml @@ -77,7 +77,7 @@ 3.4.0 3.12.0 3.2.8 - 0.10.0 + 0.11.0 true diff --git a/src/main/java/com/demcha/compose/document/dsl/BarcodeBuilder.java b/src/main/java/com/demcha/compose/document/dsl/BarcodeBuilder.java index 0e20a2cd..38cf8969 100644 --- a/src/main/java/com/demcha/compose/document/dsl/BarcodeBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/BarcodeBuilder.java @@ -246,6 +246,7 @@ public BarcodeBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public BarcodeBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/dsl/EllipseBuilder.java b/src/main/java/com/demcha/compose/document/dsl/EllipseBuilder.java index 1ee1289d..8df48183 100644 --- a/src/main/java/com/demcha/compose/document/dsl/EllipseBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/EllipseBuilder.java @@ -158,6 +158,7 @@ public EllipseBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public EllipseBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/dsl/ImageBuilder.java b/src/main/java/com/demcha/compose/document/dsl/ImageBuilder.java index d082d8f0..9baf3b5f 100644 --- a/src/main/java/com/demcha/compose/document/dsl/ImageBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/ImageBuilder.java @@ -192,6 +192,7 @@ public ImageBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ImageBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/dsl/LineBuilder.java b/src/main/java/com/demcha/compose/document/dsl/LineBuilder.java index 56a92698..0ecd83e8 100644 --- a/src/main/java/com/demcha/compose/document/dsl/LineBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/LineBuilder.java @@ -271,6 +271,7 @@ public LineBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public LineBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/dsl/ParagraphBuilder.java b/src/main/java/com/demcha/compose/document/dsl/ParagraphBuilder.java index 7b23dcbf..1326c66a 100644 --- a/src/main/java/com/demcha/compose/document/dsl/ParagraphBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/ParagraphBuilder.java @@ -188,6 +188,7 @@ public ParagraphBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder linkTo(String anchor) { @@ -247,6 +248,7 @@ public ParagraphBuilder inlineLink(String text, DocumentLinkOptions linkOptions) * @param text visible link text * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder inlineLinkTo(String text, String anchor) { @@ -340,6 +342,7 @@ public ParagraphBuilder inlineImage(DocumentImageData imageData, * @param height target height in points * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder inlineImageLinkTo(DocumentImageData imageData, double width, double height, String anchor) { @@ -357,6 +360,7 @@ public ParagraphBuilder inlineImageLinkTo(DocumentImageData imageData, double wi * @param baselineOffset extra vertical shift in points; positive moves up * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder inlineImageLinkTo(DocumentImageData imageData, @@ -653,6 +657,7 @@ public ParagraphBuilder inlineEmoji(String shortcode, * @param fill fill color * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder shapeLinkTo(ShapeOutline outline, DocumentColor fill, String anchor) { @@ -671,6 +676,7 @@ public ParagraphBuilder shapeLinkTo(ShapeOutline outline, DocumentColor fill, St * @param baselineOffset extra vertical shift in points; positive moves up * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ParagraphBuilder shapeLinkTo(ShapeOutline outline, diff --git a/src/main/java/com/demcha/compose/document/dsl/RichText.java b/src/main/java/com/demcha/compose/document/dsl/RichText.java index 5a771b67..2382bfb6 100644 --- a/src/main/java/com/demcha/compose/document/dsl/RichText.java +++ b/src/main/java/com/demcha/compose/document/dsl/RichText.java @@ -221,6 +221,7 @@ public RichText link(String text, String uri) { * @param text visible link text * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText linkTo(String text, String anchor) { @@ -235,6 +236,7 @@ public RichText linkTo(String text, String anchor) { * @param style explicit style for this run, or {@code null} for the link default * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText linkTo(String text, DocumentTextStyle style, String anchor) { @@ -343,6 +345,7 @@ public RichText image(DocumentImageData imageData, * @param height target height in points * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText imageLinkTo(DocumentImageData imageData, double width, double height, String anchor) { @@ -360,6 +363,7 @@ public RichText imageLinkTo(DocumentImageData imageData, double width, double he * @param baselineOffset extra vertical shift in points; positive moves up * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText imageLinkTo(DocumentImageData imageData, @@ -650,6 +654,7 @@ public RichText shape(ShapeOutline outline, * @param fill fill color * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText shapeLinkTo(ShapeOutline outline, DocumentColor fill, String anchor) { @@ -668,6 +673,7 @@ public RichText shapeLinkTo(ShapeOutline outline, DocumentColor fill, String anc * @param baselineOffset extra vertical shift in points; positive moves up * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public RichText shapeLinkTo(ShapeOutline outline, diff --git a/src/main/java/com/demcha/compose/document/dsl/ShapeBuilder.java b/src/main/java/com/demcha/compose/document/dsl/ShapeBuilder.java index 335c9d0a..435d7cb7 100644 --- a/src/main/java/com/demcha/compose/document/dsl/ShapeBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/ShapeBuilder.java @@ -192,6 +192,7 @@ public ShapeBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public ShapeBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/dsl/TableBuilder.java b/src/main/java/com/demcha/compose/document/dsl/TableBuilder.java index 802632f7..198b75d4 100644 --- a/src/main/java/com/demcha/compose/document/dsl/TableBuilder.java +++ b/src/main/java/com/demcha/compose/document/dsl/TableBuilder.java @@ -407,6 +407,7 @@ public TableBuilder linkTarget(DocumentLinkTarget linkTarget) { * * @param anchor target anchor name * @return this builder + * @throws IllegalArgumentException if {@code anchor} is blank * @since 1.9.0 */ public TableBuilder linkTo(String anchor) { diff --git a/src/main/java/com/demcha/compose/document/emoji/EmojiLibrary.java b/src/main/java/com/demcha/compose/document/emoji/EmojiLibrary.java index c9e75fa1..d5abce32 100644 --- a/src/main/java/com/demcha/compose/document/emoji/EmojiLibrary.java +++ b/src/main/java/com/demcha/compose/document/emoji/EmojiLibrary.java @@ -26,8 +26,8 @@ *

The engine carries no emoji art and has no Maven dependency on the emoji * module — exactly like {@code DefaultFonts} and {@code graph-compose-fonts}. * This resolver is fully data-driven: any classpath providing that layout works, - * so the small bundled starter set can be replaced wholesale by the full - * jdecked/twemoji set with no code change.

+ * so the bundled Noto Emoji set (SIL OFL 1.1) can be swapped for another emoji + * set by changing the classpath alone, with no code change.

* *

Resolution is lenient by design — {@link #find(String)} returns an empty * {@link Optional} for an unknown shortcode or when no emoji set is on the