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.coretext;
  27 
  28 import com.sun.javafx.font.CompositeFontResource;
  29 import com.sun.javafx.font.CompositeStrike;
  30 import com.sun.javafx.font.FontStrike;
  31 import com.sun.javafx.font.PGFont;
  32 import com.sun.javafx.font.PrismFontFactory;
  33 import com.sun.javafx.text.GlyphLayout;
  34 import com.sun.javafx.text.TextRun;
  35 
  36 class CTGlyphLayout extends GlyphLayout {
  37 
  38     private long createCTLine(long fontRef, char[] chars, boolean rtl,
  39                               int start, int length) {
  40         /* Use CoreText to analize the run */
  41         long alloc = OS.kCFAllocatorDefault();
  42         long textRef = OS.CFStringCreateWithCharacters(alloc, chars, start, length);
  43         long lineRef = 0;
  44         if (textRef != 0) {
  45             long attributes = OS.CFDictionaryCreateMutable(alloc, 4,
  46                                   OS.kCFTypeDictionaryKeyCallBacks(),
  47                                   OS.kCFTypeDictionaryValueCallBacks());
  48             if (attributes != 0) {
  49                 OS.CFDictionaryAddValue(attributes, OS.kCTFontAttributeName(), fontRef);
  50                 if (rtl) {
  51                     long paragraphStyleRef = OS.CTParagraphStyleCreate(OS.kCTWritingDirectionRightToLeft);
  52                     if (paragraphStyleRef != 0) {
  53                         OS.CFDictionaryAddValue(attributes, OS.kCTParagraphStyleAttributeName(), paragraphStyleRef);
  54                         OS.CFRelease(paragraphStyleRef);
  55                     }
  56                 }
  57                 /* Note that by default CoreText will apply kerning depending on the font*/
  58                 long attString = OS.CFAttributedStringCreate(alloc, textRef, attributes);
  59                 if (attString != 0) {
  60                     lineRef = OS.CTLineCreateWithAttributedString(attString);
  61                     OS.CFRelease(attString);
  62                 }
  63                 OS.CFRelease(attributes);
  64             }
  65             OS.CFRelease(textRef);
  66         }
  67         return lineRef;
  68     }
  69 
  70     private int getFontSlot(long runRef, CompositeFontResource fr, String name, int slot) {
  71         long runAttrs = OS.CTRunGetAttributes(runRef);
  72         if (runAttrs == 0) return -1;
  73         long actualFont = OS.CFDictionaryGetValue(runAttrs, OS.kCTFontAttributeName());
  74         if (actualFont == 0) return -1;
  75 
  76         /* Use the display name from the kCTFontDisplayNameAttribute attribute
  77          * instead of CTFontCopyDisplayName() to avoid localized names*/
  78         String fontName = OS.CTFontCopyAttributeDisplayName(actualFont);
  79         if (fontName == null) return -1;
  80         if (!fontName.equalsIgnoreCase(name)) {
  81             if (fr == null) return -1;
  82             slot = fr.getSlotForFont(fontName);
  83             if (PrismFontFactory.debugFonts) {
  84                 System.err.println("\tFallback font= "+ fontName + " slot=" + slot);
  85             }
  86         }
  87         return slot;
  88     }
  89 
  90     public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) {
  91 
  92         int baseSlot = 0;
  93         CompositeFontResource composite = null;
  94         if (strike instanceof CompositeStrike) {
  95             composite = (CompositeFontResource)strike.getFontResource();
  96             baseSlot = getInitialSlot(composite);
  97             strike = ((CompositeStrike)strike).getStrikeSlot(baseSlot);
  98         }
  99         float size = strike.getSize();
 100         String fontName = strike.getFontResource().getFullName();
 101         long fontRef = ((CTFontStrike)strike).getFontRef();
 102         if (fontRef == 0) return;
 103         boolean rtl = (run.getLevel() & 1) != 0;
 104         long lineRef = createCTLine(fontRef, text, rtl, run.getStart(), run.getLength());
 105         if (lineRef == 0) return;
 106         long runs = OS.CTLineGetGlyphRuns(lineRef);
 107         if (runs != 0) {
 108             int glyphCount = (int)OS.CTLineGetGlyphCount(lineRef);
 109             int[] glyphs = new int[glyphCount];
 110             float[] positions = new float[glyphCount * 2 + 2];
 111             int[] indices = new int[glyphCount];
 112             long runCount = OS.CFArrayGetCount(runs);
 113             int glyphStart = 0, posStart = 0, indicesStart = 0;
 114             for (int i = 0; i < runCount; i++) {
 115                 long runRef = OS.CFArrayGetValueAtIndex(runs, i);
 116                 if (runRef == 0) continue;
 117                 int slot = getFontSlot(runRef, composite, fontName, baseSlot);
 118                 if (slot != -1) {
 119                     glyphStart += OS.CTRunGetGlyphs(runRef, slot << 24, glyphStart, glyphs);
 120                 } else {
 121                     glyphStart += OS.CTRunGetGlyphCount(runRef);
 122                 }
 123                 if (size > 0) {
 124                     posStart += OS.CTRunGetPositions(runRef, posStart, positions);
 125                 }
 126                 indicesStart += OS.CTRunGetStringIndices(runRef, indicesStart, indices);
 127 
 128             }
 129             if (size > 0) {
 130                 positions[posStart] = (float)OS.CTLineGetTypographicBounds(lineRef);
 131             }
 132             run.shape(glyphCount, glyphs, positions, indices);
 133         }
 134         OS.CFRelease(lineRef);
 135     }
 136 }