1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.font.freetype; 27 28 import com.sun.javafx.font.CompositeFontResource; 29 import com.sun.javafx.font.CompositeGlyphMapper; 30 import com.sun.javafx.font.FontResource; 31 import com.sun.javafx.font.FontStrike; 32 import com.sun.javafx.font.PGFont; 33 import com.sun.javafx.font.PrismFontFactory; 34 import com.sun.javafx.text.GlyphLayout; 35 import com.sun.javafx.text.TextRun; 36 37 class PangoGlyphLayout extends GlyphLayout { 38 39 private int getSlot(PGFont font, PangoGlyphString glyphString) { 40 CompositeFontResource fr = (CompositeFontResource)font.getFontResource(); 41 long fallbackFont = glyphString.font; 42 long fallbackFd = OSPango.pango_font_describe(fallbackFont); 43 String fallbackFamily = OSPango.pango_font_description_get_family(fallbackFd); 44 int fallbackStyle = OSPango.pango_font_description_get_style(fallbackFd); 45 int fallbackWeight = OSPango.pango_font_description_get_weight(fallbackFd); 46 OSPango.pango_font_description_free(fallbackFd); 47 boolean bold = fallbackWeight == OSPango.PANGO_WEIGHT_BOLD; 48 boolean italic = fallbackStyle != OSPango.PANGO_STYLE_NORMAL; 49 50 PrismFontFactory prismFactory = PrismFontFactory.getFontFactory(); 51 PGFont fallbackPGFont = prismFactory.createFont(fallbackFamily, bold, 52 italic, font.getSize()); 53 String fallbackFullname = fallbackPGFont.getFullName(); 54 String primaryFullname = fr.getSlotResource(0).getFullName(); 55 56 int slot = 0; 57 if (!fallbackFullname.equalsIgnoreCase(primaryFullname)) { 58 slot = fr.getSlotForFont(fallbackFullname); 59 if (PrismFontFactory.debugFonts) { 60 System.err.println("\tFallback font= "+ fallbackFullname + " slot=" + (slot>>24)); 61 } 62 } 63 return slot; 64 } 65 66 private boolean check(long checkValue, String message, long fontmap, long context, long desc, long attrList) { 67 if (checkValue != 0) return false; 68 if (message != null && PrismFontFactory.debugFonts) { 69 System.err.println(message); 70 } 71 /* pango_attr_list_unref() also frees the attributes it contains */ 72 if (attrList != 0) OSPango.pango_attr_list_unref(attrList); 73 if (desc != 0) OSPango.pango_font_description_free(desc); 74 if (context != 0) OSPango.g_object_unref(context); 75 if (fontmap != 0) OSPango.g_object_unref(fontmap); 76 return true; 77 } 78 79 private long str = 0L; 80 public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) { 81 82 /* Create the pango font and attribute list */ 83 FontResource fr = font.getFontResource(); 84 boolean composite = fr instanceof CompositeFontResource; 85 if (composite) { 86 fr = ((CompositeFontResource)fr).getSlotResource(0); 87 } 88 long fontmap = OSPango.pango_ft2_font_map_new(); 89 if (check(fontmap, "Failed allocating PangoFontMap.", 0, 0, 0, 0)) { 90 return; 91 } 92 long context = OSPango.pango_font_map_create_context(fontmap); 93 if (check(context, "Failed allocating PangoContext.", fontmap, 0, 0, 0)) { 94 return; 95 } 96 boolean rtl = (run.getLevel() & 1) != 0; 97 if (rtl) { 98 OSPango.pango_context_set_base_dir(context, OSPango.PANGO_DIRECTION_RTL); 99 } 100 float size = font.getSize(); 101 int style = fr.isItalic() ? OSPango.PANGO_STYLE_ITALIC : OSPango.PANGO_STYLE_NORMAL; 102 int weight = fr.isBold() ? OSPango.PANGO_WEIGHT_BOLD : OSPango.PANGO_WEIGHT_NORMAL; 103 long desc = OSPango.pango_font_description_new(); 104 if (check(desc, "Failed allocating FontDescription.", fontmap, context, 0, 0)) { 105 return; 106 } 107 OSPango.pango_font_description_set_family(desc, fr.getFamilyName()); 108 OSPango.pango_font_description_set_absolute_size(desc, size * OSPango.PANGO_SCALE); 109 OSPango.pango_font_description_set_stretch(desc, OSPango.PANGO_STRETCH_NORMAL); 110 OSPango.pango_font_description_set_style(desc, style); 111 OSPango.pango_font_description_set_weight(desc, weight); 112 long attrList = OSPango.pango_attr_list_new(); 113 if (check(attrList, "Failed allocating PangoAttributeList.", fontmap, context, desc, 0)) { 114 return; 115 } 116 long attr = OSPango.pango_attr_font_desc_new(desc); 117 if (check(attr, "Failed allocating PangoAttribute.", fontmap, context, desc, attrList)) { 118 return; 119 } 120 OSPango.pango_attr_list_insert(attrList, attr); 121 if (!composite) { 122 attr = OSPango.pango_attr_fallback_new(false); 123 OSPango.pango_attr_list_insert(attrList, attr); 124 } 125 126 if (str == 0L) { 127 str = OSPango.g_utf16_to_utf8(text); 128 if (check(str, "Failed allocating UTF-8 buffer.", fontmap, context, desc, attrList)) { 129 return; 130 } 131 } 132 133 /* Itemize */ 134 long start = OSPango.g_utf8_offset_to_pointer(str, run.getStart()); 135 long end = OSPango.g_utf8_offset_to_pointer(str, run.getEnd()); 136 long runs = OSPango.pango_itemize(context, str, (int)(start - str), (int)(end - start), attrList, 0); 137 138 if (runs != 0) { 139 /* Shape all PangoItem into PangoGlyphString */ 140 int runsCount = OSPango.g_list_length(runs); 141 PangoGlyphString[] pangoGlyphs = new PangoGlyphString[runsCount]; 142 for (int i = 0; i < runsCount; i++) { 143 long pangoItem = OSPango.g_list_nth_data(runs, i); 144 if (pangoItem != 0) { 145 pangoGlyphs[i] = OSPango.pango_shape(str, pangoItem); 146 OSPango.pango_item_free(pangoItem); 147 } 148 } 149 OSPango.g_list_free(runs); 150 151 int glyphCount = 0; 152 for (PangoGlyphString g : pangoGlyphs) { 153 if (g != null) { 154 glyphCount += g.num_glyphs; 155 } 156 } 157 int[] glyphs = new int[glyphCount]; 158 float[] pos = new float[glyphCount * 2 + 2]; 159 int[] indices = new int[glyphCount]; 160 int gi = 0; 161 int ci = rtl ? run.getLength() : 0; 162 int width = 0; 163 for (PangoGlyphString g : pangoGlyphs) { 164 if (g != null) { 165 int slot = composite ? getSlot(font, g) : 0; 166 if (rtl) ci -= g.num_chars; 167 for (int i = 0; i < g.num_glyphs; i++) { 168 int gii = gi + i; 169 if (slot != -1) { 170 int gg = g.glyphs[i]; 171 172 /* Ignoring any glyphs outside the GLYPHMASK range. 173 * Note that Pango uses PANGO_GLYPH_EMPTY (0x0FFFFFFF), PANGO_GLYPH_INVALID_INPUT (0xFFFFFFFF), 174 * and other values with special meaning. 175 */ 176 if (0 <= gg && gg <= CompositeGlyphMapper.GLYPHMASK) { 177 glyphs[gii] = (slot << 24) | gg; 178 } 179 } 180 if (size != 0) { 181 width += g.widths[i]; 182 pos[2 + (gii << 1)] = ((float)width) / OSPango.PANGO_SCALE; 183 } 184 indices[gii] = g.log_clusters[i] + ci; 185 } 186 if (!rtl) ci += g.num_chars; 187 gi += g.num_glyphs; 188 } 189 } 190 run.shape(glyphCount, glyphs, pos, indices); 191 } 192 193 check(0, null, fontmap, context, desc, attrList); 194 } 195 196 @Override 197 public void dispose() { 198 super.dispose(); 199 if (str != 0L) { 200 OSPango.g_free(str); 201 str = 0L; 202 } 203 } 204 }