coretext(轉載http://www.padovo.com/blog/2013/01/31/study-coretext/)

Study CoreTextJAN 31ST, 2013 | COMMENTS前言好久沒寫日志,之前說過至少一天一篇,至了第二天就沒開始做了。太忙了,有些疲于奔命,因為涼茶項目和阿拉丁項目的原因,自己有點力不從心了。引言學習CoreText,最初的想法是寫一個雜志類的應用,因為對網易和zarca應用一些技術的疑問,所以,自己有了很強的興趣欲和鉆研欲,開始這段有點不順的學習過程。難題1、對CGContextRef的CTM不理解,觀念導致很多東西沒有正確的理解。2、對NS的了解不多,一些文字繪制方面的座標系問題讓自己很迷惑。3、對CoreText麻煩的API嚴重不適應。關于CTMCTM,Context Translate Matrix。 它是把要繪制的上下文以一個叫做Matrix的東西來表示,可以簡單地想作,繪制的上下文的每一個點都映射在Matrix上,你在Matrix上的操作都會使得上下文上的點產生相應的變動。如放大、旋轉、移動。在一般的教程里面,為了達到旋轉或放大縮小的目的,一般都會先改變這個上下文,如:12345CGContextTranslateCTM(context, 0, self.bounds.size.height);CGContextScaleCTM(context, 1.0f, -1.0f);// some draw code// ....然后進行繪圖操作。那么這個繪圖操作是怎么做的呢?這個對Matrix的操作,為什么是放在前面而不是放在后面,為什么放在后面又沒有效果呢?不是說改變Matrix就會改變上面的映射的所有點呢?這些常規的邏輯思維使得問題越發無法理解和解決。那么我們先從context來了解。一般情況也,我們總是認為context就是畫布,所有的matrix旋轉都是針對畫布的旋轉,雖然這樣的理解是錯誤的,但是得到的結果卻是正確的,但是如果在一些稍復雜的坐標系轉換時,或者更改matrix時在之前或之后的理解時,這樣理解就會得到難得理解的結果。其實context說的是繪畫人所處的角度上下文。如下圖,默認的情況下,繪畫人的角度是正對著畫布的:畫布是白色的,而我則是在左上角用一個黃色的三角形來標識它的左上角,使用left top來標識context的左上角,而繪畫人是黃色的圓形。要記著??!畫布無論怎么樣都是正對著屏幕的,它不會旋轉,或者放大縮小,或者移動。那么為什么又看起來我放大了或者移動了呢?其實移動的是你的context,也就是你所處的context視角,我舉個例子,比方說我要旋轉180度在左上角寫一個“abcdefg”。首先,我要先旋轉180度:然后,我在左上角寫上“abcdefg”:然后重置context:可以看到,我們改變context只是改變了自己面對畫布的角度,而畫布仍然是正對著屏幕的,自己始終以context的左上角為自己角度的左上角,而不是以畫布的左上角為左上角,也就是說,這時繪畫時的座標(0,0)是你旋轉后context的left top,而不是畫布 的左上角,記著這一點很重要。所以,在繪畫的時候,其實是倒著畫在了畫布的右下角上。而重置context,則是把自己正對著畫布而已。這也就說清了為什么是在使用matrix更改context之后進行繪圖有效(把自己面對畫布的角度先調整了),而不是在畫了之后再調整(因為你都畫完了,再調整自己的角度還有什么用?)。正確理解使用matrix更改context的方式很重要,因為這涉及到坐標系的問題,之后的CoreText相當討論會講到一個例子。關于NS座標系NS坐標系是以左下角為(0,0),與iOS的坐標系在Y上是相反的,所以,在iOS進行CoreText進行繪圖或文字的時候,X方向是一致的,但是Y則是倒過來的。如下圖:那么怎么辦呢?想想,仔細看上面這張圖,貌似像是正常方向的倒影,但是水平線卻在最上面。嗯,挪下來,然后再反過來,看一下效果。如下圖:效果:效果果然如圖所示,好?。】墒鞘遣皇蔷瓦@樣完了呢?不是,還有一個更為重要的問題,這個時候,進行了兩次的轉換matrix,context的left top在哪里呢?根據之前的理論,那得讓自己先把自己向下移,然后把頭倒過來,OK,這下明白了,這下畫布的左下角變成了context的左上角,別的都沒變。這時,當你在(20, 20)畫一個長方形,其實就是畫布的(20, canvas.height - 20 + rect.size.height)的位置上畫了個長方形,而且是倒過來的。仔細想想這個,有趣的事情還有很多,因為按照自己看過本文之前的理論,可能會非常驚訝為什么得到的結果和自己想的不一樣,一直以為是在(20, 20)處畫一個長方形,結果卻剛好相反,這就是沒有理解context及matrix的正確含義所致。關于CoreText的API這種CoreFoundation式的API讓我覺得很不適應,和一開始接觸Objective-C一樣,我在罵娘。不過一定要注意一點,就是使用Create函數建立的對象引用,必須要使用CFRelease掉。CoreText是什么?這是一個低級的API,它的數據源是NSAttributedString。它可以根據NSAttributedString的定義的每個range的subNSAttributedString的樣式進行對字符串的渲染。可以這樣說,這是一個富文本渲染器。為什么要用CoreText不是有了UIWebView了嗎?為什么還要用CoreText呢?首先,UIWebView是一個很重量級的東西,占用大量的內存,加載緩慢。其次,UIWebView做不了一些東西,比如說分列顯示,這點肯定webpage做不到(我做了這么久web,直到現在也想不到辦法)。最后,UIWebView做出來的東西總是讓人覺得不夠cool,不知怎的,你總會發現用CoreText做的東西相當的有雜志感。那么為什么要用CoreText呢?其實很重要的一點是那個無用的UILabel控件,一方面是不能控制行高,另一個功能有限,特別難以控制多行文本,讓人蛋疼的地方是,多行文本時,它竟然只能垂直居中。另,使用CoreText可以很好地做一些個性化的東西,比如可以使用動畫,這一點UIWebView做不到。它能做一些很cool的東西,比方說,雜志,新聞類的應用。這是我為什么使用CoreText的原因。CoreText的概念。CTFramesetterRef按名字就知道,這是一個setter,一個屬性設置器,它引用了一些有用的對像,諸如字體之類的,它的工作就是,生成一個CTFrameRef對象。CTFrameRef這是一個Frame對象,用于表示一個繪制區域,它是由CTFramesetterRef生成。CTLineRef表示要繪制的一行,這個概念可以不用理解,一般情況下我們不會使用到它,但是如果想做更為徹底的定制工作的話,那么就要去看apple的文檔了。CTFrameRef對象包含了多個line對象。CTNodeRef表示要繪制的某個節點(subNSAttributedString),每line對象包含多個node對象,這個節點表示著不同格式的NSAttributedString對象的如何繪制。這個也可以不用理解,一般情況下我們也不會使用到它,不過要是用于在中間插入圖片的話,這個就要考慮了。CoreText是不支持中間插入圖片的,不過我們可以在讀到特殊標記的node的時候,返回不同的行高和行寬,預留空間,在繪制完coretext之后,在這些個空間處繪制相應的圖片。CoreText怎么使用呢?上面寫了一堆概念性的東西,一下子還真是看不懂。那么怎么用呢?其實流程是這樣的: 1、生成要繪制的NSAttributedString對象。 2、生成一個CTFramesetterRef對象,然后創建一個CGPath對象,這個Path對象用于表示可繪制區域坐標值、長寬。 3、使用上面生成的setter和path生成一個CTFrameRef對象,這個對象包含了這兩個對象的信息(字體信息、坐標信息),它就可以使用CTFrameDraw方法繪制了。這里有一個demo代碼:1234567891011121314151617181920212223- (void)_drawRectWithContext:(CGContextRef)context{? // generate NSAttributedString object? ? NSAttributedString *contentAttrString = [self _generateAttributeString];? // path? ? CGRect bounds = CGRectInset(self.bounds, 10.0f, 10.0f);? ? CGMutablePathRef path = CGPathCreateMutable();? ? CGPathAddRect(path, NULL, bounds);? ? // ------------------------ begin draw? ? // draw coretext? ? CTFramesetterRef framesetter? ? ? ? = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(contentAttrString));? ? CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);? ? CTFrameDraw(frame, context);? ? CFRelease(frame);? ? CFRelease(path);? ? CFRelease(framesetter);? ? // ------------------------ end draw}NSAttributedString呢?一直都在說CoreText的事情,那么怎么使用NSAttributedString呢?怎么設置NSAttributedString的屬性,怎么設計行高,怎么設置字體大小,樣式?首先得會設置屬性,調用NSAttributedString的setAttributes:range:方法就可以設置屬性:123// set attributes to attributed string[attrString setAttributes:attributes? ? ? ? ? ? ? ? ? ? range:NSMakeRange(0, attrString.length)];那么都有哪些屬性呢?有下面這些屬性:123456789101112131415const CFStringRef kCTCharacterShapeAttributeName;const CFStringRef kCTFontAttributeName;const CFStringRef kCTKernAttributeName;const CFStringRef kCTLigatureAttributeName;const CFStringRef kCTForegroundColorAttributeName;const CFStringRef kCTForegroundColorFromContextAttributeName;const CFStringRef kCTParagraphStyleAttributeName;const CFStringRef kCTStrokeWidthAttributeName;const CFStringRef kCTStrokeColorAttributeName;const CFStringRef kCTSuperscriptAttributeName;const CFStringRef kCTUnderlineColorAttributeName;const CFStringRef kCTUnderlineStyleAttributeName;const CFStringRef kCTVerticalFormsAttributeName;const CFStringRef kCTGlyphInfoAttributeName;const CFStringRef kCTRunDelegateAttributeName下面是說明:kCTCharacterShapeAttributeNameControls glyph selection. Value must be a CFNumberRef object. Default is value is 0 (disabled). A non-zero value is interpreted as Apple Type Services kCharacterShapeType selector + 1 (seefor selectors). For example, an attribute value of 1 corresponds to kTraditionalCharactersSelector.

kCTFontAttributeName

The font of the text to which this attribute applies. The value associated with this attribute must be a CTFont object. Default is Helvetica 12.

kCTKernAttributeName

The amount to kern the next character. The value associated with this attribute must be a CFNumber float. Default is standard kerning. The kerning attribute indicates how many points the following character should be shifted from its default offset as defined by the current character’s font in points: a positive kern indicates a shift farther away from and a negative kern indicates a shift closer to the current character. If this attribute is not present, standard kerning is used. If this attribute is set to 0.0, no kerning is done at all.

kCTLigatureAttributeName

The type of ligatures to use. The value associated with this attribute must be a CFNumber object. Default is an integer value of 1. The ligature attribute determines what kinds of ligatures should be used when displaying the string. A value of 0 indicates that only ligatures essential for proper rendering of text should be used. A value of 1 indicates that standard ligatures should be used, and 2 indicates that all available ligatures should be used. Which ligatures are standard depends on the script and possibly the font. Arabic text, for example, requires ligatures for many character sequences but has a rich set of additional ligatures that combine characters. English text has no essential ligatures, and typically has only two standard ligatures, those for “fi” and “fl”—all others are considered more advanced or fancy.

kCTForegroundColorAttributeName

The foreground color of the text to which this attribute applies. The value associated with this attribute must be a CGColor object. Default value is black.

kCTForegroundColorFromContextAttributeName

Sets a foreground color using the context’s fill color. Value must be a CFBooleanRef object. Default is false. The reason this exists is because an NSAttributedString object defaults to a black color if no color attribute is set. This forces Core Text to set the color in the context. This attribute allows developers to sidestep this, making Core Text set nothing but font information in the CGContext. If set, this attribute also determines the color used by kCTUnderlineStyleAttributeName, in which case it overrides the foreground color.

kCTParagraphStyleAttributeName

The paragraph style of the text to which this attribute applies. A paragraph style object is used to specify things like line alignment, tab rulers, writing direction, and so on. Value must be a CTParagraphStyle object. Default is an empty CTParagraphStyle object. See CTParagraphStyle Reference for more information.

kCTStrokeWidthAttributeName

The stroke width. Value must be a CFNumberRef object. Default value is 0.0, or no stroke. This attribute, interpreted as a percentage of font point size, controls the text drawing mode: positive values effect drawing with stroke only; negative values are for stroke and fill. A typical value for outlined text is 3.0.

kCTStrokeColorAttributeName

The stroke color. Value must be a CGColorRef object. Default is the foreground color.

kCTSuperscriptAttributeName

Controls vertical text positioning. Value must be a CFNumberRef object. Default is integer value 0. If supported by the specified font, a value of 1 enables superscripting and a value of -1 enables subscripting.

kCTUnderlineColorAttributeName

The underline color. Value must be a CGColorRef object. Default is the foreground color.

kCTUnderlineStyleAttributeName

The style of underlining, to be applied at render time, for the text to which this attribute applies. Value must be a CFNumber object. Default is kCTUnderlineStyleNone. Set a value of something other than kCTUnderlineStyleNone to draw an underline. In addition, the constants listed in “CTUnderlineStyleModifiers” can be used to modify the look of the underline. The underline color is determined by the text’s foreground color.

kCTVerticalFormsAttributeName

The orientation of the glyphs in the text to which this attribute applies. Value must be a CFBoolean object. Default is False. A value of False indicates that horizontal glyph forms are to be used; True indicates that vertical glyph forms are to be used.

kCTGlyphInfoAttributeName

The glyph info object to apply to the text associated with this attribute. Value must be a CTGlyphInfo object. The glyph specified by this CTGlyphInfo object is assigned to the entire attribute range, provided that its contents match the specified base string and that the specified glyph is available in the font specified by kCTFontAttributeName. See CTGlyphInfo Reference for more information.

kCTRunDelegateAttributeName

The run-delegate object to apply to an attribute range of the string. The value must be a CTRunDelegate object. The run delegate controls such typographic traits as glyph ascent, descent, and width. The values returned by the embedded run delegate apply to each glyph resulting from the text in that range. Because an embedded object is only a display-time modification, you should avoid applying this attribute to a range of text with complex behavior, such as text having a change of writing direction or having combining marks. It is thus recommended you apply this attribute to a range containing the single character U+FFFC. See CTRunDelegate Reference for more information.

上面所述的東西貌似只是說明了設置字體與樣式,卻沒有行高、縮進之類的東西哦??!

嗯,不是沒有,而是CoreText把它當成是段落樣式來設置了,也就是說,要設置kCTParagraphStyleAttributeName的屬性就行。kCTParagraphStyleAttributeName屬性的值是一個CTParagraphStyle對象,你需要把你想要設置的段落屬性放進這個對象就可以設置行高之類的東西:


kCTParagraphStyleSpecifierAlignment = 0,

kCTParagraphStyleSpecifierFirstLineHeadIndent = 1,

kCTParagraphStyleSpecifierHeadIndent = 2,

kCTParagraphStyleSpecifierTailIndent = 3,

kCTParagraphStyleSpecifierTabStops = 4,

kCTParagraphStyleSpecifierDefaultTabInterval = 5,

kCTParagraphStyleSpecifierLineBreakMode = 6,

kCTParagraphStyleSpecifierLineHeightMultiple = 7,

kCTParagraphStyleSpecifierMaximumLineHeight = 8,

kCTParagraphStyleSpecifierMinimumLineHeight = 9,

kCTParagraphStyleSpecifierLineSpacing = 10,? ? ? ? ? /* deprecated */

kCTParagraphStyleSpecifierParagraphSpacing = 11,

kCTParagraphStyleSpecifierParagraphSpacingBefore = 12,

kCTParagraphStyleSpecifierBaseWritingDirection = 13,

kCTParagraphStyleSpecifierMaximumLineSpacing = 14,

kCTParagraphStyleSpecifierMinimumLineSpacing = 15,

kCTParagraphStyleSpecifierLineSpacingAdjustment = 16,

kCTParagraphStyleSpecifierLineBoundsOptions = 17,

kCTParagraphStyleSpecifierCount

下面是說明:

kCTParagraphStyleSpecifierAlignment

The text alignment. Natural text alignment is realized as left or right alignment, depending on the line sweep direction of the first script contained in the paragraph. Type: CTTextAlignment. Default: kCTNaturalTextAlignment. Application: CTFramesetter.

kCTParagraphStyleSpecifierFirstLineHeadIndent

The distance, in points, from the leading margin of a frame to the beginning of the paragraph’s first line. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierHeadIndent

The distance, in points, from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative. Type: CGFloat Default: 0.0 Application: CTFramesetter

kCTParagraphStyleSpecifierTailIndent

The distance, in points, from the margin of a frame to the end of lines. If positive, this value is the distance from the leading margin (for example, the left margin in left-to-right text). If 0 or negative, it’s the distance from the trailing margin. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierTabStops

The CTTextTab objects, sorted by location, that define the tab stops for the paragraph style. Type: CFArray of CTTextTabRef. Default: 12 left-aligned tabs, spaced by 28.0 points. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierDefaultTabInterval

The documentwide default tab interval. Tabs after the last specified by kCTParagraphStyleSpecifierTabStops are placed at integer multiples of this distance (if positive). Type: CGFloat. Default: 0.0. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierLineBreakMode

The mode that should be used to break lines when laying out the paragraph’s text. Type: CTLineBreakMode. Default: kCTLineBreakByWordWrapping. Application: CTFramesetter

kCTParagraphStyleSpecifierLineHeightMultiple

The line height multiple. The natural line height of the receiver is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierMaximumLineHeight

The maximum height that any line in the frame will occupy, regardless of the font size or size of any attached graphic. Glyphs and graphics exceeding this height will overlap neighboring lines. A maximum height of 0 implies no line height limit. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierMinimumLineHeight

The minimum height that any line in the frame will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierLineSpacing

The space in points added between lines within the paragraph (commonly known as leading). This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierParagraphSpacing

The space added at the end of the paragraph to separate it from the following paragraph. This value is always nonnegative and is determined by adding the previous paragraph’s kCTParagraphStyleSpecifierParagraphSpacing setting and the current paragraph’s kCTParagraphStyleSpecifierParagraphSpacingBefore setting. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierParagraphSpacingBefore

The distance between the paragraph’s top and the beginning of its text content. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierBaseWritingDirection

The base writing direction of the lines. Type: CTWritingDirection. Default: kCTWritingDirectionNatural. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierCount

The number of style specifiers. The purpose is to simplify validation of style specifiers

那么怎么編碼呢?也就是這些屬性怎么用呢?下面是一個demo:

NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:_content];

// line space

CTParagraphStyleSetting lineSpaceSetting;

lineSpaceSetting.spec = kCTParagraphStyleSpecifierLineSpacing;

lineSpaceSetting.value = &_lineSpace;

lineSpaceSetting.valueSize = sizeof(float);

// indent

CTParagraphStyleSetting indentSetting;

indentSetting.spec = kCTParagraphStyleSpecifierFirstLineHeadIndent;

indentSetting.value = &_indent;

indentSetting.valueSize = sizeof(float);

// composite settings

CTParagraphStyleSetting settings[] = {

lineSpaceSetting,

indentSetting

};

CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 2);

// build attributes

NSDictionary *attributes = @{(__bridge id)kCTParagraphStyleAttributeName: (__bridge id)style};

// set attributes to attributed string

[attrString setAttributes:attributes

range:NSMakeRange(0, attrString.length)];

分欄顯示

這個東西是相當有趣的東東,可以使用CTFrameGetVisibleStringRange函數來計算指定frame繪制了多少字符,那么就可以另建一個frame把剩余的字符繪制進去:


// get current context and store it's state

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSaveGState(context);

// translate CTM for iOS

CGContextTranslateCTM(context, 0, self.bounds.size.height);

CGContextScaleCTM(context, 1, -1);

// generate attributed string

NSAttributedString *attrString = [self _generateAttributedString];

// Draw code start -------------------------------------------------------------------------------------------------

CGRect bounds = CGRectInset(self.bounds, 25.0f, 25.0f);

float columnWidth = (bounds.size.width - 30.0f) / 2.0f;

float columnHeight = bounds.size.height;

CTFramesetterRef framesetter

= CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));

int location = 0;

// ------------- first column

CGRect firstColumnRect = CGRectMake(bounds.origin.x, bounds.origin.y, columnWidth, columnHeight);

CGMutablePathRef firstColumnPath = CGPathCreateMutable();

CGPathAddRect(firstColumnPath, NULL, firstColumnRect);

CTFrameRef firstColumnFrame =

CTFramesetterCreateFrame(framesetter, CFRangeMake(location, 0), firstColumnPath, NULL);

CFRange firstColumnStringRange = CTFrameGetVisibleStringRange(firstColumnFrame);

CTFrameDraw(firstColumnFrame, context);

// recalculate the location for next frame.

location = firstColumnStringRange.length;

// ------------- second column

CGRect secondColumnRect =

CGRectMake(bounds.origin.x + 30 + columnWidth, bounds.origin.y, columnWidth, columnHeight);

CGMutablePathRef secondColumnPath = CGPathCreateMutable();

CGPathAddRect(secondColumnPath, NULL, secondColumnRect);

CTFrameRef secondColumnFrame =

CTFramesetterCreateFrame(framesetter,

CFRangeMake(location, 0),

secondColumnPath, NULL);

CTFrameDraw(secondColumnFrame, context);

// release

CFRelease(firstColumnPath);

CFRelease(firstColumnFrame);

CFRelease(secondColumnPath);

CFRelease(secondColumnFrame);

CFRelease(framesetter);

// Draw code end? -------------------------------------------------------------------------------------------------

// restore current context

CGContextRestoreGState(context);

總結

總的來說,CoreText是相當的麻煩的,所以,必須要對相關的代碼進行一定的抽象。

這篇日志1月份就寫了,直到現在才補完。一直害怕把這些內容忘記,于是就翻了下之前寫好的測試代碼,復習了一下,然后寫完它。

好了,今天收工。

2013-3-1 智品公司

Posted by Kut.Zhang Jan 31st, 2013? iOS

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容