/* * Copyright 2013 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.rendering.assets.font; import com.google.common.base.Charsets; import org.terasology.assets.ResourceUrn; import org.terasology.assets.format.AbstractAssetFileFormat; import org.terasology.assets.format.AssetDataFile; import org.terasology.assets.management.AssetManager; import org.terasology.assets.module.annotations.RegisterAssetFileFormat; import org.terasology.naming.Name; import org.terasology.rendering.assets.material.Material; import org.terasology.rendering.assets.texture.Texture; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; /** */ @RegisterAssetFileFormat public class FontFormat extends AbstractAssetFileFormat<FontData> { private static final String INTEGER_PATTERN = "((?:[\\+-]?\\d+)(?:[eE][\\+-]?\\d+)?)"; private static final Name FONT_RESOURCE_NAME = new Name("font"); // Common patterns private Pattern lineHeightPattern = Pattern.compile("lineHeight=" + INTEGER_PATTERN); private Pattern baseHeightPattern = Pattern.compile("base=" + INTEGER_PATTERN); private Pattern pagesPattern = Pattern.compile("pages=" + INTEGER_PATTERN); private Pattern pagePattern = Pattern.compile("page id=" + INTEGER_PATTERN + " file=\"(.*)\""); private Pattern charsPattern = Pattern.compile("chars count=" + INTEGER_PATTERN); private Pattern charPattern = Pattern.compile("char\\s+" + "id=" + INTEGER_PATTERN + "\\s+" + "x=" + INTEGER_PATTERN + "\\s+" + "y=" + INTEGER_PATTERN + "\\s+" + "width=" + INTEGER_PATTERN + "\\s+" + "height=" + INTEGER_PATTERN + "\\s+" + "xoffset=" + INTEGER_PATTERN + "\\s+" + "yoffset=" + INTEGER_PATTERN + "\\s+" + "xadvance=" + INTEGER_PATTERN + "\\s+" + "page=" + INTEGER_PATTERN + "\\s+" + "chnl=" + INTEGER_PATTERN + "\\s*"); private AssetManager assetManager; public FontFormat(AssetManager assetManager) { super("fnt"); this.assetManager = assetManager; } @Override public FontData load(ResourceUrn urn, List<AssetDataFile> inputs) throws IOException { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputs.get(0).openStream(), Charsets.UTF_8))) { FontDataBuilder builder = new FontDataBuilder(); parseHeader(reader.readLine()); int numPages = parseCommon(builder, reader.readLine()); for (int i = 0; i < numPages; ++i) { parsePage(builder, urn.getModuleName(), reader.readLine()); } int charCount = getCharacterCount(reader.readLine()); for (int i = 0; i < charCount; ++i) { parseCharacter(builder, reader.readLine()); } return builder.build(); } } private void parseCharacter(FontDataBuilder builder, String charInfo) throws IOException { Matcher matcher = charPattern.matcher(charInfo); if (matcher.matches()) { try { builder.startCharacter(Integer.parseInt(matcher.group(1))) .setCharacterX(Integer.parseInt(matcher.group(2))) .setCharacterY(Integer.parseInt(matcher.group(3))) .setCharacterWidth(Integer.parseInt(matcher.group(4))) .setCharacterHeight(Integer.parseInt(matcher.group(5))) .setCharacterXOffset(Integer.parseInt(matcher.group(6))) .setCharacterYOffset(Integer.parseInt(matcher.group(7))) .setCharacterXAdvance(Integer.parseInt(matcher.group(8))) .setCharacterPage(Integer.parseInt(matcher.group(9))) .endCharacter(); } catch (IllegalArgumentException e) { throw new IOException("Failed to load font", e); } } else { throw new IOException("Failed to parse font - invalid char line '" + charInfo + "'"); } } private int getCharacterCount(String charsInfo) throws IOException { Matcher charsMatcher = charsPattern.matcher(charsInfo); if (charsMatcher.matches()) { return Integer.parseInt(charsMatcher.group(1)); } else { throw new IOException("Failed to load font - invalid chars line '" + charsInfo + "'"); } } private void parsePage(FontDataBuilder builder, Name moduleName, String pageInfo) throws IOException { Matcher pageMatcher = pagePattern.matcher(pageInfo); if (pageMatcher.matches()) { int pageId = Integer.parseInt(pageMatcher.group(1)); Name textureName = new Name(pageMatcher.group(2).substring(0, pageMatcher.group(2).lastIndexOf('.'))); Optional<Material> material = assetManager.getAsset(new ResourceUrn(moduleName, new Name("font"), textureName), Material.class); if (!material.isPresent()) { throw new IOException("Failed to load font - unable to resolve font page '" + textureName + "'"); } else { builder.addPage(pageId, assetManager.getAsset(new ResourceUrn(moduleName, textureName), Texture.class).get(), material.get()); } } else { throw new IOException("Failed to load font - invalid page line '" + pageInfo + "'"); } } private void parseHeader(String info) throws IOException { if (!info.startsWith("info ")) { throw new IOException("Invalid font - missing info line"); } // We don't actually use anything from the info line } private int parseCommon(FontDataBuilder builder, String commonLine) throws IOException { if (!commonLine.startsWith("common ")) { throw new IOException("Failed to load font - missing common line"); } builder.setLineHeight(findInteger(lineHeightPattern, commonLine)); builder.setBaseHeight(findInteger(baseHeightPattern, commonLine)); return findInteger(pagesPattern, commonLine); } private int findInteger(Pattern pattern, String in) { Matcher matcher = pattern.matcher(in); if (matcher.find()) { return Integer.parseInt(matcher.group(1)); } return 0; } }