Apple - Core Text Programming Guide

本文翻译整理自:Core Text Programming Guide(Updated: 2014-09-17
https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533


文章目录

  • 一、关于 Core Text
    • 1、概览
      • 核心文本布局文本
      • 您可以使用核心文本管理字体
    • 2、先决条件
    • 3、另见
  • 二、核心文本概述
    • 1、Core Text是一个基于C的平台中立API
    • 2、核心文本对象是C语言不透明类型
      • 核心文本不透明类型
      • 字体对象
      • 字体描述符
      • 字体集合
  • 三、通用文本布局操作
    • 1、布置一个段落
    • 2、简单文本标签
    • 3、柱状布局
    • 4、手动断线
    • 5、应用段落样式
    • 6、在非矩形区域中显示文本
  • 四、常用字体操作
    • 1、创建字体描述符
    • 2、从字体描述符创建字体
    • 3、创建相关字体
    • 4、序列化字体
    • 5、从序列化数据创建字体
    • 6、更改字距
    • 7、获取字符的字形


一、关于 Core Text

Core Text是一种用于布局文本和处理字体的高级低级技术。
Mac OS X v10.5和3.2iOS中引入的Core Text API可从所有OS X和iOS环境中访问。

重要提示: Core Text适用于必须在低级别进行文本布局和字体处理的开发人员,例如布局引擎的开发人员。
如果可能,您应该使用更高级别的框架开发您的应用程序——即在iOS中使用Text Kit(参见 文本编程指南iOS )或OS X中的Cocoa文本系统(参见 Cocoa文本架构指南 )。
Core Text是这些文本系统的底层技术,因此它们共享其速度和效率。
此外,Text Kit和Cocoa文本系统提供富文本编辑、全功能页面布局引擎和其他基础设施,如果您的应用程序单独使用Core Text,则需要提供这些基础设施。

在这里插入图片描述


1、概览

Core Text适用于需要与Core Graphics框架(Quartz)相关的低级文本处理技术的应用程序。
如果您直接使用Quartz并且需要绘制一些文本,请使用Core Text。
例如,如果您有自己的页面布局引擎——您有一些文本并且您知道它需要在视图中的位置——您可以使用Core Text生成字形并将它们相对定位,并具有精细排版的所有功能,例如字距调整、连字符、换行、断字和对齐。


核心文本布局文本

Core Text生成字形(来自字符代码和字体数据),并在字形运行中相对定位它们。
它将字形运行分解为行,并将行组装成多行框架(例如段落)。
Core Text还提供字形和布局相关数据,例如字形位置以及行和框架的测量。
它处理字符属性和段落样式,包括各种类型的标签样式和定位。

相关章节: 核心文本概述、常见文本布局操作


您可以使用核心文本管理字体

Core Text字体API提供字体、字体集合、字体描述符以及对字体数据的轻松访问。
它还提供对多种主字体、字体变体、字体级联和字体链接的支持。
Core Text提供了Quartz的替代方案,用于将您自己的字体加载到当前进程中,即字体激活。

相关章节: 常用字体操作


2、先决条件

要充分利用本文档,您应该了解文本系统和问题,并且应该知道如何使用Core Foundation不透明类型。
有关Core Foundation的信息,请参阅 Core Foundation设计概念


3、另见

除了本文档之外,还有几个文档涵盖了Core Text的更具体方面或描述了Core Text使用的软件服务。

  • 核心文本参考集合 为核心文本布局和字体API提供完整的参考信息。
  • CoreTextPageViewer (在iOS开发人员库中)展示了如何使用核心文本来显示大型文本。
  • DownloadFont (在iOS开发人员库中)演示了如何按需下载字体。
  • CoreTextRTF (在Mac Developer Library中)展示了如何使用Core Text在Cocoa应用程序的窗口中布局和绘制RTF内容。
  • 使用Core Text with Cocoa沿着路径绘图 (在Mac开发人员库中)展示了如何使用Core Text沿着曲线布局和绘制字形。
  • 核心基础设计概念核心基础框架参考 描述了核心基础,一个为核心文本使用的常见数据类型和基本软件服务提供抽象的框架。

以下章节(在iOS开发人员库中)描述了iOS中的Text Kit:

  • 绘图和管理文本中的 文本编程指南iOS 描述了iOS中的应用程序级文本处理系统。
  • 有关与核心文本和其他文本系统相关的排版概念的信息,请参阅 iOS文本编程指南 中的排版概念。

以下文档(在Mac开发者库中)提供了描述OS X中Cocoa文本系统的留档入口点:

  • Cocoa文本体系结构指南 介绍了Cocoa文本系统。
  • 文本布局编程指南 描述了Cocoa文本布局引擎。

二、核心文本概述

Core Text是一种用于布局文本和处理字体的高级低级技术。
Core Text直接与Core Graphics(CG)(也称为Quartz)一起工作,后者是高速图形渲染引擎,在OS X和iOS中处理最低级别的二维成像。

重要提示: Core Text专为开发更高级别的文本处理框架而设计。
一般应用程序开发人员应在iOS中使用Text Kit(参见 文本编程指南iOS )或OS X中的Cocoa文本系统(参见 Cocoa文本架构指南 )。

Core Text介于更高级别框架提供的文本布局和字体支持以及Quartz为所有文本和字体框架提供的低级功能之间。
Quartz框架作用于字形及其位置。
Core Text知道字符如何映射到字体,并在调用Quartz渲染文本之前考虑有关样式、字体指标和其他属性的信息。
Quartz是在基本级别绘制字形的唯一方法,并且由于Core Text以Quartz直接可用的形式提供所有数据,因此结果是高性能的文本渲染。

多线程:如果客户端不改变任何参数,例如线程之间共享的属性字符串,则可以同时从多个线程调用Core Text函数。


1、Core Text是一个基于C的平台中立API

核心文本API在iOS和OS X上几乎相同,尽管OS X版本提供了一组更丰富的字体管理API,包括可变的字体集合。
但是,在平台之间移植代码时,必须考虑UIKit和AppKit之间的差异。
例如,您必须有一个Quartz图形上下文来渲染核心文本生成的字形,并且您在每个平台上获得的图形上下文不同。
您在iOS中绘制的视图在iOS中是UIView子类,在OS X中是NSView子类别。
您应该注意,CGRect对象被传递到UIView``drawRect:方法中,而drawRect:的OS X版本被传递到一个NSRect对象中。
(您可以使用OS X中的NSRectToCGRect函数将传入的NSRect对象转换为核心文本函数参数所需的`CGRect’对象。)

UIView函数UIGraphicsGetCurrentContext返回的图形上下文相对于未经修改的Quartz图形上下文是翻转的(也就是说,UIView返回的上下文的原点在左上角),因此您必须在iOS中而不是在OS X中重新翻转图形上下文。
有关此技术的代码示例,请参见例2-1。

核心文本尽可能使用系统数据类型和服务,并且您可以使用与OS X和iOS中的其他核心框架相关的相同约定。
例如,核心文本对许多输入和输出参数使用核心基础对象,因此您可以将它们存储在核心基础集合类中。
核心文本处理的其他对象,如CGPath对象,由核心图形框架提供。


2、核心文本对象是C语言不透明类型

为了快速和简单,OS X和iOS中的许多低级库都是用普通C编写的。
使用Core Text时,您使用一组C函数,例如CTFramesetterCreateWithAttributedStringCTFramesetterCreateFrame,而不是Objective-C类和方法。


核心文本不透明类型

核心文本布局引擎通常使用属性字符串(CFAttributedStringRef)和图形路径(CGPathRef)。
属性字符串对象封装支持显示文本的字符串,并包含定义字符串中字符样式方面的属性(或“属性”),例如字体和颜色。
核心文本中的排版机制使用属性字符串中的信息来执行字符到字形的转换。


图形路径定义文本帧的形状。
在OS X v10.7和iOS3.2及更高版本中,路径可以是非矩形的。

cft引用类型,CFAttributedStringRef,与它的Foundation对应物NSAttributedString是免费桥接的。
这意味着Core Foundation类型在函数或方法调用中可以与桥接的Foundation对象互换。
因此,在一个方法中,您可以看到一个NSAttributedString *参数,您可以传入一个CFAttributedStringRef,而在一个函数中,您可以看到一个CFAttributedStringRef参数,您可以传入一个NSAttributedString实例。
(您可能需要将一种类型转换为另一种类型以抑制编译器警告。)这也适用于NSAttributedString的具体子类。

这些属性是定义字符串中字符的样式特征的键值对,这些字符被分组在共享相同属性的范围内。
属性本身被传递到属性字符串中,并使用CFDicizard对象从中检索。
要将样式应用于字形运行(CTRun对象),请创建一个CFDicizard对象来保存要应用的属性,然后创建一个属性字符串,将字典作为参数传递。
或者,您可以将属性应用于已经存在的CFMutableAttributedString对象。
尽管CFDictionaryRefNSDictionary是免费桥接的,但存储在字典中的单个属性对象可能不是。

运行时的核心文本对象形成层次结构,如图1-1所示。
该层次结构的顶部是框架设置器对象(CTFramesetterRef)。
以属性字符串和图形路径作为输入,框架设置器生成一个或多个文本帧(CTFrameRef)。
每个CTFrame对象代表一个段落。


图1-1 Core Text布局引擎的架构
在这里插入图片描述

为了生成框架,排帧器调用排字器对象(CTTypesetterRef)。
当它在框架中布置文本时,排帧器会对其应用段落样式,包括对齐、制表符、行距、缩进和换行模式等属性。
排字器将属性字符串中的字符转换为字形,并将这些字形放入填充文本框架的行中。

每个CTFrame对象都包含段落的行(CTLine)对象。
每个行对象代表一行文本。
CTFrame对象可能只包含一个长的CTLine对象,也可能包含一组行。
行对象由排字员在框架设置操作期间创建,并且像框架一样,可以将自己直接绘制到图形上下文中。

每个CTLine对象都包含一个字形运行(CTRun)对象数组。
字形运行是一组共享相同属性和方向的连续字形。
排字器在从字符串、属性和字体对象生成行时创建字形运行。
这意味着一行由一个或多个字形运行构成。
如果需要,字形运行可以将自己绘制到图形上下文中,尽管大多数客户端不需要直接与字形运行交互。


字体对象

字体有助于相对于彼此排列字形,并用于在图形上下文中绘图时建立当前字体。
Core Text字体不透明类型CTFont是一个特定的字体实例,它封装了大量信息。
它的引用类型CTFontRef是免费桥接的UIFont在iOS和NSFont在OS X中。
当您创建CTFont对象时,您通常会指定(或使用默认)点大小和转换矩阵,这会给出字体实例的特定特征。
然后,您可以查询字体对象以获取有关该特定点大小的字体的各种信息,例如字符到字形的映射、编码、字体度量数据和字形数据等。
字体度量是诸如上升、下降、领先、上限高度、x高度等参数。
字形数据包括边界矩形和字形前进等参数。

Core Text字体对象是不可变的,因此它们可以被多个操作、工作队列或线程同时使用。
有许多方法可以创建字体对象。
首选方法是从字体描述符使用CTFontCreateWithFontDescriptor
您还可以使用许多转换API,具体取决于您必须从什么开始。
例如,您可以使用字体的PostScript名称(CTFontCreateWithName)或Core Graphics字体引用(CTFontCreateWithGraphicsFont)。
还有CTFontCreateUIFontForLanguage,它为您正在使用的本地化中的应用程序的用户界面字体创建引用。

Core Text字体引用提供了一种复杂的自动字体替换机制,称为字体级联,它选择适当的字体来替换缺失的字体,同时考虑字体特征。
字体级联基于级联列表,级联列表是有序字体描述符的数组。
有一个系统默认级联列表(它是多态的,基于用户的语言设置和当前字体)和一个在字体创建时指定的字体级联列表。
使用字体描述符中的信息,级联机制可以根据样式匹配字体以及匹配字符。
CTFontCreateForString函数使用级联列表来选择适当的字体来编码给定的字符串。
要指定和检索字体级联列表,请使用kCTFontCascadeListAttribute属性。


字体描述符

由CTFontDescriptor不透明类型表示的字体描述符提供了一种完全根据属性字典描述字体的机制,以及用于构建新字体的易于使用的字体匹配工具。
您可以从字体描述符中创建字体对象,也可以从字体对象中获取描述符,还可以更改描述符并使用它来创建新的字体对象。
您可以通过创建字体描述符来部分描述字体,例如,仅使用家族名称或权重,然后可以在系统上找到与给定特征匹配的所有字体。
CTFontDescriptorRef类型免费桥接到iOS中的UIFontDescriptor和OS X中的NSFontDescriptor

您无需处理复杂的转换矩阵,而是可以创建字体属性字典,其中包括PostScript名称、字体系列和样式以及特征(例如,粗体或斜体字)等属性作为CTFontDescriptor对象。
您可以使用字体描述符创建CTFont对象。
字体描述符可以序列化并存储在文档中,以提供字体的持久性。
图1-2说明了使用字体描述符创建特定字体实例的字体系统。


图1-2从字体描述符创建字体

在这里插入图片描述

您可以将字体描述符视为对字体系统的查询。
您可以创建一个规范不完整的字体描述符,也就是说,在属性字典中只有一个或几个值,系统将从可用的字体中选择最合适的字体。
例如,如果您使用描述符对具有标准面孔(正常、粗体、斜体、粗体斜体字)的家族名称进行查询,不指定任何特征将匹配家族中的所有面孔,但是如果您指定一个特征字典,其kCTFontTraitsAttributekCTFontTraitBold,结果将从整个家族进一步缩小到满足粗体特征的成员。
系统可以通过CTFontDescriptorCreateMatchingFontDescriptors.

在iOS6.0及更高版本中,应用程序可以按需下载未使用CTFontDescriptorMatchFontDescriptorsWithProgressHandler 功能安装的可用字体。
以这种方式下载的字体不会永久安装,系统可能会在某些情况下将其删除。
可供下载的字体列在“附加信息”中iOS6:字体列表和iOS7:字体列表。
下载字体 (在iOS开发者库中)演示了下载技术。
在OS X中不需要按需下载字体,因为所有可用字体都随系统安装。


字体集合

字体集合是作为单个对象的字体描述符组。
字体集合由CTFontCollection不透明类型表示。
字体集合提供了字体枚举、访问全局和自定义字体集合以及访问包含该集合的字体描述符的功能。
例如,您可以通过调用CTFontCollectionCreateFromAvailableFonts来创建系统中所有可用字体的字体集合,并且可以使用该集合来获取所有成员字体描述符的数组。


三、通用文本布局操作

本章描述了一些常见的文本布局操作,并展示了如何使用Core Text执行它们。
本章包含以下带有代码列表的操作:

  • 布置一个段落
  • 简单文本标签
  • 柱状布局
  • 手动断线
  • 应用段落样式
  • 在非矩形区域中显示文本

1、布置一个段落

排版中最常见的操作之一是在任意大小的矩形区域内布局多行段落。
Core Text使此操作变得简单,只需要几行Core Text特定的代码。
要布局段落,您需要一个图形上下文来绘制,一个矩形路径来提供文本布局的区域,以及一个属性字符串。
本例中的大部分代码都需要创建和初始化上下文、路径和字符串。
完成后,Core Text只需要三行代码来进行布局。

在[例2-1] 中的代码显示了一个段落是如何布局的,这段代码可以驻留在UIView子类(OS X中的NSView子类)的drawRect:方法中。


例2-1排版一个简单的段落

// Initialize a graphics context in iOS.
CGContextRef context = UIGraphicsGetCurrentContext();
 
// Flip the context coordinates, in iOS only.
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
 
// Initializing a graphic context in OS X is different:
// CGContextRef context =
//     (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
// Set the text matrix.
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
// Create a path which bounds the area where you will be drawing text.
// The path need not be rectangular.
CGMutablePathRef path = CGPathCreateMutable();
 
// In this simple example, initialize a rectangular path.
CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0);
CGPathAddRect(path, NULL, bounds );
 
// Initialize a string.
CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");
 
// Create a mutable attributed string with a max length of 0.
// The max length is a hint as to how much internal storage to reserve.
// 0 means no hint.
CFMutableAttributedStringRef attrString =
         CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
 
// Copy the textString into the newly created attrString
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0),
         textString);
 
// Create a color that will be added as an attribute to the attrString.
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
CGColorRef red = CGColorCreate(rgbColorSpace, components);
CGColorSpaceRelease(rgbColorSpace);
 
// Set the color of the first 12 chars to red.
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12),
         kCTForegroundColorAttributeName, red);
 
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter =
         CTFramesetterCreateWithAttributedString(attrString);
CFRelease(attrString);
 
// Create a frame.
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
          CFRangeMake(0, 0), path, NULL);
 
// Draw the specified frame in the given context.
CTFrameDraw(frame, context);
 
// Release the objects we used.
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);


2、简单文本标签

另一个常见的排版操作是绘制单行文本以用作用户界面元素的标签。
在Core Text中,这只需要两行代码:一行用于创建带有CF属性字符串的行对象,另一行用于将该行绘制到图形上下文中。

例2-2显示了如何在UIViewNSView子类的drawRect:方法中完成此操作。
该例省略了纯文本字符串、字体和图形上下文的初始化,以及本文档其他例中显示的操作。
它显示了如何创建属性字典并使用它来创建属性字符串。
(字体创建显示在创建字体描述符和从字体描述符创建字体中。)


例2-2排版一个简单的文本标签

CFStringRef string; CTFontRef font; CGContextRef context;
// Initialize the string, font, and context
 
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };
 
CFDictionaryRef attributes =
    CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
        (const void**)&values, sizeof(keys) / sizeof(keys[0]),
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks);
 
CFAttributedStringRef attrString =
    CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CFRelease(string);
CFRelease(attributes);
 
CTLineRef line = CTLineCreateWithAttributedString(attrString);
 
// Set text position and draw the line into the graphics context
CGContextSetTextPosition(context, 10.0, 10.0);
CTLineDraw(line, context);
CFRelease(line);


3、柱状布局

在多列中布局文本是另一种常见的排版操作。
严格来说,Core Text本身一次只布局一列,不计算列大小或位置。
您可以在调用Core Text以在您计算的路径区域内布局文本之前执行这些操作。
在此示例中,Core Text除了在每列中布局文本外,还为每列提供文本字符串内的子范围。


例[2-3] 中的createColumnsWithColumnCount:方法接受要绘制的列数作为参数,并返回一个路径数组,每列一个路径。

[例2-4] 包括一个drawRect:method的实现,它调用本地createColumnsWithColumnCount方法,该方法在此例中首先定义。
此代码位于UIView子类(OS X中的NSView子类)中。
该子类包括一个attributedString,此处未显示该属性,但在此例中调用其访问器以返回要布局的属性字符串。


例2-3将视图划分为列

- (CFArrayRef)createColumnsWithColumnCount:(int)columnCount
{
    int column;
 
    CGRect* columnRects = (CGRect*)calloc(columnCount, sizeof(*columnRects));
    // Set the first column to cover the entire view.
    columnRects[0] = self.bounds;
 
    // Divide the columns equally across the frame's width.
    CGFloat columnWidth = CGRectGetWidth(self.bounds) / columnCount;
    for (column = 0; column < columnCount - 1; column++) {
        CGRectDivide(columnRects[column], &columnRects[column],
                     &columnRects[column + 1], columnWidth, CGRectMinXEdge);
    }
 
   // Inset all columns by a few pixels of margin.
    for (column = 0; column < columnCount; column++) {
        columnRects[column] = CGRectInset(columnRects[column], 8.0, 15.0);
    }
 
    // Create an array of layout paths, one for each column.
    CFMutableArrayRef array =
                     CFArrayCreateMutable(kCFAllocatorDefault,
                                  columnCount, &kCFTypeArrayCallBacks);
 
    for (column = 0; column < columnCount; column++) {
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, columnRects[column]);
        CFArrayInsertValueAtIndex(array, column, path);
        CFRelease(path);
    }
    free(columnRects);
    return array;
}


Listing 2-4 Performing columnar text layout

// Override drawRect: to draw the attributed string into columns.
// (In OS X, the drawRect: method of NSView takes an NSRect parameter,
//  but that parameter is not used in this listing.)
- (void)drawRect:(CGRect)rect
{
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Initializing a graphic context in OS X is different:
    // CGContextRef context =
    //     (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    // Create the framesetter with the attributed string.
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
                                      (CFAttributedStringRef)self.attributedString);
 
    // Call createColumnsWithColumnCount function to create an array of
    // three paths (columns).
    CFArrayRef columnPaths = [self createColumnsWithColumnCount:3];
 
    CFIndex pathCount = CFArrayGetCount(columnPaths);
    CFIndex startIndex = 0;
    int column;
 
    // Create a frame for each column (path).
    for (column = 0; column < pathCount; column++) {
        // Get the path for this column.
        CGPathRef path = (CGPathRef)CFArrayGetValueAtIndex(columnPaths, column);
 
        // Create a frame for this column and draw it.
        CTFrameRef frame = CTFramesetterCreateFrame(
                             framesetter, CFRangeMake(startIndex, 0), path, NULL);
        CTFrameDraw(frame, context);
 
        // Start the next frame at the first character not visible in this frame.
        CFRange frameRange = CTFrameGetVisibleStringRange(frame);
        startIndex += frameRange.length;
        CFRelease(frame);
 
    }
    CFRelease(columnPaths);
    CFRelease(framesetter);
 
}


4、手动断线

在Core Text中,您通常不需要手动换行,除非您有特殊的断字过程或类似的要求。
排版器自动执行换行。
或者,Core Text使您能够准确指定每行文本的换行位置。
[例2-5] 显示了如何创建排版器,排版器使用的对象,并直接使用排版器来查找适当的换行并手动创建排版线。
此示例还显示了如何在绘图前居中。

此代码可能在UIView子类(OS X中的NSView子类)的drawRect:方法中。


例2-5执行手动换行

double width; CGContextRef context; CGPoint textPosition; CFAttributedStringRef attrString;
// Initialize those variables.
 
// Create a typesetter using the attributed string.
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(attrString);
 
// Find a break for line from the beginning of the string to the given width.
CFIndex start = 0;
CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, width);
 
// Use the returned character count (to the break) to create the line.
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count));
 
// Get the offset needed to center the line.
float flush = 0.5; // centered
double penOffset = CTLineGetPenOffsetForFlush(line, flush, width);
 
// Move the given text drawing position by the calculated offset and draw the line.
CGContextSetTextPosition(context, textPosition.x + penOffset, textPosition.y);
CTLineDraw(line, context);
 
// Move the index beyond the line break.
start += count;


5、应用段落样式

例2-6实现了一个将段落样式应用于属性字符串的函数。
该函数接受字体名称、点大小和增加或减少文本行间距的行距作为参数。
该函数由[例2-7] 中的代码调用,该代码创建一个纯文本字符串,使用applyParaStyle函数使用给定的段落属性创建一个属性字符串,然后创建一个框架和框架,并绘制框架。


例2-6应用段落样式

NSAttributedString* applyParaStyle(
                CFStringRef fontName , CGFloat pointSize,
                NSString *plainText, CGFloat lineSpaceInc){
 
    // Create the font so we can determine its height.
    CTFontRef font = CTFontCreateWithName(fontName, pointSize, NULL);
 
    // Set the lineSpacing.
    CGFloat lineSpacing = (CTFontGetLeading(font) + lineSpaceInc) * 2;
 
    // Create the paragraph style settings.
    CTParagraphStyleSetting setting;
 
    setting.spec = kCTParagraphStyleSpecifierLineSpacing;
    setting.valueSize = sizeof(CGFloat);
    setting.value = &lineSpacing;
 
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&setting, 1);
 
    // Add the paragraph style to the dictionary.
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                               (__bridge id)font, (id)kCTFontNameAttribute,
                               (__bridge id)paragraphStyle,
                               (id)kCTParagraphStyleAttributeName, nil];
    CFRelease(font);
    CFRelease(paragraphStyle);
 
    // Apply the paragraph style to the string to created the attributed string.
    NSAttributedString* attrString = [[NSAttributedString alloc]
                               initWithString:(NSString*)plainText
                               attributes:attributes];
 
    return attrString;
}

在[例2-7] 中,样式字符串用于创建帧表,代码使用帧表创建帧并绘制帧。


例2-7绘制样式段落

- (void)drawRect:(CGRect)rect {
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    CFStringRef fontName = CFSTR("Didot Italic");
    CGFloat pointSize = 24.0;
 
    CFStringRef string = CFSTR("Hello, World! I know nothing in the world that has
                                   as much power as a word. Sometimes I write one,
                                   and I look at it, until it begins to shine.");
 
    // Apply the paragraph style.
    NSAttributedString* attrString = applyParaStyle(fontName, pointSize, string, 50.0);
 
    // Put the attributed string with applied paragraph style into a framesetter.
    CTFramesetterRef framesetter =
             CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
 
    // Create a path to fill the View.
    CGPathRef path = CGPathCreateWithRect(rect, NULL);
 
    // Create a frame in which to draw.
    CTFrameRef frame = CTFramesetterCreateFrame(
                                    framesetter, CFRangeMake(0, 0), path, NULL);
 
    // Draw the frame.
    CTFrameDraw(frame, context);
    CFRelease(frame);
    CGPathRelease(path);
    CFRelease(framesetter);
}

在OS X中,NSView drawRect:方法接收NSRect参数,但CGPathCreateWithRect函数需要CGRect参数。
因此,必须使用以下函数调用将NSRect对象转换为CGRect对象:

CGRect myRect = NSRectToCGRect([self bounds]);

此外,在OS X中,您获得的图形上下文不同,并且不会翻转其坐标,如[例2-7] 中的注释所示。


6、在非矩形区域中显示文本

在非矩形区域中显示文本的难点是描述非矩形路径。
[例2-8] 中的AddSquashedDonutPath函数返回一个甜甜圈形状的路径。
一旦有了路径,只需调用通常的Core Text函数来应用属性并绘制。


例2-8在非矩形路径中显示文本

// Create a path in the shape of a donut.
static void AddSquashedDonutPath(CGMutablePathRef path,
              const CGAffineTransform *m, CGRect rect)
{
    CGFloat width = CGRectGetWidth(rect);
    CGFloat height = CGRectGetHeight(rect);
 
    CGFloat radiusH = width / 3.0;
    CGFloat radiusV = height / 3.0;
 
    CGPathMoveToPoint( path, m, rect.origin.x, rect.origin.y + height - radiusV);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y + height,
                               rect.origin.x + radiusH, rect.origin.y + height);
    CGPathAddLineToPoint( path, m, rect.origin.x + width - radiusH,
                               rect.origin.y + height);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width,
                               rect.origin.y + height,
                               rect.origin.x + width,
                               rect.origin.y + height - radiusV);
    CGPathAddLineToPoint( path, m, rect.origin.x + width,
                               rect.origin.y + radiusV);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width, rect.origin.y,
                               rect.origin.x + width - radiusH, rect.origin.y);
    CGPathAddLineToPoint( path, m, rect.origin.x + radiusH, rect.origin.y);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y,
                               rect.origin.x, rect.origin.y + radiusV);
    CGPathCloseSubpath( path);
 
    CGPathAddEllipseInRect( path, m,
                            CGRectMake( rect.origin.x + width / 2.0 - width / 5.0,
                            rect.origin.y + height / 2.0 - height / 5.0,
                            width / 5.0 * 2.0, height / 5.0 * 2.0));
}
 
// Generate the path outside of the drawRect call so the path is calculated only once.
- (NSArray *)paths
{
    CGMutablePathRef path = CGPathCreateMutable();
    CGRect bounds = self.bounds;
    bounds = CGRectInset(bounds, 10.0, 10.0);
    AddSquashedDonutPath(path, NULL, bounds);
 
    NSMutableArray *result =
              [NSMutableArray arrayWithObject:CFBridgingRelease(path)];
    return result;
}
 
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
 
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    // Initialize an attributed string.
    CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that
    has as much power as a word. Sometimes I write one, and I look at it,
    until it begins to shine.");
 
    // Create a mutable attributed string.
     CFMutableAttributedStringRef attrString =
                CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
 
    // Copy the textString into the newly created attrString.
    CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);
 
    // Create a color that will be added as an attribute to the attrString.
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
    CGColorRef red = CGColorCreate(rgbColorSpace, components);
    CGColorSpaceRelease(rgbColorSpace);
 
    // Set the color of the first 13 chars to red.
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13),
                                     kCTForegroundColorAttributeName, red);
 
    // Create the framesetter with the attributed string.
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
 
    // Create the array of paths in which to draw the text.
    NSArray *paths = [self paths];
 
    CFIndex startIndex = 0;
 
    // In OS X, use NSColor instead of UIColor.
    #define GREEN_COLOR [UIColor greenColor]
    #define YELLOW_COLOR [UIColor yellowColor]
    #define BLACK_COLOR [UIColor blackColor]
 
    // For each path in the array of paths...
    for (id object in paths) {
        CGPathRef path = (__bridge CGPathRef)object;
 
        // Set the background of the path to yellow.
        CGContextSetFillColorWithColor(context, [YELLOW_COLOR CGColor]);
 
        CGContextAddPath(context, path);
        CGContextFillPath(context);
 
        CGContextDrawPath(context, kCGPathStroke);
 
        // Create a frame for this path and draw the text.
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                         CFRangeMake(startIndex, 0), path, NULL);
        CTFrameDraw(frame, context);
 
        // Start the next frame at the first character not visible in this frame.
        CFRange frameRange = CTFrameGetVisibleStringRange(frame);
        startIndex += frameRange.length;
        CFRelease(frame);
}
 
CFRelease(attrString);
CFRelease(framesetter);
}

四、常用字体操作

本章介绍了一些常见的字体处理操作,并展示了如何使用Core Text对它们进行编码。
这些操作在iOS和OS X上是相同的。
本章包含以下带有代码列表的操作:

  • 创建字体描述符
  • 从字体描述符创建字体
  • 创建相关字体
  • 序列化字体
  • 从序列化数据创建字体
  • 更改字距
  • 获取字符的字形

1、创建字体描述符


例3-1中的示例函数从指定PostScript字体名称和点大小的参数值创建字体描述符。


例3-1根据名称和点大小创建字体描述符

CTFontDescriptorRef CreateFontDescriptorFromName(CFStringRef postScriptName,
                                                  CGFloat size)
{
    return CTFontDescriptorCreateWithNameAndSize(postScriptName, size);
} 

例3-2中的示例函数根据字体系列名称和字体特征创建字体描述符。


例3-2从系列和特征创建字体描述符

NSString* familyName = @"Papyrus";
CTFontSymbolicTraits symbolicTraits = kCTFontTraitCondensed;
CGFloat size = 24.0;
 
NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
[attributes setObject:familyName forKey:(id)kCTFontFamilyNameAttribute];
 
// The attributes dictionary contains another dictionary, the traits dictionary,
// which in this example specifies only the symbolic traits.
NSMutableDictionary* traits = [NSMutableDictionary dictionary];
[traits setObject:[NSNumber numberWithUnsignedInt:symbolicTraits]
                                           forKey:(id)kCTFontSymbolicTrait];
 
[attributes setObject:traits forKey:(id)kCTFontTraitsAttribute];
[attributes setObject:[NSNumber numberWithFloat:size]
                                         forKey:(id)kCTFontSizeAttribute];
 
CTFontDescriptorRef descriptor =
             CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes);
CFRelease(descriptor);


2、从字体描述符创建字体

例3-3显示了如何创建字体描述符并使用它来创建字体。
当调用CTFontCreateWithFontDescriptor时,通常为矩阵参数传递NULL以指定默认(单位)矩阵。
CTFontCreateWithFontDescriptor的大小和矩阵(第二和第三)参数覆盖字体描述符中指定的任何参数,除非它们未指定(大小为0.0,矩阵为NULL)。


例3-3从字体描述符创建字体

NSDictionary *fontAttributes =
                  [NSDictionary dictionaryWithObjectsAndKeys:
                          @"Courier", (NSString *)kCTFontFamilyNameAttribute,
                          @"Bold", (NSString *)kCTFontStyleNameAttribute,
                          [NSNumber numberWithFloat:16.0],
                          (NSString *)kCTFontSizeAttribute,
                          nil];
// Create a descriptor.
CTFontDescriptorRef descriptor =
          CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes);
 
// Create a font using the descriptor.
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);
CFRelease(descriptor);


3、创建相关字体

将现有字体转换为相关或相似的字体通常很有用。
例3-4中的示例函数展示了如何根据函数调用传递的布尔参数的值将字体设为粗体或非粗体。
如果当前字体系列没有请求的样式,则函数返回NULL


例3-4更改字体的特征

CTFontRef CreateBoldFont(CTFontRef font, Boolean makeBold)
{
    CTFontSymbolicTraits desiredTrait = 0;
    CTFontSymbolicTraits traitMask;
 
    // If requesting that the font be bold, set the desired trait
    // to be bold.
    if (makeBold) desiredTrait = kCTFontBoldTrait;
 
    // Mask off the bold trait to indicate that it is the only trait
    // to be modified. As CTFontSymbolicTraits is a bit field,
    // could change multiple traits if desired.
    traitMask = kCTFontBoldTrait;
 
    // Create a copy of the original font with the masked trait set to the
    // desired value. If the font family does not have the appropriate style,
    // returns NULL.
 
    return CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, desiredTrait, traitMask);
}

在例3-8中的示例函数将给定字体转换为另一个字体系列中的类似字体,如果可能的话保留特征。
它可能返回NULL
大小参数传入0.0,矩阵参数传入NULL会保留原始字体的大小。


例3-5将字体转换为另一个系列

CTFontRef CreateFontConvertedToFamily(CTFontRef font, CFStringRef family)
{
    // Create a copy of the original font with the new family. This call
    // attempts to preserve traits, and may return NULL if that is not possible.
    // Pass in 0.0 and NULL for size and matrix to preserve the values from
    // the original font.
 
    return CTFontCreateCopyWithFamily(font, 0.0, NULL, family);
}


4、序列化字体

在例3-6中的示例函数展示了如何创建XML数据来序列化可以嵌入到文档中的字体。
或者,最好使用NSArchiver
这只是完成此任务的一种方法,但它保留了以后重新创建确切字体所需的字体中的所有数据。


例3-6序列化字体

CFDataRef CreateFlattenedFontData(CTFontRef font)
{
    CFDataRef           result = NULL;
    CTFontDescriptorRef descriptor;
    CFDictionaryRef     attributes;
 
    // Get the font descriptor for the font.
    descriptor = CTFontCopyFontDescriptor(font);
 
    if (descriptor != NULL) {
        // Get the font attributes from the descriptor. This should be enough
        // information to recreate the descriptor and the font later.
        attributes = CTFontDescriptorCopyAttributes(descriptor);
 
        if (attributes != NULL) {
            // If attributes are a valid property list, directly flatten
            // the property list. Otherwise we may need to analyze the attributes
            // and remove or manually convert them to serializable forms.
            // This is left as an exercise for the reader.
           if (CFPropertyListIsValid(attributes, kCFPropertyListXMLFormat_v1_0)) {
                result = CFPropertyListCreateXMLData(kCFAllocatorDefault, attributes);
            }
        }
    }
    return result;
}


5、从序列化数据创建字体


例3-7中的示例函数展示了如何从扁平化的XML数据创建字体引用。
它展示了如何取消扁平化字体属性并使用这些属性创建字体。


例3-7从序列化数据创建字体

CTFontRef CreateFontFromFlattenedFontData(CFDataRef iData)
{
    CTFontRef           font = NULL;
    CFDictionaryRef     attributes;
    CTFontDescriptorRef descriptor;
 
    // Create our font attributes from the property list.
    // For simplicity, this example creates an immutable object.
    // If you needed to massage or convert certain attributes
    // from their serializable form to the Core Text usable form,
    // do it here.
    attributes =
          (CFDictionaryRef)CFPropertyListCreateFromXMLData(
                               kCFAllocatorDefault,
                               iData, kCFPropertyListImmutable, NULL);
    if (attributes != NULL) {
        // Create the font descriptor from the attributes.
        descriptor = CTFontDescriptorCreateWithAttributes(attributes);
        if (descriptor != NULL) {
            // Create the font from the font descriptor. This sample uses
            // 0.0 and NULL for the size and matrix parameters. This
            // causes the font to be created with the size and/or matrix
            // that exist in the descriptor, if present. Otherwise default
            // values are used.
            font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);
        }
    }
    return font;
}


6、更改字距

默认情况下启用连词和字距调整。
要禁用,请将kCTKernAttributeName属性设置为0
例3-8将绘制的前几个字符的kern大小设置为大数字。


例3-8设置字距

// Set the color of the first 13 characters to red
 // using a previously defined red CGColor object.
 CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13),
                                      kCTForegroundColorAttributeName, red);
 
 // Set kerning between the first 18 chars to be 20
 CGFloat otherNum = 20;
 CFNumberRef otherCFNum = CFNumberCreate(NULL, kCFNumberCGFloatType, &otherNum);
 CFAttributedStringSetAttribute(attrString, CFRangeMake(0,18),
                                           kCTKernAttributeName, otherCFNum);


7、获取字符的字形


例3-9展示了如何为具有单一字体的字符串中的字符获取字形。
大多数时候,您应该只使用CTLine对象来获取此信息,因为一种字体可能不会对整个字符串进行编码。
此外,简单的字符到字形映射不会为复杂的脚本获得正确的外观。
如果您尝试为字体显示特定的Unicode字符,这种简单的字形映射可能是合适的。


例3-9获取字符的字形

void GetGlyphsForCharacters(CTFontRef font, CFStringRef string)
{
    // Get the string length.
    CFIndex count = CFStringGetLength(string);
 
    // Allocate our buffers for characters and glyphs.
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar) * count);
    CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * count);
 
    // Get the characters from the string.
    CFStringGetCharacters(string, CFRangeMake(0, count), characters);
 
    // Get the glyphs for the characters.
    CTFontGetGlyphsForCharacters(font, characters, glyphs, count);
 
    // Do something with the glyphs here. Characters not mapped by this font will be zero.
    // ...
 
    // Free the buffers
    free(characters);
    free(glyphs);
}

2024-06-16(日)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/728712.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker将容器打包提交为镜像,再打包成tar包

将容器打包成镜像可以通过以下步骤来实现。这里以 Docker 为例&#xff0c;假设你已经安装了 Docker 并且有一个正在运行的容器。 1. 找到正在运行的容器 首先&#xff0c;你需要找到你想要打包成镜像的容器的 ID 或者名字。可以使用以下命令查看所有正在运行的容器&#xff…

高速异地组网怎么办理?

在当今信息化时代&#xff0c;跨地域的远程办公、远程教育、远程医疗等需求越来越多。而高速异地组网作为一种解决不同地区之间快速组建局域网的方法&#xff0c;被广泛应用。本文将介绍一款异地组网内网穿透产品——【天联】&#xff0c;并提供其办理流程。 【天联】组网是什…

【系统设计】如何权衡范式与反范式设计

一、什么是范式设计与反范式设计 1.1、范式设计&#xff08;Normalization&#xff09; 定义&#xff1a; 范式设计是数据库设计中最基础的设计原则之一&#xff0c;它主要通过规范化数据模型&#xff0c;减少数据冗余和数据不一致的问题。 常用的范式&#xff1a; 第一范式…

Android Studio main,xml 视图代码转换

Android Studio main,xml 视图&&代码转换 其实很简单,但是对我们小白来说还是比较蒙的。 废话不多说,直接上图。 我的Android Studio 是 4.0 版的 我刚打开是这个界面,在我想学习如何用代码来布局,可能大家也会找不见代码的位置。 follow me 是不是感觉很简单呢。…

基于DE2-115平台的VGA显示实验

一.任务需求 深入了解VGA协议&#xff0c;理解不同显示模式下的VGA控制时序参数&#xff08;行频、场频、水平/垂直同步时钟周期、显示后沿/前沿等概念和计算方式&#xff09;&#xff1b;通过Verilog编程&#xff0c;在至少2种显示模式下&#xff08;64048060Hz,102476875Hz&…

Day14——Python文本挖掘数据分析

文章目录 竞争分析-品类分布-适用对象竞争分析-产品结构-拜耳在这里插入图片描述竞争分析-产品结构-拜耳-BCG图竞争分析-产品结构-拜耳-明星竞争分析-产品结构-拜耳-奶牛竞争分析-产品结构-拜耳-问题竞争分析-产品结构-安速-BCG图竞争分析-产品结构-安速-明星竞争分析-产品结构…

vue3项目使用Electron打包成exe的方法与打包报错解决

将vue3项目打包成exe文件方法 一、安装 1.安装electron npm install electron --save-devnpm install electron-builder --save-dev 2.在vue项目根目录新建文件index.js // index.js// Modules to control application life and create native browser window const { app…

Python日志管理利器:如何高效管理平台日志

一、为什么需要日志管理&#xff1f; 日志是应用程序的重要组成部分&#xff0c;它记录了应用程序的运行状态、错误信息以及用户交互等关键信息。良好的日志管理可以帮助开发人员及时发现和解决问题&#xff0c;提高应用程序的稳定性和可靠性。 项目在本地开发调试时&#xf…

AGI的多模态融合

在人工智能的宏伟蓝图中&#xff0c;人工通用智能&#xff08;AGI&#xff09;代表着一个集大成者&#xff0c;一个能够理解、学习、适应并执行任何智能任务的系统。随着我们对AGI的探索愈发深入&#xff0c;尤其是在视觉、语言和其他模态的融合上&#xff0c;关于AGI的讨论愈发…

详解DAC数模转换+DAC输出模拟电压的测量比对实验程序

前言&#xff1a;详解DAC数模转换原理DAC输出模拟电压的测量比对实验程序&#xff08;使用 DAC 通道 1 输出模拟电压&#xff0c;然后通过 ADC1 的通道 1 对该输出电压进行读取&#xff0c;并显示在 LCD 模块上面&#xff0c;DAC 的输出电压可以通过按键&#xff08;或 USMART&…

PostgreSQL的学习心得和知识总结(一百四十五)|深入理解PostgreSQL数据库之ShowTransactionState的使用及父子事务有限状态机

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

Kubernetes部署Kanboard看板管理平台

【云原生】Kubernetes部署Kanboard项目管理平台 文章目录 【云原生】Kubernetes部署Kanboard项目管理平台介绍资源列表基础环境一、检查k8s环境1.1、检查工作节点状态1.2、检查系统pod状态 二、编辑kanboard.yaml文件2.1、创建项目目录2.2、编辑kanboard.yaml文件 三、部署Kanb…

Hadoop三大组件原理详解:hdfs-yarn-MapReduce(第9天)

系列文章目录 一、HDFS读写原理【重点】 二、YARN提交mr流程【重点】 三、MapReduce计算流程【重点】 文章目录 系列文章目录前言一、HDFS读写原理[面试]1、HDFS数据写入解析2、HDFS数据读取解析 二、YARN提交mr流程[面试]1. YARN提交mr过程解析 三、MapReduce计算流程[面试]1…

探索监管沙箱在金融科技行业中的应用

一、引言 随着金融科技的快速发展&#xff0c;传统金融机构与科技企业之间的竞争也日趋激烈。为了平衡金融科技创新与风险防控&#xff0c;各国监管机构纷纷引入监管沙箱&#xff08;Regulatory Sandbox&#xff09;机制。监管沙箱作为一个受监督的安全测试区&#xff0c;允许金…

MySQL 面试突击指南:核心知识点解析1

MySQL中有哪些存储引擎? InnoDB存储引擎 InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎,设计用于处理大量短期事务。 MyISAM存储引擎 在MySQL 5.1及之前版本,MyISAM是默认的存储引擎。它提供了全文索引、压缩、空间函数(GIS)等特性,但不支持事务和…

台积电(TSMC)正在探索采用新型先进芯片封装技术

台积电&#xff08;TSMC&#xff09;正在探索采用新型先进芯片封装技术&#xff0c;使用类似面板的矩形基板&#xff0c;以应对日益增长的先进多芯片组处理器需求。据日经亚洲报道&#xff0c;这项开发仍处于早期阶段&#xff0c;可能需要数年时间才能商业化&#xff0c;但如果…

Python酷库之旅-第三方库openpyxl(01)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…

【C语言 || 数据结构】二叉树

文章目录 前言 二叉树1.树1.1树的定义1.2 树的结构 2.特殊的树&#xff08;二叉树&#xff09;2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的储存2.3.1 顺序储存二叉树2.3.2 链表储存二叉树 2.4 二叉树的遍历2.4.1 二叉树的中序遍历2.4.2 二叉树的前序遍历2.4.3 二叉树的后序遍…

【React】使用Token做路由权限控制

在components/AuthRoute/index.js中 import { getToken } from /utils import { Navigate } from react-router-domconst AuthRoute ({ children }) > {const isToken getToken()if (isToken) {return <>{children}</>} else {return <Navigate to"/…

Solr9 如何使用 DIH 读取数据库索引数据

使用 Solr 9 中的数据导入处理程序&#xff08;DIH&#xff09; DIH&#xff08;Data Import Handler&#xff09;提供了一种可配置的方式向 Solr 中导入数据。 从 Solr 9 开始&#xff0c;数据导入处理程序&#xff08;DIH&#xff09;已经不再直接包含在 Solr 中&#xff0c…