void EpubFormatter::HandleTagPagebreak(HtmlToken *t) { AttrInfo *attr = t->GetAttrByName("page_path"); if (!attr || pagePath) ForceNewPage(); if (attr) { RectF bbox(0, currY, pageDx, 0); currPage->instructions.Append(DrawInstr::Anchor(attr->val, attr->valLen, bbox)); pagePath.Set(str::DupN(attr->val, attr->valLen)); // reset CSS style rules for the new document styleRules.Reset(); } }
// the name doesn't quite fit: this handles FB2 tags void Fb2Formatter::HandleHtmlTag(HtmlToken *t) { if (Tag_Title == t->tag || Tag_Subtitle == t->tag) { bool isSubtitle = Tag_Subtitle == t->tag; ScopedMem<char> name(str::Format("h%d", section + (isSubtitle ? 1 : 0))); HtmlToken tok; tok.SetTag(t->type, name, name + str::Len(name)); HandleTagHx(&tok); HandleAnchorAttr(t); if (!isSubtitle && t->IsStartTag()) { char *link = (char *)Allocator::Alloc(textAllocator, 24); sprintf_s(link, 24, FB2_TOC_ENTRY_MARK "%d", ++titleCount); currPage->instructions.Append(DrawInstr::Anchor(link, str::Len(link), RectF(0, currY, pageDx, 0))); } } else if (Tag_Section == t->tag) { if (t->IsStartTag()) section++; else if (t->IsEndTag() && section > 1) section--; FlushCurrLine(true); HandleAnchorAttr(t); } else if (Tag_P == t->tag) { if (!tagNesting.Contains(Tag_Title)) HtmlFormatter::HandleHtmlTag(t); } else if (Tag_Image == t->tag) { HandleTagImg(t); HandleAnchorAttr(t); } else if (Tag_A == t->tag) { HandleTagA(t, "href", "http://www.w3.org/1999/xlink"); HandleAnchorAttr(t, true); } else if (Tag_Pagebreak == t->tag) ForceNewPage(); else if (Tag_Strong == t->tag) HandleTagAsHtml(t, "b"); else if (t->NameIs("emphasis")) HandleTagAsHtml(t, "i"); else if (t->NameIs("epigraph")) HandleTagAsHtml(t, "blockquote"); else if (t->NameIs("empty-line")) { if (!t->IsEndTag()) EmitParagraph(0); } else if (t->NameIs("stylesheet")) HandleTagAsHtml(t, "style"); }
bool HtmlFormatter::EmitImage(ImageData* img) { CrashIf(!img->data); Size imgSize = BitmapSizeFromData(img->data, img->len); if (imgSize.Empty()) return false; SizeF newSize((REAL)imgSize.Width, (REAL)imgSize.Height); // move overly large images to a new line (if they don't fit entirely) if (!IsCurrLineEmpty() && (currX + newSize.Width > pageDx || currY + newSize.Height > pageDy)) FlushCurrLine(false); // move overly large images to a new page // (if they don't fit even when scaled down to 75%) REAL scalePage = std::min((pageDx - currX) / newSize.Width, pageDy / newSize.Height); if (currY > 0 && currY + newSize.Height * std::min(scalePage, 0.75f) > pageDy) ForceNewPage(); // if image is bigger than the available space, scale it down if (newSize.Width > pageDx - currX || newSize.Height > pageDy - currY) { REAL scale = std::min(scalePage, (pageDy - currY) / newSize.Height); // scale down images that follow right after a line // containing a single image as little as possible, // as they might be intended to be of the same size if (scale < scalePage && HasPreviousLineSingleImage(currPage->instructions)) { ForceNewPage(); scale = scalePage; } if (scale < 1) { newSize.Width = std::min(newSize.Width * scale, pageDx - currX); newSize.Height = std::min(newSize.Height * scale, pageDy - currY); } } RectF bbox(PointF(currX, 0), newSize); AppendInstr(DrawInstr::Image(img->data, img->len, bbox)); currX += bbox.Width; return true; }
void HtmlFormatter::EmitEmptyLine(float lineDy) { CrashIf(!IsCurrLineEmpty()); currY += lineDy; if (currY <= pageDy) { currX = NewLineX(); // remove all spaces (only keep SetFont, LinkStart and Anchor instructions) for (size_t k = currLineInstr.size(); k > 0; k--) { DrawInstr& i = currLineInstr.at(k - 1); if (InstrFixedSpace == i.type || InstrElasticSpace == i.type) currLineInstr.RemoveAt(k - 1); } return; } ForceNewPage(); }
MobiFormatter::MobiFormatter(HtmlFormatterArgs* args, MobiDoc *doc) : HtmlFormatter(args), doc(doc) { bool fromBeginning = (0 == args->reparseIdx); if (!doc || !fromBeginning) return; ImageData *img = doc->GetCoverImage(); if (!img) return; // TODO: vertically center the cover image? EmitImage(img); // only add a new page if the image isn't broken if (currLineInstr.Count() > 0) ForceNewPage(); }
Fb2Formatter::Fb2Formatter(HtmlFormatterArgs *args, Fb2Doc *doc) : HtmlFormatter(args), fb2Doc(doc), section(1), titleCount(0) { if (args->reparseIdx != 0) return; ImageData *cover = doc->GetCoverImage(); if (!cover) return; EmitImage(cover); // render larger images alone on the cover page, // smaller images just separated by a horizontal line if (0 == currLineInstr.Count()) /* the image was broken */; else if (currLineInstr.Last().bbox.Height > args->pageDy / 2) ForceNewPage(); else EmitHr(); }
void MobiFormatter::HandleHtmlTag(HtmlToken *t) { CrashIf(!t->IsTag()); if (Tag_P == t->tag || Tag_Blockquote == t->tag) { HtmlFormatter::HandleHtmlTag(t); HandleSpacing_Mobi(t); } else if (Tag_Mbp_Pagebreak == t->tag) { ForceNewPage(); } else if (Tag_A == t->tag) { HandleAnchorAttr(t); // handle internal and external links (prefer internal ones) if (!HandleTagA(t, "filepos")) HandleTagA(t); } else if (Tag_Hr == t->tag) { // imitating Kindle: hr is proceeded by an empty line FlushCurrLine(false); EmitEmptyLine(lineSpacing); EmitHr(); } else { HtmlFormatter::HandleHtmlTag(t); } }