diff --git a/.github/GitHub-Actions.org b/.github/GitHub-Actions.org new file mode 100644 index 0000000..1c882b8 --- /dev/null +++ b/.github/GitHub-Actions.org @@ -0,0 +1,41 @@ +* Deploying using Github Actions + +** Actions definition: workflow/sonatype-deploy.yml + +When a GitHub Release is created, usually from a tag, this action will trigger. + +Using JDK8 the software will be packaged, including running any tests. + +Then the Deploy script will sign the created artifacts then deploy them according to the distributionManagement configuration in the `pom.xml`. + +** Deploy Script + +Uses a signing key provided from the GitHub Actions Secrets as an environment variable to sign the artifact(s) before they are then deployed. + +*** Inputs + +**** DEPLOY_PROJECTS (optional) + +An optional list of modules in a multi-module project to be deployed. If this value is not specified, then all projects will be deployed. + +** Maven Configuration + +Picks up the credentials from Environment variables for authenticating both with GPG and with the target deployment server (e.g. sonatype-nexus). + +*** Inputs + +**** NEXUS_USERNAME + +The username for your account on the deployment server. + +**** NEXUS_PASSWORD + +The password for your account on the deployement server. + +**** GPG_KEYNAME + +The key to use when signing. + +**** GPG_PASSPHRASE + +The passphrase to unlock the key to use when signing. diff --git a/.github/NOTES b/.github/NOTES new file mode 100644 index 0000000..64253c9 --- /dev/null +++ b/.github/NOTES @@ -0,0 +1,53 @@ +Add subkeys: + +???? + +Publish: + +gpg --send-keys --keyserver keyserver.ubuntu.com $KEYID +gpg --send-keys --keyserver pgp.mit.edu $KEYID +gpg --send-keys --keyserver pool.sks-keyservers.net $KEYID + +Backup: + +gpg --export --armor pcampbell@kemitix.net > gpg-key-backup.asc +gpg --export-secret-keys --armor pcampbell@kemitix.net >> gpg-key-backup.asc + +Export sub-keys: + +gpg --export-secret-subkeys pcampbell@kemitix.net > subkeys + +Remove master keys: + +gpg --delete-secret-key pcampbell@kemitix.net + +Import sub-keys and clean up: + +gpg --import subkeys + +shred --remove subkeys + +Delete any encryption subkeys: + +gpg --edit-key pcampbell@kemitix.net + +2 +delkey +save + +Change passphrase: + +gpg --edit-key pcampbell@kemitix.net +passwd +save + +Export keys: + +gpg --export --armor pcampbell@kemitix.net > codesigning.asc +gpg --export-secret-keys --armor pcampbell@kemitix.net >> codesigning.asc + +Encrypt keys: + +gpg --symmetric --cipher-algo AES256 codesigning.asc + +shred codesigning.asc diff --git a/.github/codesigning.asc.gpg b/.github/codesigning.asc.gpg new file mode 100644 index 0000000..f5c71e0 Binary files /dev/null and b/.github/codesigning.asc.gpg differ diff --git a/.github/settings.xml b/.github/settings.xml new file mode 100644 index 0000000..8791e47 --- /dev/null +++ b/.github/settings.xml @@ -0,0 +1,28 @@ + + + + + sonatype-nexus-snapshots + ${env.NEXUS_USERNAME} + ${env.NEXUS_PASSWORD} + + + sonatype-nexus-staging + ${env.NEXUS_USERNAME} + ${env.NEXUS_PASSWORD} + + + + + gpg-sign + + true + + + gpg + ${env.GPG_KEYNAME} + ${env.GPG_PASSPHRASE} + + + + diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml new file mode 100644 index 0000000..dc00080 --- /dev/null +++ b/.github/workflows/maven-build.yml @@ -0,0 +1,26 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: '*' + pull_request: + branches: '*' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ 11, 13 ] + steps: + - uses: kamiazya/setup-graphviz@v1 + - uses: actions/checkout@v2 + - name: setup-java-${{ matrix.java }} + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: install + run: mvn -B install diff --git a/.github/workflows/sonatype-deploy.yml b/.github/workflows/sonatype-deploy.yml new file mode 100644 index 0000000..2465173 --- /dev/null +++ b/.github/workflows/sonatype-deploy.yml @@ -0,0 +1,39 @@ +name: Deploy to Sonatype Nexus + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: kamiazya/setup-graphviz@v1 + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn -B install + - name: Nexus Repo Publish + run: | + gpg --quiet \ + --batch \ + --yes \ + --decrypt \ + --passphrase="${{ secrets.GPG_PASSPHRASE }}" \ + --output codesigning.asc \ + .github/codesigning.asc.gpg + gpg --batch \ + --fast-import codesigning.asc + mvn --settings .github/settings.xml \ + -Dskip-Tests=true \ + -P release \ + -B \ + deploy + env: + NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + GPG_KEYNAME: ${{ secrets.GPG_KEYNAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.gitignore b/.gitignore index a1c2a23..2ea9f5e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,17 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +*.jqwik-database* + +*.iml +.idea/ +target/ +pom.xml.* +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..08e9fcb --- /dev/null +++ b/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + net.kemitix + kemitix-parent + 5.3.0 + + + + text-wrap-fit-box + DEV-SNAPSHOT + + + 2.16 + 2.6.0 + 5.4.0 + 1.18.12 + + 5.6.2 + 3.16.0 + 3.3.3 + 1.2.7 + + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + + + + + + + + + io.repaint.maven + tiles-maven-plugin + ${tiles-maven-plugin.version} + true + + + net.kemitix.tiles:maven-plugins:${kemitix-maven-tiles.version} + net.kemitix.tiles:enforcer:${kemitix-maven-tiles.version} + net.kemitix.tiles:compiler-jdk-11:${kemitix-maven-tiles.version} + net.kemitix.tiles:pmd:${kemitix-maven-tiles.version} + net.kemitix.tiles:testing:${kemitix-maven-tiles.version} + net.kemitix.tiles:coverage:${kemitix-maven-tiles.version} + net.kemitix.tiles:pitest:${kemitix-maven-tiles.version} + net.kemitix.checkstyle:tile:${kemitix-checkstyle.version} + + + + + + + + diff --git a/src/main/java/net/kemitix/text/fit/TextFit.java b/src/main/java/net/kemitix/text/fit/TextFit.java new file mode 100644 index 0000000..3ad6509 --- /dev/null +++ b/src/main/java/net/kemitix/text/fit/TextFit.java @@ -0,0 +1,7 @@ +package net.kemitix.text.fit; + +public interface TextFit { + static WordWrapper wrapper() { + return new TextLineWrapImpl(); + } +} diff --git a/src/main/java/net/kemitix/text/fit/TextLineWrapImpl.java b/src/main/java/net/kemitix/text/fit/TextLineWrapImpl.java new file mode 100644 index 0000000..3735185 --- /dev/null +++ b/src/main/java/net/kemitix/text/fit/TextLineWrapImpl.java @@ -0,0 +1,65 @@ +package net.kemitix.text.fit; + +import lombok.Getter; + +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +class TextLineWrapImpl implements WordWrapper { + + @Override + public List wrap( + String text, + Font font, + Graphics2D graphics2D, + int width + ) { + String source = String.join(" ", text.split("\n")); + List words = wordLengths(source.split(" "), font, graphics2D); + return wrapWords(words, width); + } + + private List wrapWords(List words, int width) { + List lines = new ArrayList<>(); + int end = 0; + List line = new ArrayList<>(); + for (Word word : words) { + if ((end + word.width) > width) { + lines.add(String.join(" ", line)); + line.clear(); + end = 0; + } + line.add(word.word); + end += word.width; + } + lines.add(String.join(" ", line)); + return lines.stream() + .filter(l -> l.length() > 0) + .collect(Collectors.toList()); + } + + private List wordLengths(String[] words, Font font, Graphics2D graphics2D) { + FontRenderContext fontRenderContext = graphics2D.getFontRenderContext(); + return Arrays.stream(words) + .map(word -> new Word(word, font, fontRenderContext)) + .collect(Collectors.toList()); + } + + @Getter + private static class Word { + private final String word; + private final int width; + + public Word(String word, Font font, FontRenderContext fontRenderContext) { + this.word = word; + Rectangle2D stringBounds = font.getStringBounds(word + " ", fontRenderContext); + this.width = Double.valueOf(stringBounds.getWidth()).intValue(); + } + } + +} diff --git a/src/main/java/net/kemitix/text/fit/WordWrapper.java b/src/main/java/net/kemitix/text/fit/WordWrapper.java new file mode 100644 index 0000000..a4d2a09 --- /dev/null +++ b/src/main/java/net/kemitix/text/fit/WordWrapper.java @@ -0,0 +1,13 @@ +package net.kemitix.text.fit; + +import java.awt.*; +import java.util.List; + +public interface WordWrapper { + List wrap( + String text, + Font font, + Graphics2D graphics2D, + int width + ); +} diff --git a/src/test/java/net/kemitix/text/fit/TextLineWrapTest.java b/src/test/java/net/kemitix/text/fit/TextLineWrapTest.java new file mode 100644 index 0000000..7266db8 --- /dev/null +++ b/src/test/java/net/kemitix/text/fit/TextLineWrapTest.java @@ -0,0 +1,81 @@ +package net.kemitix.text.fit; + +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; + +public class TextLineWrapTest + implements WithAssertions { + + private final WordWrapper textLineWrap = TextFit.wrapper(); + private final int imageSize = 300; + private final int fontSize = 20; + private final Graphics2D graphics100x100 = graphics(imageSize, imageSize); + private final Font font; + + public TextLineWrapTest() + throws FontFormatException, + IOException, + URISyntaxException { + URL resource = this.getClass().getResource("alice/Alice-Regular.ttf"); + font = Font.createFont(Font.TRUETYPE_FONT, new File(resource.toURI())) + .deriveFont(Font.PLAIN, fontSize); + } + + private List invoke(String in) { + return textLineWrap.wrap(in, font, graphics100x100, imageSize); + } + + @Test + @DisplayName("Empty String give empty List") + public void emptyStringEmptyList() { + assertThat(invoke("")).isEmpty(); + } + + @Test + @DisplayName("Short string fits on one line") + public void shortStringOnOneLine() { + assertThat(invoke("x")).containsExactly("x"); + } + + @Test + @DisplayName("Longer string fits on two lines") + public void longerStringOnTwoLines() { + assertThat(invoke( + "xxxxxxxxxxx xxxxxxxxxxxx " + + "xxxxxxxxxxx xxxxxxxxxxxx")) + .containsExactly( + "xxxxxxxxxxx xxxxxxxxxxxx", + "xxxxxxxxxxx xxxxxxxxxxxx"); + } + + @Test + @DisplayName("Longer string fits on three lines") + public void longerStringOnThreeLines() { + assertThat(invoke( + "xxxxxxxxxxx xxxxxxxxxxxx " + + "xxxxxxxxxxx xxxxxxxxxxxx " + + "xxxxxxxxxxx xxxxxxxxxxxx")) + .containsExactly( + "xxxxxxxxxxx xxxxxxxxxxxx", + "xxxxxxxxxxx xxxxxxxxxxxx", + "xxxxxxxxxxx xxxxxxxxxxxx"); + } + + private Graphics2D graphics(int width, int height) { + return image(width, height) + .createGraphics(); + } + + private BufferedImage image(int width, int height) { + return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } +} diff --git a/src/test/resources/net/kemitix/text/fit/alice/Alice-Regular.ttf b/src/test/resources/net/kemitix/text/fit/alice/Alice-Regular.ttf new file mode 100644 index 0000000..12dfdc1 Binary files /dev/null and b/src/test/resources/net/kemitix/text/fit/alice/Alice-Regular.ttf differ diff --git a/src/test/resources/net/kemitix/text/fit/alice/FONTLOG.txt b/src/test/resources/net/kemitix/text/fit/alice/FONTLOG.txt new file mode 100644 index 0000000..8dade5f --- /dev/null +++ b/src/test/resources/net/kemitix/text/fit/alice/FONTLOG.txt @@ -0,0 +1,50 @@ +FONTLOG for the Alice font + +This file provides detailed information on the Alice font Software. + +This information should be distributed along with the +Alice fonts and any derivative works. + +Basic Font Information + +Ksenia Erulevich, designer of Alice typeface, +was inspired by Lewis Carroll's novel and +decided to make a typeface that will +be suitable for typesetting this book. + +It came out eclectic and quaint, old-fashioned, +having widened proportions, open aperture, and +soft rounded features. Perfect for long meditative text-setting +and headlines. + +This is in fact Ksenia's first typeface, as part of +her diploma project at the Type & Typography course in +Moscow, Russia. + +Released by Cyreal with help from Gayaneh Bagdasaryan +and Alexei Vanyashin. + +Alice is a Unicode typeface family that supports +languages that use the Latin script and its variants, and +could be expanded to support other scripts. + +More specifically, this release supports the following Unicode +ranges: Latin-1 + +To contribute to the project contact Alexei Vanyashin at +a@cyreal.org + +There are two .VBF Source files: +1. Alice_PS_Source.vbf (Original Source files with contour overlaps) +2. Alice_merged_and_optimized.vbf (Merged and optimized file) + +ChangeLog + +If you make modifications be sure to add your name (N), +email (E), web-address (if you have one) (W) and description (D). +This list is in alphabetical order. + +N: Alexei Vanyashin +E: a@cyreal.org +W: www.cyreal.org +D: Designer and Mastering \ No newline at end of file diff --git a/src/test/resources/net/kemitix/text/fit/alice/OFL.txt b/src/test/resources/net/kemitix/text/fit/alice/OFL.txt new file mode 100644 index 0000000..636f1b4 --- /dev/null +++ b/src/test/resources/net/kemitix/text/fit/alice/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2011, Cyreal (www.cyreal.org), +with Reserved Font Name "Alice" and "Alice Regular". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE.