backend/app/model/index/Page.scala (92 lines of code) (raw):

package model.index import model.Language import model.frontend.HighlightableText import play.api.libs.json.{Format, JsArray, JsNull, JsNumber, JsString, JsValue, Json, Writes} case class PagesSummary(numberOfPages: Long, height: Double) object PagesSummary { implicit val format: Format[PagesSummary] = Json.format[PagesSummary] } case class PageDimensions(width: Double, height: Double, top: Double, bottom: Double) object PageDimensions { implicit val format: Format[PageDimensions] = Json.format[PageDimensions] val A4_PORTRAIT = PageDimensions(595, 842, 0, 842) } case class HighlightSpan(x: Double, y: Double, width: Double, height: Double, rotation: Double) sealed abstract class PageHighlight { def id: String def index: Int def spans: List[HighlightSpan] } // TODO: these are identical... why do we need them? case class SearchHighlight(id: String, index: Int, spans: List[HighlightSpan]) extends PageHighlight case class FindHighlight(id: String, index: Int, spans: List[HighlightSpan]) extends PageHighlight // Other types of highlight might include comments or Ctrl-F searches object PageHighlight { implicit val writes: Writes[PageHighlight] = { case h: SearchHighlight => Json.obj( "type" -> JsString("SearchHighlight"), "id" -> JsString(h.id), "index" -> JsNumber(h.index), "data" -> JsArray(h.spans.map { s => Json.obj( "x" -> JsNumber(s.x), "y" -> JsNumber(s.y), "width" -> JsNumber(s.width), "height" -> JsNumber(s.height), "rotation" -> JsNumber(s.rotation), ) }) ) case h: FindHighlight => Json.obj( "type" -> JsString("FindHighlight"), "id" -> JsString(h.id), "index" -> JsNumber(h.index), "data" -> JsArray(h.spans.map { s => Json.obj( "x" -> JsNumber(s.x), "y" -> JsNumber(s.y), "width" -> JsNumber(s.width), "height" -> JsNumber(s.height), "rotation" -> JsNumber(s.rotation), ) }) ) } } case class HighlightForSearchNavigation( pageNumber: Long, highlightNumber: Long, id: String, firstSpan: Option[HighlightSpan] ) object HighlightForSearchNavigation { def fromPageHighlight(pageNumber: Long, highlightNumber: Long, pageHighlight: PageHighlight) = HighlightForSearchNavigation(pageNumber, highlightNumber, pageHighlight.id, pageHighlight.spans.headOption) implicit val writes: Writes[HighlightForSearchNavigation] = { h => Json.obj( "pageNumber" -> JsNumber(h.pageNumber), "highlightNumber" -> JsNumber(h.highlightNumber), "id" -> JsString(h.id), "firstSpan" -> (h.firstSpan match { case Some(s) => Json.obj( "x" -> JsNumber(s.x), "y" -> JsNumber(s.y), "width" -> JsNumber(s.width), "height" -> JsNumber(s.height), "rotation" -> JsNumber(s.rotation), ) case None => JsNull }) ) } } case class Page(page: Long, value: Map[Language, String], dimensions: PageDimensions) case class PageWithFind(page: Long, value: Map[Language, String], highlightedText: Option[Map[Language, String]], dimensions: PageDimensions) case class FrontendPage(page: Long, currentLanguage: Language, allLanguages: Set[Language], dimensions: PageDimensions, highlights: List[PageHighlight]) object FrontendPage { implicit val writes: Writes[FrontendPage] = Json.writes[FrontendPage] } case class PageFetchViewport(top: Double, bottom: Double) case class PageResult(summary: PagesSummary, pages: List[Page]) case class FrontendPageResult(summary: PagesSummary, pages: List[FrontendPage]) object FrontendPageResult { implicit val format: Writes[FrontendPageResult] = Json.writes[FrontendPageResult] }