OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
14 * its contributors may be used to endorse or promote products derived | |
15 * from this software without specific prior written permission. | |
16 * | |
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 */ | |
28 | |
29 #include "platform/fonts/GlyphPageTreeNode.h" | |
30 | |
31 #include "platform/fonts/SegmentedFontData.h" | |
32 #include "platform/fonts/SimpleFontData.h" | |
33 #include "platform/fonts/opentype/OpenTypeVerticalData.h" | |
34 #include "wtf/PtrUtil.h" | |
35 #include "wtf/text/CString.h" | |
36 #include "wtf/text/CharacterNames.h" | |
37 #include "wtf/text/WTFString.h" | |
38 #include <memory> | |
39 #include <stdio.h> | |
40 | |
41 namespace blink { | |
42 | |
43 using std::max; | |
44 using std::min; | |
45 | |
46 HashMap<int, GlyphPageTreeNode*>* GlyphPageTreeNode::roots = 0; | |
47 GlyphPageTreeNode* GlyphPageTreeNode::pageZeroRoot = 0; | |
48 | |
49 GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber) { | |
50 static bool initialized; | |
51 if (!initialized) { | |
52 initialized = true; | |
53 roots = new HashMap<int, GlyphPageTreeNode*>; | |
54 pageZeroRoot = new GlyphPageTreeNode(); | |
55 } | |
56 | |
57 if (!pageNumber) | |
58 return pageZeroRoot; | |
59 | |
60 if (GlyphPageTreeNode* foundNode = roots->get(pageNumber)) | |
61 return foundNode; | |
62 | |
63 GlyphPageTreeNode* node = new GlyphPageTreeNode(); | |
64 #if ENABLE(ASSERT) | |
65 node->m_pageNumber = pageNumber; | |
66 #endif | |
67 roots->set(pageNumber, node); | |
68 return node; | |
69 } | |
70 | |
71 size_t GlyphPageTreeNode::treeGlyphPageCount() { | |
72 size_t count = 0; | |
73 if (roots) { | |
74 HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); | |
75 for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); | |
76 it != end; ++it) | |
77 count += it->value->pageCount(); | |
78 } | |
79 | |
80 if (pageZeroRoot) | |
81 count += pageZeroRoot->pageCount(); | |
82 | |
83 return count; | |
84 } | |
85 | |
86 size_t GlyphPageTreeNode::pageCount() const { | |
87 size_t count = m_page && m_page->owner() == this ? 1 : 0; | |
88 | |
89 GlyphPageTreeNodeMap::const_iterator end = m_children.end(); | |
90 for (GlyphPageTreeNodeMap::const_iterator it = m_children.begin(); it != end; | |
91 ++it) | |
92 count += it->value->pageCount(); | |
93 | |
94 if (m_systemFallbackChild) | |
95 ++count; | |
96 | |
97 return count; | |
98 } | |
99 | |
100 void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) { | |
101 // Enumerate all the roots and prune any tree that contains our custom font | |
102 // data. | |
103 if (roots) { | |
104 HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); | |
105 for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); | |
106 it != end; ++it) | |
107 it->value->pruneCustomFontData(fontData); | |
108 } | |
109 | |
110 if (pageZeroRoot) | |
111 pageZeroRoot->pruneCustomFontData(fontData); | |
112 } | |
113 | |
114 void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData) { | |
115 if (roots) { | |
116 HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); | |
117 for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); | |
118 it != end; ++it) | |
119 it->value->pruneFontData(fontData); | |
120 } | |
121 | |
122 if (pageZeroRoot) | |
123 pageZeroRoot->pruneFontData(fontData); | |
124 } | |
125 | |
126 static bool fill(GlyphPage* pageToFill, | |
127 unsigned offset, | |
128 unsigned length, | |
129 UChar* buffer, | |
130 unsigned bufferLength, | |
131 const SimpleFontData* fontData) { | |
132 bool hasGlyphs = | |
133 fontData->fillGlyphPage(pageToFill, offset, length, buffer, bufferLength); | |
134 return hasGlyphs; | |
135 } | |
136 | |
137 void GlyphPageTreeNode::initializePage(const FontData* fontData, | |
138 unsigned pageNumber) { | |
139 ASSERT(!m_page); | |
140 ASSERT(fontData); | |
141 | |
142 // This function must not be called for the root of the tree, because that | |
143 // level does not contain any glyphs. | |
144 ASSERT(m_level > 0 && m_parent); | |
145 | |
146 if (m_level == 1) { | |
147 // Children of the root hold pure pages. These will cover only one | |
148 // font data's glyphs, and will have glyph index 0 if the font data does not | |
149 // contain the glyph. | |
150 initializePurePage(fontData, pageNumber); | |
151 return; | |
152 } | |
153 | |
154 // The parent's page will be 0 if we are level one or the parent's font data | |
155 // did not contain any glyphs for that page. | |
156 GlyphPage* parentPage = m_parent->page(); | |
157 | |
158 if (parentPage && parentPage->owner() != m_parent) { | |
159 // The page we're overriding may not be owned by our parent node. | |
160 // This happens when our parent node provides no useful overrides | |
161 // and just copies the pointer to an already-existing page (see | |
162 // below). | |
163 // | |
164 // We want our override to be shared by all nodes that reference | |
165 // that page to avoid duplication, and so standardize on having the | |
166 // page's owner collect all the overrides. Call getChild on the | |
167 // page owner with the desired font data (this will populate | |
168 // the page) and then reference it. | |
169 m_page = static_cast<GlyphPageTreeNode*>(parentPage->owner()) | |
170 ->getNormalChild(fontData, pageNumber) | |
171 ->page(); | |
172 return; | |
173 } | |
174 | |
175 initializeOverridePage(fontData, pageNumber); | |
176 } | |
177 | |
178 void GlyphPageTreeNode::initializePurePage(const FontData* fontData, | |
179 unsigned pageNumber) { | |
180 ASSERT(m_level == 1); | |
181 | |
182 unsigned start = pageNumber * GlyphPage::size; | |
183 UChar buffer[GlyphPage::size * 2 + 2]; | |
184 unsigned bufferLength; | |
185 unsigned i; | |
186 | |
187 // Fill in a buffer with the entire "page" of characters that we want to look | |
188 // up glyphs for. | |
189 if (start < 0x10000) { | |
190 bufferLength = GlyphPage::size; | |
191 for (i = 0; i < GlyphPage::size; i++) | |
192 buffer[i] = start + i; | |
193 | |
194 if (start == 0) { | |
195 // Control characters must not render at all. | |
196 for (i = 0; i < 0x20; ++i) | |
197 buffer[i] = zeroWidthSpaceCharacter; | |
198 for (i = 0x7F; i < 0xA0; i++) | |
199 buffer[i] = zeroWidthSpaceCharacter; | |
200 buffer[softHyphenCharacter] = zeroWidthSpaceCharacter; | |
201 | |
202 // \n and \t must render as a spaceCharacter. | |
203 buffer[newlineCharacter] = spaceCharacter; | |
204 buffer[tabulationCharacter] = spaceCharacter; | |
205 } else if (start == (arabicLetterMarkCharacter & ~(GlyphPage::size - 1))) { | |
206 buffer[arabicLetterMarkCharacter - start] = zeroWidthSpaceCharacter; | |
207 } else if (start == (leftToRightMarkCharacter & ~(GlyphPage::size - 1))) { | |
208 // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all. | |
209 buffer[leftToRightMarkCharacter - start] = zeroWidthSpaceCharacter; | |
210 buffer[rightToLeftMarkCharacter - start] = zeroWidthSpaceCharacter; | |
211 buffer[leftToRightEmbedCharacter - start] = zeroWidthSpaceCharacter; | |
212 buffer[rightToLeftEmbedCharacter - start] = zeroWidthSpaceCharacter; | |
213 buffer[leftToRightOverrideCharacter - start] = zeroWidthSpaceCharacter; | |
214 buffer[rightToLeftOverrideCharacter - start] = zeroWidthSpaceCharacter; | |
215 buffer[zeroWidthNonJoinerCharacter - start] = zeroWidthSpaceCharacter; | |
216 buffer[zeroWidthJoinerCharacter - start] = zeroWidthSpaceCharacter; | |
217 buffer[popDirectionalFormattingCharacter - start] = | |
218 zeroWidthSpaceCharacter; | |
219 buffer[activateArabicFormShapingCharacter - start] = | |
220 zeroWidthSpaceCharacter; | |
221 buffer[activateSymmetricSwappingCharacter - start] = | |
222 zeroWidthSpaceCharacter; | |
223 buffer[firstStrongIsolateCharacter - start] = zeroWidthSpaceCharacter; | |
224 buffer[inhibitArabicFormShapingCharacter - start] = | |
225 zeroWidthSpaceCharacter; | |
226 buffer[inhibitSymmetricSwappingCharacter - start] = | |
227 zeroWidthSpaceCharacter; | |
228 buffer[leftToRightIsolateCharacter - start] = zeroWidthSpaceCharacter; | |
229 buffer[nationalDigitShapesCharacter - start] = zeroWidthSpaceCharacter; | |
230 buffer[nominalDigitShapesCharacter - start] = zeroWidthSpaceCharacter; | |
231 buffer[popDirectionalIsolateCharacter - start] = zeroWidthSpaceCharacter; | |
232 buffer[rightToLeftIsolateCharacter - start] = zeroWidthSpaceCharacter; | |
233 } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) { | |
234 // Object replacement character must not render at all. | |
235 buffer[objectReplacementCharacter - start] = zeroWidthSpaceCharacter; | |
236 } else if (start == | |
237 (zeroWidthNoBreakSpaceCharacter & ~(GlyphPage::size - 1))) { | |
238 // ZWNBS/BOM must not render at all. | |
239 buffer[zeroWidthNoBreakSpaceCharacter - start] = zeroWidthSpaceCharacter; | |
240 } | |
241 } else { | |
242 bufferLength = GlyphPage::size * 2; | |
243 for (i = 0; i < GlyphPage::size; i++) { | |
244 int c = i + start; | |
245 buffer[i * 2] = U16_LEAD(c); | |
246 buffer[i * 2 + 1] = U16_TRAIL(c); | |
247 } | |
248 } | |
249 | |
250 // Now that we have a buffer full of characters, we want to get back an array | |
251 // of glyph indices. This part involves calling into the platform-specific | |
252 // routine of our glyph map for actually filling in the page with the glyphs. | |
253 // Success is not guaranteed. For example, Times fails to fill page 260, | |
254 // giving glyph data for only 128 out of 256 characters. | |
255 bool haveGlyphs; | |
256 if (!fontData->isSegmented()) { | |
257 m_page = | |
258 GlyphPage::createForSingleFontData(this, toSimpleFontData(fontData)); | |
259 haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, bufferLength, | |
260 toSimpleFontData(fontData)); | |
261 } else { | |
262 m_page = GlyphPage::createForMixedFontData(this); | |
263 haveGlyphs = false; | |
264 | |
265 const SegmentedFontData* segmentedFontData = toSegmentedFontData(fontData); | |
266 for (int i = segmentedFontData->numFaces() - 1; i >= 0; i--) { | |
267 RefPtr<FontDataForRangeSet> fontDataForRangeSet = | |
268 segmentedFontData->faceAt(i); | |
269 RefPtr<UnicodeRangeSet> ranges = fontDataForRangeSet->ranges(); | |
270 // If there are no ranges, that means this font should be used for | |
271 // the full codepoint range, thus running the loop once over a | |
272 // synthetic full UnicodeRange object. Otherwise we use the ranges | |
273 // that come from the segmented font. This needs a locally | |
274 // initialized size_t variable (or a cast alternatively) as a | |
275 // compile fix for Android since size_t differs between platforms. | |
276 const size_t oneRoundForZeroRanges = 1u; | |
277 for (size_t i = 0; i < max(ranges->size(), oneRoundForZeroRanges); ++i) { | |
278 UnicodeRange range(0, kMaxCodepoint); | |
279 if (ranges->size()) { | |
280 range = ranges->rangeAt(i); | |
281 } | |
282 // all this casting is to ensure all the parameters to min and max have | |
283 // the same type, to avoid ambiguous template parameter errors on | |
284 // Windows | |
285 int from = | |
286 max(0, static_cast<int>(range.from()) - static_cast<int>(start)); | |
287 int to = 1 + min(static_cast<int>(range.to()) - static_cast<int>(start), | |
288 static_cast<int>(GlyphPage::size) - 1); | |
289 if (from >= static_cast<int>(GlyphPage::size) || to <= 0) | |
290 continue; | |
291 | |
292 // If this is a custom font needs to be loaded, do not fill | |
293 // the page so that font fallback is used while loading. | |
294 RefPtr<CustomFontData> customData = | |
295 fontDataForRangeSet->fontData()->customFontData(); | |
296 if (customData && customData->isLoadingFallback()) { | |
297 for (int j = from; j < to; j++) { | |
298 m_page->setCustomFontToLoad(j, customData.get()); | |
299 haveGlyphs = true; | |
300 } | |
301 continue; | |
302 } | |
303 | |
304 haveGlyphs |= fill(m_page.get(), from, to - from, | |
305 buffer + from * (start < 0x10000 ? 1 : 2), | |
306 (to - from) * (start < 0x10000 ? 1 : 2), | |
307 fontDataForRangeSet->fontData()); | |
308 } | |
309 } | |
310 } | |
311 | |
312 if (!haveGlyphs) | |
313 m_page = nullptr; | |
314 } | |
315 | |
316 void GlyphPageTreeNode::initializeOverridePage(const FontData* fontData, | |
317 unsigned pageNumber) { | |
318 GlyphPage* parentPage = m_parent->page(); | |
319 | |
320 // Get the pure page for the fallback font (at level 1 with no | |
321 // overrides). getRootChild will always create a page if one | |
322 // doesn't exist, but the page doesn't necessarily have glyphs | |
323 // (this pointer may be 0). | |
324 GlyphPage* fallbackPage = getNormalRootChild(fontData, pageNumber)->page(); | |
325 if (!parentPage) { | |
326 // When the parent has no glyphs for this page, we can easily | |
327 // override it just by supplying the glyphs from our font. | |
328 m_page = fallbackPage; | |
329 return; | |
330 } | |
331 | |
332 if (!fallbackPage) { | |
333 // When our font has no glyphs for this page, we can just reference the | |
334 // parent page. | |
335 m_page = parentPage; | |
336 return; | |
337 } | |
338 | |
339 // Combine the parent's glyphs and ours to form a new more complete page. | |
340 m_page = GlyphPage::createForMixedFontData(this); | |
341 | |
342 // Overlay the parent page on the fallback page. Check if the fallback font | |
343 // has added anything. | |
344 bool newGlyphs = false; | |
345 for (unsigned i = 0; i < GlyphPage::size; i++) { | |
346 if (parentPage->glyphAt(i)) { | |
347 m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); | |
348 } else if (fallbackPage->glyphAt(i)) { | |
349 m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); | |
350 newGlyphs = true; | |
351 } | |
352 | |
353 if (parentPage->customFontToLoadAt(i)) { | |
354 m_page->setCustomFontToLoad(i, parentPage->customFontToLoadAt(i)); | |
355 } else if (fallbackPage->customFontToLoadAt(i) && !parentPage->glyphAt(i)) { | |
356 m_page->setCustomFontToLoad(i, fallbackPage->customFontToLoadAt(i)); | |
357 newGlyphs = true; | |
358 } | |
359 } | |
360 | |
361 if (!newGlyphs) { | |
362 // We didn't override anything, so our override is just the parent page. | |
363 m_page = parentPage; | |
364 } | |
365 } | |
366 | |
367 GlyphPageTreeNode* GlyphPageTreeNode::getNormalChild(const FontData* fontData, | |
368 unsigned pageNumber) { | |
369 ASSERT(fontData); | |
370 ASSERT(pageNumber == m_pageNumber); | |
371 | |
372 if (GlyphPageTreeNode* foundChild = m_children.get(fontData)) | |
373 return foundChild; | |
374 | |
375 GlyphPageTreeNode* child = new GlyphPageTreeNode(this); | |
376 if (fontData->isCustomFont()) { | |
377 for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) | |
378 curr->m_customFontCount++; | |
379 } | |
380 | |
381 #if ENABLE(ASSERT) | |
382 child->m_pageNumber = m_pageNumber; | |
383 #endif | |
384 m_children.set(fontData, wrapUnique(child)); | |
385 fontData->setMaxGlyphPageTreeLevel( | |
386 max(fontData->maxGlyphPageTreeLevel(), child->m_level)); | |
387 child->initializePage(fontData, pageNumber); | |
388 return child; | |
389 } | |
390 | |
391 SystemFallbackGlyphPageTreeNode* GlyphPageTreeNode::getSystemFallbackChild( | |
392 unsigned pageNumber) { | |
393 ASSERT(pageNumber == m_pageNumber); | |
394 | |
395 if (m_systemFallbackChild) | |
396 return m_systemFallbackChild.get(); | |
397 | |
398 SystemFallbackGlyphPageTreeNode* child = | |
399 new SystemFallbackGlyphPageTreeNode(this); | |
400 m_systemFallbackChild = wrapUnique(child); | |
401 #if ENABLE(ASSERT) | |
402 child->m_pageNumber = m_pageNumber; | |
403 #endif | |
404 return child; | |
405 } | |
406 | |
407 void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData) { | |
408 if (!fontData || !m_customFontCount) | |
409 return; | |
410 | |
411 // Prune any branch that contains this FontData. | |
412 if (std::unique_ptr<GlyphPageTreeNode> node = m_children.take(fontData)) { | |
413 if (unsigned customFontCount = node->m_customFontCount + 1) { | |
414 for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) | |
415 curr->m_customFontCount -= customFontCount; | |
416 } | |
417 } | |
418 | |
419 // Check any branches that remain that still have custom fonts underneath | |
420 // them. | |
421 if (!m_customFontCount) | |
422 return; | |
423 | |
424 GlyphPageTreeNodeMap::iterator end = m_children.end(); | |
425 for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) | |
426 it->value->pruneCustomFontData(fontData); | |
427 } | |
428 | |
429 void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, | |
430 unsigned level) { | |
431 ASSERT(fontData); | |
432 | |
433 // Prune fall back child (if any) of this font. | |
434 if (m_systemFallbackChild) | |
435 m_systemFallbackChild->pruneFontData(fontData); | |
436 | |
437 // Prune from m_page if it's a mixed page. | |
438 if (m_page && m_page->hasPerGlyphFontData()) | |
439 m_page->removePerGlyphFontData(fontData); | |
440 | |
441 // Prune any branch that contains this FontData. | |
442 if (std::unique_ptr<GlyphPageTreeNode> node = m_children.take(fontData)) { | |
443 if (unsigned customFontCount = node->m_customFontCount) { | |
444 for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) | |
445 curr->m_customFontCount -= customFontCount; | |
446 } | |
447 } | |
448 | |
449 level++; | |
450 if (level > fontData->maxGlyphPageTreeLevel()) | |
451 return; | |
452 | |
453 GlyphPageTreeNodeMap::iterator end = m_children.end(); | |
454 for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) | |
455 it->value->pruneFontData(fontData, level); | |
456 } | |
457 | |
458 GlyphPage* SystemFallbackGlyphPageTreeNode::page(UScriptCode script) { | |
459 PageByScriptMap::iterator it = m_pagesByScript.find(script); | |
460 if (it != m_pagesByScript.end()) | |
461 return it->value.get(); | |
462 | |
463 RefPtr<GlyphPage> newPage = initializePage(); | |
464 m_pagesByScript.set(script, newPage); | |
465 return newPage.get(); | |
466 } | |
467 | |
468 void SystemFallbackGlyphPageTreeNode::pruneFontData( | |
469 const SimpleFontData* fontData) { | |
470 PageByScriptMap::iterator end = m_pagesByScript.end(); | |
471 for (PageByScriptMap::iterator it = m_pagesByScript.begin(); it != end; ++it) | |
472 it->value->removePerGlyphFontData(fontData); | |
473 } | |
474 | |
475 PassRefPtr<GlyphPage> SystemFallbackGlyphPageTreeNode::initializePage() { | |
476 // System fallback page is initialized with the parent's page, as individual | |
477 // entries may use different fonts depending on character. If the Font | |
478 // ever finds it needs a glyph out of the system fallback page, it will | |
479 // ask the system for the best font to use and fill that glyph in for us. | |
480 if (GlyphPage* parentPage = m_parent->page()) | |
481 return parentPage->createCopiedSystemFallbackPage(this); | |
482 return GlyphPage::createForMixedFontData(this); | |
483 } | |
484 | |
485 } // namespace blink | |
OLD | NEW |