bool SaveFileModifictions(const WCHAR *filepath, Vec<PageAnnotation> *list) { if (!list) return false; str::Str<char> data; data.AppendFmt("/* SumatraPDF: modifications to \"%S\" */\r\n", path::GetBaseName(filepath)); data.AppendFmt("@meta { version: %s", SMX_CURR_VERSION); int64 size = file::GetSize(filepath); if (0 <= size && size <= UINT_MAX) data.AppendFmt("; filesize: %u", (UINT)size); data.Append(" }\r\n\r\n"); for (size_t i = 0; i < list->Count(); i++) { PageAnnotation& annot = list->At(i); switch (annot.type) { case Annot_Highlight: data.Append("highlight"); break; case Annot_Underline: data.Append("underline"); break; case Annot_StrikeOut: data.Append("strikeout"); break; case Annot_Squiggly: data.Append("squiggly "); break; default: continue; } data.AppendFmt(" { page: %d; rect: %.2f %.2f %.2f %.2f; color: #%02X%02X%02X%02X }\r\n", annot.pageNo, annot.rect.x, annot.rect.y, annot.rect.dx, annot.rect.dy, annot.color.a, annot.color.r, annot.color.g, annot.color.b); } ScopedMem<WCHAR> modificationsPath(str::Join(filepath, SMX_FILE_EXT)); return file::WriteAll(modificationsPath, data.LendData(), data.Size()); }
bool SaveFileModifictions(const WCHAR *filePath, Vec<PageAnnotation> *list) { if (!list) return false; ScopedMem<WCHAR> modificationsPath(str::Join(filePath, SMX_FILE_EXT)); str::Str<char> data; size_t offset = 0; ScopedMem<char> prevData(file::ReadAll(modificationsPath, NULL)); Vec<PageAnnotation> *prevList = ParseFileModifications(prevData); if (prevList) { // in the case of an update, append changed annotations to the existing ones // (don't rewrite the existing ones in case they're by a newer version which // added annotation types and properties this version doesn't know anything about) for (; offset < prevList->Count() && prevList->At(offset) == list->At(offset); offset++); CrashIf(offset != prevList->Count()); data.AppendAndFree(prevData.StealData()); delete prevList; } else { data.AppendFmt("# SumatraPDF: modifications to \"%S\"\r\n", path::GetBaseName(filePath)); } data.Append("\r\n"); if (list->Count() == offset) return true; // nothing (new) to save data.AppendFmt("[@%s]\r\n", prevList ? "update" : "meta"); data.AppendFmt("version = %s\r\n", SMX_CURR_VERSION); int64 size = file::GetSize(filePath); if (0 <= size && size <= UINT_MAX) data.AppendFmt("filesize = %u\r\n", (UINT)size); SYSTEMTIME time; GetSystemTime(&time); data.AppendFmt("timestamp = %04d-%02d-%02dT%02d:%02d:%02dZ\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); data.Append("\r\n"); for (size_t i = offset; i < list->Count(); i++) { PageAnnotation& annot = list->At(i); switch (annot.type) { case Annot_Highlight: data.Append("[highlight]\r\n"); break; case Annot_Underline: data.Append("[underline]\r\n"); break; case Annot_StrikeOut: data.Append("[strikeout]\r\n"); break; case Annot_Squiggly: data.Append("[squiggly]\r\n"); break; default: continue; } data.AppendFmt("page = %d\r\n", annot.pageNo); data.AppendFmt("rect = %g %g %g %g\r\n", annot.rect.x, annot.rect.y, annot.rect.dx, annot.rect.dy); data.AppendFmt("color = #%02x%02x%02x\r\n", annot.color.r, annot.color.g, annot.color.b); data.AppendFmt("opacity = %g\r\n", annot.color.a / 255.f); data.Append("\r\n"); } data.RemoveAt(data.Size() - 2, 2); FileTransaction trans; return trans.WriteAll(modificationsPath, data.LendData(), data.Size()) && trans.Commit(); }
Vec<PageAnnotation> *LoadFileModifications(const WCHAR *filePath) { ScopedMem<WCHAR> modificationsPath(str::Join(filePath, SMX_FILE_EXT)); ScopedMem<char> data(file::ReadAll(modificationsPath, NULL)); return ParseFileModifications(data); }
Vec<PageAnnotation> *LoadFileModifications(const WCHAR *filepath) { ScopedMem<WCHAR> modificationsPath(str::Join(filepath, SMX_FILE_EXT)); size_t len; ScopedMem<char> data(file::ReadAll(modificationsPath, &len)); if (!data) return NULL; CssPullParser parser(data, len); if (!parser.NextRule()) return NULL; const CssSelector *sel = parser.NextSelector(); if (!sel || !IsSelector(sel, "@meta") || parser.NextSelector()) return NULL; const CssProperty *prop = parser.NextProperty(); if (!prop || Css_Version != prop->type || !str::Parse(prop->s, prop->sLen, SMX_CURR_VERSION "%$")) return NULL; Vec<PageAnnotation> *list = new Vec<PageAnnotation>(); while (parser.NextRule()) { sel = parser.NextSelector(); if (!sel) continue; PageAnnotType type = IsSelector(sel, "highlight") ? Annot_Highlight : IsSelector(sel, "underline") ? Annot_Underline : IsSelector(sel, "strikeout") ? Annot_StrikeOut : IsSelector(sel, "squiggly") ? Annot_Squiggly : Annot_None; if (Annot_None == type || parser.NextSelector()) continue; int pageNo = 0; RectT<float> rect; PageAnnotation::Color color; while ((prop = parser.NextProperty())) { switch (prop->type) { case Css_Page: if (!str::Parse(prop->s, prop->sLen, "%d%$", &pageNo)) pageNo = 0; break; case Css_Rect: if (!str::Parse(prop->s, prop->sLen, "%f %f %f %f%$", &rect.x, &rect.y, &rect.dx, &rect.dy)) rect = RectT<float>(); break; case Css_Color: int r, g, b, a; if (str::Parse(prop->s, prop->sLen, "#%2x%2x%2x%2x%$", &a, &r, &g, &b)) color = PageAnnotation::Color(r, g, b, a); else if (str::Parse(prop->s, prop->sLen, "#%2x%2x%2x%$", &r, &g, &b)) color = PageAnnotation::Color(r, g, b); break; } } if (pageNo <= 0 || rect.IsEmpty()) continue; list->Append(PageAnnotation(type, pageNo, rect.Convert<double>(), color)); } return list; }