1 Star 0 Fork 0

qsBye/kindle-plugin-touch

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MulanPSL-2.0

kindle-plugin-touch

介绍

为越狱的kindle开发的插件「touch」,显示每日天气

项目结构

  • hello_weather_one 初代项目,主要是Shell
  • hello_weather_two Golang实现

git

[git@gitee.com:qsbye/kindle-plugin-touch.git]

git push -u gitee master

[git@github.com:QS2002/kindle-plugin-touch.git]

git push -u github master

流程

在服务器用php生成600*800的png图片
kindle端定时获取图片并显示到屏幕上

安装说明

(子目录,e.g. kindle_weather_one)

  1. php>7.4
  2. kindle已经越狱
  3. 复制文件夹到电脑挂载的kindle盘的/extensions目录,这个目录在kindle中实际上是/mnt/us/extensions

使用说明

  1. 进入kual找到touch打开
  2. kindle最好先连上wifi

工程日志

第一天:2022年9月21日

主要内容:整理目录,区分一二代

第二天:2023年3月20日

主要内容:恢复工程;信息流图片生成;升级插件;恢复文档; 备忘:文档;

安装go插件

export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
go install -v github.com/ramya-rao-a/go-outline@v0.0.0-20210608161538-9736a4bde949
go install -v golang.org/x/tools/gopls@latest

编译测试

[https://zhuanlan.zhihu.com/p/507786807]

cd hello_weather_two
go build test4.go
# -x选项显示详细输出
GOOS=linux GOARCH=arm go build -x test4.go
GOOS=darwin GOARCH=arm64 go build test4.go
./test4

结果:

little-endian: [0 1 2 3] to 50462976
big-endian: [0 1 2 3] to 66051

查看文件信息

file test4

输出:

test4: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, Go BuildID=b_zNxvL8Mx9BbKsvTTre/W9WOSRtyZx8-Lcukc1-d/4thocgzUpZ4Dpt4c3_YE/nOD6UV8V0GX07dezmmtA, with debug_info, not stripped

第三天:2023年3月21日

主要内容:信息流图片生成;go操作framebuffer写入png图片;触摸;

信息流来源

  1. 一言
  2. 心知天气
  3. rss订阅
  4. 天气新闻
  5. bing每日一图
  6. 高质量广告图片

RSS订阅

[https://zhuanlan.zhihu.com/p/178788237] 国家统计局

[http://www.stats.gov.cn/sj/zxfb/rss.xml] [http://www.stats.gov.cn/sj/sjjd/rss.xml] [https://zhuanlan.zhihu.com/p/113487194] 知乎每日精选 豆瓣书评 豆瓣影评 科学网 周末画报

解析RSS 2.0为HTML文件

[https://www.cnblogs.com/sinosaurus/p/12287388.html] 目前尝试了两种方式,一种是直接在 html 中使用,一种是在vue项目中使用。 这里直接阐述结果,细节不做过多介绍. html效果 vue 需要注意的问题 在html中使用npm包时,需要使用 Browserify 进行转化为 浏览器可以读取的代码 在vue中使用上述的包时,并不是每个都可以直接使用,需要测试一下 由于每个包的开发理念不同,有的从xml转移到json的结构哦,数据嵌套过深,因而还需要自己测试一下 获取 feed 的文件,其实跟平时的 ajax请求,没有什么特别之处,只是在于数据结构不同罢了

bing每日一图

[https://tool.liumingye.cn/bingimg/] 请求地址: http://tool.liumingye.cn/bingimg/img.php 请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php 请求方法: GET

<img src="http://tool.liumingye.cn/bingimg/img.php" />
<div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"></div>

图片布局

目标尺寸:600x800 颜色:黑白

图片生成

问题: 用php7;从http://tool.liumingye.cn/bingimg/img.php获取图片;图片处理为5px圆角;生成600x800的png图片,将从如上网址获取的图片等比例缩放到600x800的图片中;给出代码

用python3;从http://tool.liumingye.cn/bingimg/img.php获取图片;图片处理为5px圆角;生成600x800的png图片,将从如上网址获取的图片等比例缩放到600x800的图片中;给出代码

你可以使用Pillow库来实现这个需求,以下是Python 3的代码:

from io import BytesIO
from PIL import Image, ImageDraw
# 获取图片
response = requests.get("http://tool.liumingye.cn/bingimg/img.php")
image = Image.open(BytesIO(response.content))
# 将图片处理为5px圆角
radius = 5
mask = Image.new("L", image.size, 0)
draw = ImageDraw.Draw(mask)
draw.rectangle((0, radius, radius, image.size[1] - radius), fill=255)
draw.rectangle((radius, 0, image.size[0] - radius, image.size[1]), fill=255)
draw.rectangle((image.size[0] - radius, radius, image.size[0], image.size[1] - radius), fill=255)
mask = mask.filter(ImageFilter.GaussianBlur(radius=radius))
image.putalpha(mask)
# 将图片等比例缩放到600x800的大小
width, height = image.size
if width / height > 0.75:
    target_width = int(height * 0.75)
    target_height = height
else:
    target_width = width
    target_height = int(width / 0.75)
image = image.resize((target_width, target_height), resample=Image.LANCZOS)
# 在600x800的背景图片中心插入处理后的图片
background = Image.new("RGB", (600, 800), "white")
x = (600 - image.size[0]) // 2
y = (800 - image.size[1]) // 2
background.paste(image, (x, y), image)
# 保存为png格式图片
background.save("output.png", "PNG")

代码中,首先使用requests库获取图片,然后使用Pillow库处理图片。Pillow库提供了ImageDraw模块来进行图片绘制,可以使用rectangle()方法绘制矩形。将图片的四个角处理为圆角,可以先创建一个mask图层,然后在上面绘制圆角矩形,最后将mask图层模糊,得到圆角效果。使用putalpha()方法将处理后的mask图层作为图片的透明度层。然后根据图片的宽高比例,将其等比例缩放到600x800的大小,并在600x800的背景图片中心插入处理后的图片。最后将结果保存为png格式的图片。

图片显示

问题:

使用go语言;每5分钟自动从http://tool.liumingye.cn/bingimg/img.php获取图片`并裁剪为600x800的图片并保存到/mnt/us/kindle.png;将/mnt/us/kindle.png图片写入/dev/fb0的framebuffer中;给出代码

以下是使用Go语言实现的代码,需要使用image和image/draw包来处理图片:

package main
import (
    "image"
    "image/draw"
    "image/png"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"
)
func main() {
    // 定义图片URL和文件路径
    imgUrl := "http://tool.liumingye.cn/bingimg/img.php"
    filePath := "/mnt/us/kindle.png"
    
    // 设置定时器,每5分钟执行一次
    timer := time.NewTicker(5 * time.Minute)
    for range timer.C {
        // 获取图片
        response, err := http.Get(imgUrl)
        if err != nil {
            log.Fatal(err)
        }
        defer response.Body.Close()
        
        // 解码图片
        img, err := png.Decode(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        
        // 裁剪图片为600x800大小
        width := img.Bounds().Max.X
        height := img.Bounds().Max.Y
        aspectRatio := float64(width) / float64(height)
        if aspectRatio > 0.75 {
            targetWidth := int(float64(height) * 0.75)
            img = cropImage(img, (width-targetWidth)/2, 0, targetWidth, height)
        } else {
            targetHeight := int(float64(width) / 0.75)
            img = cropImage(img, 0, (height-targetHeight)/2, width, targetHeight)
        }
        img = resizeImage(img, 600, 800)
        
        // 保存图片到文件
        file, err := os.Create(filePath)
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()
        err = png.Encode(file, img)
        if err != nil {
            log.Fatal(err)
        }
        
        // 将图片写入framebuffer
        fb, err := os.OpenFile("/dev/fb0", os.O_WRONLY, 0666)
        if err != nil {
            log.Fatal(err)
        }
        defer fb.Close()
        err = writeImageToFramebuffer(fb, img)
        if err != nil {
            log.Fatal(err)
        }
    }
}
// 裁剪图片
func cropImage(img image.Image, x, y, width, height int) image.Image {
    rect := image.Rect(x, y, x+width, y+height)
    cropped := image.NewRGBA(rect)
    draw.Draw(cropped, cropped.Bounds(), img, rect.Min, draw.Src)
    return cropped
}
// 缩放图片
func resizeImage(img image.Image, width, height int) image.Image {
    resized := image.NewRGBA(image.Rect(0, 0, width, height))
    draw.NearestNeighbor.Scale(resized, resized.Bounds(), img, img.Bounds(), draw.Src)
    return resized
}
// 将图片写入framebuffer
func writeImageToFramebuffer(fb *os.File, img image.Image) error {
    bounds := img.Bounds()
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            pixel := img.At(x, y)
            r, g, b, a := pixel.RGBA()
            r >>= 8
            g >>= 8
            b >>= 8
            a >>= 8
            color := (r << 16) | (g << 8) | b
            err := writePixel(fb, x, y, color)
            if err != nil {
                return err
            }
        }
    }
    return nil
}
// 写入单个像素到framebuffer
func writePixel(fb *os.File, x, y, color int) error {
    var data [4]byte
    data[0] = byte((color >> 16) & 0xff)
    data[1] = byte((color >> 8) & 0xff)
    data[2] = byte(color & 0xff)
    data[3] = 0x00
    offset := (y*600 + x) * 4
    _, err := fb.WriteAt(data[:], int64(offset))
    return err
}

代码中使用定时器每隔5分钟执行一次,从指定的URL获取图片,并将其裁剪为600x800大小,保存到文件中,然后将图片写入framebuffer。图片裁剪和缩放的过程使用了image包中的方法,写入framebuffer的过程则是将每个像素点的RGB值转换为一个32位整数,然后写入framebuffer中对应的位置。

报错:

# command-line-arguments
./show_img1.go:82:7: undefined: draw.NearestNeighbor
NearestNeighbor not declared by package
./show_img1.go:98:32: cannot use color (variable of type uint32) as type int in argument to writePixel
x/image 包中有四种缩放算法:
NearestNeighbor
ApproxBiLinear
BiLinear
CatmullRom

[https://jaycechant.info/2019/golang-1-13-from-dep-to-mod/] [golang.google.cn]

go install golang.org/x/image@latest
go install golang.org/x@latest
go install golang.org/x/image/draw@latest
go list image/draw
touch go.mod
go mode tidy
go mod edit -module=golang.org/x/image
go proxy
export GOPROXY=http://127.0.0.1:7890

因为国情特殊,一定要设置 GOPROXY。 golang.org/x/image: module golang.org/x: reading http://127.0.0.1:7890/golang.org/x/@v/list: 400 Bad Request 问题: go: golang.org/x/image@latest: module golang.org/x/image@latest found (v0.6.0), but does not contain package golang.org/x/image

问题: cannot use color (variable of type uint32) as int value in argument to writePixel

第四天:2023年3月22日

主要内容:SSH;

kindle的MAC地址

00:BB:3A:9E:69:7D

OpenSSH安装

[https://blog.csdn.net/cuemes08808/article/details/100392125] [http://violet.ibs.com.au/openssh/] [https://www.openssh.com] [https://github.com/openssh/openssh-portable]

DropBearSSH安装(嵌入式Linux)

[https://www.cnblogs.com/Jimmy1988/p/9060826.html]

  1. 交叉编译zlib
  2. 交叉编译dropbear
交叉编译工具链

目标架构:Linux,armv7

/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin
/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc
cmake设置教程编译

[https://zhuanlan.zhihu.com/p/467812302] set(CMAKE_SYSTEM_NAME Linux)

CMAKE_SYSTEM_NAME 交叉编译的必设参数,只有当CMAKE_SYSTEM_NAME这个变量被设置了,CMake才认为此时正在交叉编译,它会额外设置一个变量CMAKE_CROSSCOMPILING为true.

set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_SYSTEM_PROCESSOR armv7)

zlib编译
cd ./files/zlib-1.2.13
mkdir build
cd build
#cmake -A armv7 ..
../configure --prefix=$PWD

修改Makefile:

CC=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc  //你交叉编译工具的绝对路径  
AR=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc-ar  
RANLIB=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc-ranlib    
LDSHARED=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc -shared  -Wl,-soname,libz.so.1,--version-script,zlib.map   //(我只是将原来的gcc改成了我自己的编译工具,后面的参数没动过)

修改:

CC=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc 
AR=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-ar  
RANLIB=//Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc-ranlib  
LDSHARED=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc -shared  -Wl,-soname,libz.so.1,--version-script,zlib.map

然后:

make 
make install
dropbear编译
cd ./files/dropbear-2016.74
mkdir build
cd build
../configure --prefix=$PWD  --with-zlib=/Users/workspace/Desktop/projects/kindle-plugin-touch/files/zlib-1.2.13/build CC=gcc --host=arm
make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" 
make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install

局域网设备扫描

brew install nmap
sudo nmap -sP -PI -PT 192.168.0.1/24

图片布局

#wrapper{
width:600px;
height:800px;
margin: 0 auto;
border:1px solid;
}
* {
  box-sizing: border-box;
}
 
body {
  font-family: Arial;
  padding: 10px;
  background: #f1f1f1;
}
 
/* 头部标题 */
.header {
  padding: 30px;
  text-align: center;
  background: white;
}
 
.header h1 {
  font-size: 50px;
}
 
/* 导航条 */
.topnav {
  overflow: hidden;
  background-color: #333;
}
 
/* 导航条链接 */
.topnav a {
  float: left;
  display: block;
  color: #f2f2f2;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}
 
/* 链接颜色修改 */
.topnav a:hover {
  background-color: #ddd;
  color: black;
}
 
/* 创建两列 */
/* Left column */
.leftcolumn {   
  float: left;
  width: 75%;
}
 
/* 右侧栏 */
.rightcolumn {
  float: left;
  width: 25%;
  background-color: #f1f1f1;
  padding-left: 20px;
}
 
/* 图像部分 */
.fakeimg {
  background-color: #aaa;
  width: 100%;
  padding: 20px;
}
 
/* 文章卡片效果 */
.card {
  background-color: white;
  padding: 20px;
  margin-top: 20px;
}
 
/* 列后面清除浮动 */
.row:after {
  content: "";
  display: table;
  clear: both;
}
 
/* 底部 */
.footer {
  padding: 20px;
  text-align: center;
  background: #ddd;
  margin-top: 20px;
}
 
/* 响应式布局 - 屏幕尺寸小于 800px 时,两列布局改为上下布局 */
@media screen and (max-width: 800px) {
  .leftcolumn, .rightcolumn {   
    width: 100%;
    padding: 0;
  }
}

根据以上CSS布局,给出生成600x800px的包含如下内容的图片的python代码:

  1. 来自http://tool.liumingye.cn/bingimg/img.php的图片,
西窗烛

宋代:谭宣子

春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 
燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。

结果:

from PIL import Image, ImageDraw, ImageFont
import requests
# 获取图片
img_url = 'http://tool.liumingye.cn/bingimg/img.php'
response = requests.get(img_url)
# 创建画布
canvas = Image.new('RGB', (600, 800), 'white')
# 在画布上添加图片
img = Image.open(BytesIO(response.content))
img = img.resize((600, 400), Image.ANTIALIAS)
canvas.paste(img, (0, 0))
# 在画布上添加文字
draw = ImageDraw.Draw(canvas)
font = ImageFont.truetype('SmileySans-Oblique.ttf', 18)
text = '''西窗烛
宋代:谭宣子
春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 
燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。'''
draw.text((10, 420), text, font=font, fill='black')
# 保存图片
canvas.save('result.png')

圆角图片

根据以下规则,给出生成600x800px大小的png图片的python代码: 内容:

<html>
<meta charset="UTF-8">
<body>
<img src="http://tool.liumingye.cn/bingimg/img.php" width="200" height="100"  style="border-radius: 10px;" />
<p>西窗烛
宋代:谭宣子
春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 
燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。
</p>
</body>
</html>

布局样式:

/* styles.css */
p{
  font-family: Arial, Helvetica, sans-serif;
}
body{
    background-color: skyblue;
    background-size: cover;
    background-attachment:fixed;
    width: 600px;
    height: 800px;
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}
/*毛玻璃效果*/
.glass{
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}
/*卡片效果*/
.card {
    width: 400px;
    height: auto;
    padding: 2rem;
    border-radius: 1rem;
    background: rgba(255, 255, 255, .7);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    margin: 5rem;
  }
  
  .card-title {
    margin-top: 0;
    margin-bottom: .5rem;
    font-size: 1.2rem;
  }
  
  p, a {
    font-size: 1rem;
  }
  
  a {
    color: #4d4ae8;
    text-decoration: none;
  }
  .glass h1{
    color:aliceblue;
    font-family: Arial, Helvetica, sans-serif;
  }
/* Add styles here to customize the appearance of your app */

结果:

from PIL import Image, ImageDraw, ImageFont
import requests
# 获取图片
img_url = 'http://tool.liumingye.cn/bingimg/img.php'
response = requests.get(img_url)
# 创建画布
canvas = Image.new('RGB', (600, 800), 'white')
# 在画布上添加图片
img = Image.open(BytesIO(response.content))
img = img.resize((200, 100), Image.ANTIALIAS)
canvas.paste(img, (200, 50))
# 在画布上添加文字
draw = ImageDraw.Draw(canvas)
font = ImageFont.truetype('arial.ttf', 18)
text = '''西窗烛
宋代:谭宣子
春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 
燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。'''
draw.text((50, 200), text, font=font, fill='black')
# 添加卡片效果
draw.rectangle([100, 150, 500, 600], fill=(255, 255, 255, 100), outline=None)
draw.rectangle([120, 170, 480, 580], fill=(255, 255, 255, 150), outline=None)
draw.rectangle([140, 190, 460, 560], fill=(255, 255, 255, 200), outline=None)
# 添加毛玻璃效果
canvas = canvas.filter(ImageFilter.GaussianBlur(radius=10))
# 保存图片
canvas.save('result.png')

梯子客户端

[https://github.com/2dust/clashN] ClashN

第五天:2023年3月23日

主要内容:图片接收2;

图片接收并显示

问题: 使用go语言;每5分钟自动从http://tool.liumingye.cn/bingimg/img.php获取图片并保存到/mnt/us/kindle.png;将/mnt/us/kindle.png图片写入/dev/fb0的framebuffer中;给出代码,

以下是使用Go语言每5分钟从指定URL获取图片并将其写入到Kindle设备的framebuffer中的代码:

package main
import (
    "image"
    "image/png"
    "log"
    "os"
    "time"
    "github.com/kettek/goroimagehash"
    "github.com/sajari/regression"
    "golang.org/x/exp/io/i2c"
)
const (
    frameBufferPath = "/dev/fb0"
    imagePath      = "/mnt/us/kindle.png"
    imageURL       = "http://tool.liumingye.cn/bingimg/img.php"
)
func main() {
    for {
        // 获取图片
        resp, err := http.Get(imageURL)
        if err != nil {
            log.Printf("Failed to get image: %v", err)
            continue
        }
        defer resp.Body.Close()
        img, err := png.Decode(resp.Body)
        if err != nil {
            log.Printf("Failed to decode image: %v", err)
            continue
        }
        // 保存图片到本地
        f, err := os.Create(imagePath)
        if err != nil {
            log.Printf("Failed to create file: %v", err)
            continue
        }
        defer f.Close()
        if err := png.Encode(f, img); err != nil {
            log.Printf("Failed to encode image: %v", err)
            continue
        }
        // 将图片写入framebuffer中
        fb, err := os.OpenFile(frameBufferPath, os.O_WRONLY, 0644)
        if err != nil {
            log.Printf("Failed to open framebuffer: %v", err)
            continue
        }
        defer fb.Close()
        if err := writeImageToFramebuffer(img, fb); err != nil {
            log.Printf("Failed to write image to framebuffer: %v", err)
            continue
        }
        // 等待5分钟
        time.Sleep(5 * time.Minute)
    }
}
func writeImageToFramebuffer(img image.Image, fb *os.File) error {
    // 获取图片的尺寸和颜色模式
    bounds := img.Bounds()
    width, height := bounds.Dx(), bounds.Dy()
    format := img.ColorModel()
    // 创建framebuffer
    fb, err := i2c.NewFile(0x3c, frameBufferPath)
    if err != nil {
        return err
    }
    // 设置framebuffer的像素格式
    if _, err := fb.Write([]byte{0, 0x80, 0x08, 0x01}); err != nil {
        return err
    }
    // 将图片写入framebuffer
    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            color := format.Convert(img.At(x, y))
            r, g, b, _ := color.RGBA()
            pixel := uint16((r>>8)<<11 | (g>>8)<<5 | (b >> 8))
            if _, err := fb.Write([]byte{byte(pixel >> 8), byte(pixel)}); err != nil {
                return err
            }
        }
    }
    return nil
}

需要注意的是,以上代码使用了第三方库goroimagehash和sajari/regression,分别用于计算图像的哈希值和做线性回归分析。如果没有安装这两个库,请在终端中执行以下命令进行安装:

go get github.com/kettek/goroimagehash
go get kgithub.com/kettek/goroimagehash
go get github.com/sajari/regression
go get kgithub.com/sajari/regression
go mod download golang.org/x/exp
go get golang.org/x/exp

显示当前时间

问题: kindle 7;使用go语言;屏幕分辨率600x800;仿照“新机表APP"给出一个写入/dev/fb0的framebuffer中显示当前时间的程序

以下是使用Go语言在Kindle 7设备上以600x800分辨率,仿照“新机表APP”在framebuffer中显示当前时间的程序:

package main
import (
    "fmt"
    "image"
    "image/color"
    "image/draw"
    "log"
    "os"
    "time"
    "golang.org/x/exp/io/i2c"
)
const (
    frameBufferPath = "/dev/fb0"
    screenWidth     = 600
    screenHeight    = 800
)
func main() {
    for {
        // 获取当前时间
        now := time.Now()
        hour, min, sec := now.Hour(), now.Minute(), now.Second()
        // 创建画布
        canvas := image.NewRGBA(image.Rect(0, 0, screenWidth, screenHeight))
        // 添加背景色
        draw.Draw(canvas, canvas.Bounds(), &image.Uniform{color.White}, image.ZP, draw.Src)
        // 添加时针
        hourAngle := float64(hour%12)/12*360 + float64(min)/60*30
        hourX, hourY := getHandPosition(hourAngle, 100, 20, screenWidth/2, screenHeight/2)
        drawLine(canvas, screenWidth/2, screenHeight/2, hourX, hourY, color.Black)
        // 添加分针
        minAngle := float64(min)/60*360 + float64(sec)/60*6
        minX, minY := getHandPosition(minAngle, 150, 10, screenWidth/2, screenHeight/2)
        drawLine(canvas, screenWidth/2, screenHeight/2, minX, minY, color.Black)
        // 添加秒针
        secAngle := float64(sec) / 60 * 360
        secX, secY := getHandPosition(secAngle, 200, 5, screenWidth/2, screenHeight/2)
        drawLine(canvas, screenWidth/2, screenHeight/2, secX, secY, color.Red)
        // 将画布写入framebuffer中
        fb, err := os.OpenFile(frameBufferPath, os.O_WRONLY, 0644)
        if err != nil {
            log.Printf("Failed to open framebuffer: %v", err)
            continue
        }
        defer fb.Close()
        if err := writeImageToFramebuffer(canvas, fb); err != nil {
            log.Printf("Failed to write image to framebuffer: %v", err)
            continue
        }
        // 等待1秒
        time.Sleep(time.Second)
    }
}
func getHandPosition(angle float64, length, width int, cx, cy int) (int, int) {
    rad := angle * math.Pi / 180
    x := cx + int(float64(length)*math.Sin(rad))
    y := cy - int(float64(length)*math.Cos(rad))
    return x, y
}
func drawLine(img *image.RGBA, x0, y0, x1, y1 int, c color.Color) {
    dx := math.Abs(float64(x1 - x0))
    dy := math.Abs(float64(y1 - y0))
    sx, sy := 0, 0
    if x0 < x1 {
        sx = 1
    } else {
        sx = -1
    }
    if y0 < y1 {
        sy = 1
    } else {
        sy = -1
    }
    err := dx - dy
    for {
        img.Set(x0, y0, c)
        if x0 == x1 && y0 == y1 {
            break
        }
        e2 := 2 * err
        if e2 > -dy {
            err -= dy
            x0 += sx
        }
        if e2 < dx {
            err += dx
            y0 += sy
        }
    }
}
func writeImageToFramebuffer(img image.Image, fb *os.File) error {
    // 获取图片的尺寸和颜色模式
    bounds := img.Bounds()
    width, height := bounds.Dx(), bounds.Dy()
    format := img.ColorModel()
    // 创建framebuffer
    fb, err := i2c.NewFile(0x3c, frameBufferPath)
    if err != nil {
        return err
    }
    // 设置framebuffer的像素格式
    if _, err := fb.Write([]byte{0, 0x80, 0x08, 0x01}); err != nil {
        return err
    }
    // 将图片写入framebuffer
    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            color := format.Convert(img.At(x, y))
            r, g, b, _ := color.RGBA()
            pixel := uint16((r>>8)<<11 | (g>>8)<<5 | (b >> 8))
            if _, err := fb.Write([]byte{byte(pixel >> 8), byte(pixel)}); err != nil {
                return err
            }
        }
    }
    return nil
}

以上代码使用了image和image/draw包分别用于创建画布和绘制时分秒针,使用了math包计算时分秒针的位置。需要注意的是,以上代码使用了i2c包的NewFile函数来创建framebuffer,如果你的Kindle设备上的framebuffer不是I2C设备,则需要使用其他方式来创建framebuffer。

第六天:2023年5月21日

主要内容:rust运行测试;

查看CPU架构

cat /proc/cpuinfo

结果:

Processor:ARM v7 rev 10(v71)
MIPS:1963.43
Features:swp half thumb fastmult vfp edsp neon vfpv3
Hardware:Freescale i.MX 6soloLite based Wario Board

查看系统信息

unname -r

结果:

3.0.35-lab126

macOS平台交叉编译

现在已经有开发者把交叉编译工具做个 brew 安装脚本,安装

brew install FiloSottile/musl-cross/musl-cross --without-x86_64 --with-arm-hf
#brew install FiloSottile/musl-cross/musl-cross

这个交叉编译工具使用的是 musl c 库,跨平台,性能比 glibc 要差一点。

然后通过 rustup 安装不同的目标支持

rustup target add arm-unknown-linux-musleabihf      # armv6
rustup target add armv7-unknown-linux-musleabihf    # armv7

编译设置 在项目下创建一个 .cargo/config 文件,内容为

[target.arm-unknown-linux-musleabihf]
linker = "arm-linux-musleabihf-ld"

[target.armv7-unknown-linux-musleabihf]
linker = "arm-linux-musleabihf-ld"

[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"

[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"

然后用按照不同的开发机、目标机类型,使用 cargo 进行编译

添加环境变量:

#/opt/homebrew/Homebrew/Cellar
#/opt/homebrew/Homebrew/Cellar/musl-cross
cargo build --target arm-unknown-linux-gnueabihf --release      # armv6, linux
cargo build --target armv7-unknown-linux-gnueabihf --release    # armv7, linux

cargo build --target arm-unknown-linux-musleabihf --release     # armv6, mac
cargo build --target armv7-unknown-linux-musleabihf --release   # armv7, mac

编译完后,在 target/<目标类型>/release 中可找到对应的可执行文件,copy 到目标上就可以运行。

这个文件也可使用 strip 工具来删掉一些符号,从而减少整个可执行文件的体积。

rust运行测试

rust 交叉编译树莓派程序

cargo init
rustup target add arm-unknown-linux-gnueabihf
rustup target add armv7-unknown-linux-gnueabihf
cargo build --target=armv7-unknown-linux-gnueabihf
GOOS=linux GOARCH=arm go build -x test4.go

将如上Go的编译命令翻译为Rust语言的编译命令; 在Rust中,要将代码编译成ARM架构的Linux二进制文件,可以使用以下命令:

cargo build --target=arm-unknown-linux-gnueabihf

其中,cargo build是Rust的构建命令,--target选项指定了编译目标为ARM架构的Linux,arm-unknown-linux-gnueabihf是Linux下ARMv7架构的三元组(Triple),其中unknown表示厂商未知,gnueabihf表示使用了GNU工具链和硬浮点支持。

报错:

  = note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
          ld: unknown option: --as-needed
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

这个错误提示表明你使用了不支持--as-needed选项的链接器。这通常是因为你的编译环境中的链接器版本过低导致的。

为了解决这个问题,你可以尝试以下几个步骤:

  1. 更新链接器版本:尝试更新你的编译环境中的链接器版本。具体方法取决于你所使用的操作系统和包管理器。例如,如果你使用的是Ubuntu系统,可以尝试运行以下命令来更新链接器版本:

    sudo apt-get update
    sudo apt-get install --only-upgrade binutils
    
  2. 移除--as-needed选项:如果更新链接器版本不可行,你可以尝试在编译命令中移除--as-needed选项。在某些情况下,这可能会导致编译出的二进制文件大小增加,但可以解决链接器错误问题。例如:

    GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -ldflags="-s -w"
    

    在这个命令中,我们移除了-pie--as-needed选项,并添加了-s-w选项来减小生成二进制文件的大小。

希望这些方法可以帮助你解决问题。

如果你在执行cargo build --target=arm-unknown-linux-gnueabihf命令时遇到了错误,可以根据错误提示来修改命令。

例如,如果你遇到了error: linker arm-linux-gnueabihf-gcc not found错误,这意味着你的系统中没有安装ARM架构的GCC编译器。你可以通过以下命令来安装:

sudo apt-get install gcc-arm-linux-gnueabihf

如果你遇到了其他错误,可以根据错误提示来修改命令。一般来说,你需要确保你的编译环境中已经安装了适当的交叉编译工具链,并且在构建命令中正确地设置了目标架构。例如,如果你想编译为ARMv7架构,则应该使用armv7-unknown-linux-gnueabihf作为目标架构。

cargo build --target=armv7-unknown-linux-gnueabihf

希望这些信息可以帮助你解决问题。

问题:

fn main() {
    println!("Hello, world!");
}

配置:

[package]
name = "hello_weather_three"
version = "0.1.0"
edition = "2021"

[dependencies]


[target.armv7-unknown-linux-gnueabihf]
rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now"]

编译:

cargo build --target=armv7-unknown-linux-gnueabihf

报错:

  = note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
          ld: unknown option: --as-needed
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

这个错误可能是因为您的链接器不支持 --as-needed 标志,这个标志在某些链接器上是默认启用的。您可以尝试在 Cargo.toml 文件中添加以下代码:

[target.armv7-unknown-linux-gnueabihf]
rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now"]

这将替换默认链接器标志,以便在链接时使用 -Wl,-z,relro,-z,now 标志,而不是默认的 --as-needed 标志。

这个错误可能是由于在使用 --target=armv7-unknown-linux-gnueabihf 时,链接器不支持 --as-needed 标志引起的。您可以尝试将 rustflags 更改为以下内容,以避免使用 --as-needed 标志:

[target.armv7-unknown-linux-gnueabihf]
rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now,-z,noexecstack"]

这将使用 -z,noexecstack 标志替换 --as-needed 标志。 运行:

/mnt/us/hello_weather_three

结果:

Hello,World!

kindle文件路径

ls /mnt/us

获取屏幕信息

eips -i

结果:

Fixed framebuffer info
    id:              mxc_epdc_fb    smem_start:       0x80C00000
    smem_len:            3268608    type:          PACKED_PIXELS
    type_aux:                  0    visual:   STATIC_PSEUDOCOLOR
    xpanstep:                  1    ypanstep:                  1
    ywrapstep:                 0    line_length:             608
    mmio_start:       0x00000000    mmio_len:                  0
    accel:                     0

Variable framebuffer info
    xres:                    600    yres:                    800
    xres_virtual:            608    yres_virtual:           5376
    xoffset:                   0    yoffset:                   0
    bits_per_pixel:            8    grayscale:                 1
    red.offset:                0    green.offset:              0
    red.length:                8    green.length:              8
    red.msb_right:             0    green.msb_right:           0
    blue.offset:               0    transp.offset:             0
    blue.length:               8    transp.length:             0
    blue.msb_right:            0    transp.msb_right:          0
    nonstd:                    0    activate:                128
    width:                    91    height:                  122
    accel_flags:               0    pixclock:           27027027
    left_margin:               8    right_margin:            100
    upper_margin:              4    lower_margin:              8
    hsync_len:                 4    vsync_len:                 1
    sync:                      0    vmode:                     0
    rotate:                    3
DPI: 167.472527 166.557377 167
waveform type = 2
material type = 0
temperature = 28 (C)

清空framebuffer

使用RUST;使用Linux;给出清空framebuffer的代码,使得屏幕短暂空白; 以下是使用Rust在Linux上清空framebuffer的代码:

use std::fs::OpenOptions;
use std::io::{Seek, Write};
use std::mem::size_of;

fn main() {
    // 打开framebuffer设备
    let mut fb_file = OpenOptions::new()
        .write(true)
        .open("/dev/fb0")
        .expect("failed to open framebuffer device");

    // 获取framebuffer的大小
    let mut fb_var_screeninfo = mem::MaybeUninit::<libc::fb_var_screeninfo>::uninit();
    unsafe {
        libc::ioctl(fb_file.as_raw_fd(), libc::FBIOGET_VSCREENINFO, fb_var_screeninfo.as_mut_ptr())
            .expect("failed to get variable screen info");
    }
    let fb_var_screeninfo = unsafe { fb_var_screeninfo.assume_init() };
    let fb_size = fb_var_screeninfo.xres * fb_var_screeninfo.yres * fb_var_screeninfo.bits_per_pixel / 8;

    // 清空framebuffer
    let zero_buffer = vec![0u8; fb_size as usize];
    fb_file.seek(SeekFrom::Start(0)).expect("failed to seek framebuffer device");
    fb_file.write_all(&zero_buffer).expect("failed to write to framebuffer device");
}

这段代码使用了libc库中的ioctl函数来获取framebuffer的大小,并使用标准库中的fsio模块来操作framebuffer设备文件。在清空framebuffer时,我们创建了一个与framebuffer大小相同的零向量,然后将其写入framebuffer设备文件中。这将导致屏幕短暂地变成空白。

使用RUST;使用Linux;使用musl c库;给出清空framebuffer的代码,使得屏幕短暂空白; 以下是使用Rust编写的清空Linux Framebuffer的代码,使用musl c库:

use std::fs::OpenOptions;
use std::io::{self, Write};
use std::mem::size_of;

const FB_DEVICE: &str = "/dev/fb0";

#[repr(C)]
struct FbVarScreenInfo {
    xres: u32,
    yres: u32,
    xres_virtual: u32,
    yres_virtual: u32,
    xoffset: u32,
    yoffset: u32,
    bits_per_pixel: u32,
    grayscale: u32,
    red: FbBitfield,
    green: FbBitfield,
    blue: FbBitfield,
    transp: FbBitfield,
    nonstd: u32,
    activate: u32,
    height: u32,
    width: u32,
    accel_flags: u32,
    pixclock: u32,
    left_margin: u32,
    right_margin: u32,
    upper_margin: u32,
    lower_margin: u32,
    hsync_len: u32,
    vsync_len: u32,
    sync: u32,
    vmode: u32,
    rotate: u32,
    colorspace: u32,
    reserved: [u32; 4],
}

#[repr(C)]
struct FbBitfield {
    offset: u32,
    length: u32,
    msb_right: u32,
}

fn main() -> io::Result<()> {
    let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?;

    let mut vinfo = FbVarScreenInfo::default();
    unsafe {
        let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8;
        let len = size_of::<FbVarScreenInfo>();
        libc::ioctl(fb.as_raw_fd(), 0x4600, ptr);
    }

    let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize];

    // Clear the buffer to black
    buffer.iter_mut().for_each(|x| *x = 0);

    // Write the buffer to the framebuffer
    fb.write_all(&buffer)?;

    Ok(())
}

这段代码通过使用ioctl函数和/dev/fb0设备文件来获取屏幕信息并清空屏幕,使其短暂地变成黑色。请注意,这段代码假定您的系统支持Framebuffer,并且您已经以root身份运行了该程序。

报错:

9  | struct FbVarScreenInfo {
   | ---------------------- function or associated item `default` not found for this struct
...
51 |     let mut vinfo = FbVarScreenInfo::default();
   |                                      ^^^^^^^ function or associated item not found in `FbVarScreenInfo`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `default`, perhaps you need to implement it:
           candidate #1: `Default`

非常抱歉,我在之前提供的代码中犯了错误。由于FbVarScreenInfo是一个自定义结构体,它没有默认值。您需要手动实现Default trait,以便在声明vinfo时可以使用FbVarScreenInfo::default()

以下是更新后的代码:

use std::fs::OpenOptions;
use std::io::{self, Write};
use std::mem::size_of;

const FB_DEVICE: &str = "/dev/fb0";

#[repr(C)]
struct FbVarScreenInfo {
    xres: u32,
    yres: u32,
    xres_virtual: u32,
    yres_virtual: u32,
    xoffset: u32,
    yoffset: u32,
    bits_per_pixel: u32,
    grayscale: u32,
    red: FbBitfield,
    green: FbBitfield,
    blue: FbBitfield,
    transp: FbBitfield,
    nonstd: u32,
    activate: u32,
    height: u32,
    width: u32,
    accel_flags: u32,
    pixclock: u32,
    left_margin: u32,
    right_margin: u32,
    upper_margin: u32,
    lower_margin: u32,
    hsync_len: u32,
    vsync_len: u32,
    sync: u32,
    vmode: u32,
    rotate: u32,
    colorspace: u32,
    reserved: [u32; 4],
}

impl Default for FbVarScreenInfo {
    fn default() -> Self {
        FbVarScreenInfo {
            xres: 0,
            yres: 0,
            xres_virtual: 0,
            yres_virtual: 0,
            xoffset: 0,
            yoffset: 0,
            bits_per_pixel: 0,
            grayscale: 0,
            red: FbBitfield::default(),
            green: FbBitfield::default(),
            blue: FbBitfield::default(),
            transp: FbBitfield::default(),
            nonstd: 0,
            activate: 0,
            height: 0,
            width: 0,
            accel_flags: 0,
            pixclock: 0,
            left_margin: 0,
            right_margin: 0,
            upper_margin: 0,
            lower_margin: 0,
            hsync_len: 0,
            vsync_len: 0,
            sync: 0,
            vmode: 0,
            rotate: 0,
            colorspace: 0,
            reserved: [0; 4],
        }
    }
}

#[repr(C)]
struct FbBitfield {
    offset: u32,
    length: u32,
    msb_right: u32,
}

impl Default for FbBitfield {
    fn default() -> Self {
        FbBitfield {
            offset: 0,
            length: 0,
            msb_right: 0,
        }
    }
}

fn main() -> io::Result<()> {
    let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?;

    let mut vinfo = FbVarScreenInfo::default();
    unsafe {
        let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8;
        let len = size_of::<FbVarScreenInfo>();
        libc::ioctl(fb.as_raw_fd(), 0x4600, ptr);
    }

    let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize];

    // Clear the buffer to black
    buffer.iter_mut().for_each(|x| *x = 0);

    // Write the buffer to the framebuffer
    fb.write_all(&buffer)?;

    Ok(())
}

这样,您应该能够成功编译并运行这段代码了。

cargo add libc
# from macOS to arm-v7
cargo build --target armv7-unknown-linux-musleabihf --release

musl c库

几种C库比较(musl uClibc dietlibc glibc) [https://www.bytenote.net/article/163117942798548993] musl libc和glibc是两个不同的C标准库,用于提供C语言程序运行所需的基础功能。它们在实现上存在一些重要的差异:

实现方式:musl libc是一个简单,轻量级的C标准库,它的设计目标是实现纯粹的C标准,没有任何额外的功能。相反,glibc是一个功能非常强大的标准库,提供了大量的扩展功能,以及与操作系统内核进行交互的特殊接口。 兼容性:由于glibc提供了大量的额外功能,因此它与各种操作系统,硬件平台以及第三方库之间的兼容性更好。然而,这也使得glibc的代码实现更加复杂,可维护性更加困难。 性能:由于musl libc的设计目标是简单和轻量级,因此它的运行速度通常比glibc更快。同时,由于musl libc代码实现更简单,它的内存使用量也通常更少。

根据获取的eips信息修正代码

Fixed framebuffer info
    id:              mxc_epdc_fb    smem_start:       0x80C00000
    smem_len:            3268608    type:          PACKED_PIXELS
    type_aux:                  0    visual:   STATIC_PSEUDOCOLOR
    xpanstep:                  1    ypanstep:                  1
    ywrapstep:                 0    line_length:             608
    mmio_start:       0x00000000    mmio_len:                  0
    accel:                     0

Variable framebuffer info
    xres:                    600    yres:                    800
    xres_virtual:            608    yres_virtual:           5376
    xoffset:                   0    yoffset:                   0
    bits_per_pixel:            8    grayscale:                 1
    red.offset:                0    green.offset:              0
    red.length:                8    green.length:              8
    red.msb_right:             0    green.msb_right:           0
    blue.offset:               0    transp.offset:             0
    blue.length:               8    transp.length:             0
    blue.msb_right:            0    transp.msb_right:          0
    nonstd:                    0    activate:                128
    width:                    91    height:                  122
    accel_flags:               0    pixclock:           27027027
    left_margin:               8    right_margin:            100
    upper_margin:              4    lower_margin:              8
    hsync_len:                 4    vsync_len:                 1
    sync:                      0    vmode:                     0
    rotate:                    3
DPI: 167.472527 166.557377 167
waveform type = 2
material type = 0
temperature = 28 (C)

根据如上信息,修正如下代码,给出完整代码;

use std::fs::OpenOptions;
use std::io::{self, Write};
use std::mem::size_of;
use std::os::fd::AsRawFd;

const FB_DEVICE: &str = "/dev/fb0";

#[repr(C)]
struct FbVarScreenInfo {
    xres: u32,
    yres: u32,
    xres_virtual: u32,
    yres_virtual: u32,
    xoffset: u32,
    yoffset: u32,
    bits_per_pixel: u32,
    grayscale: u32,
    red: FbBitfield,
    green: FbBitfield,
    blue: FbBitfield,
    transp: FbBitfield,
    nonstd: u32,
    activate: u32,
    height: u32,
    width: u32,
    accel_flags: u32,
    pixclock: u32,
    left_margin: u32,
    right_margin: u32,
    upper_margin: u32,
    lower_margin: u32,
    hsync_len: u32,
    vsync_len: u32,
    sync: u32,
    vmode: u32,
    rotate: u32,
    colorspace: u32,
    reserved: [u32; 4],
}

impl Default for FbVarScreenInfo {
    fn default() -> Self {
        FbVarScreenInfo {
            xres: 0,
            yres: 0,
            xres_virtual: 0,
            yres_virtual: 0,
            xoffset: 0,
            yoffset: 0,
            bits_per_pixel: 0,
            grayscale: 0,
            red: FbBitfield::default(),
            green: FbBitfield::default(),
            blue: FbBitfield::default(),
            transp: FbBitfield::default(),
            nonstd: 0,
            activate: 0,
            height: 0,
            width: 0,
            accel_flags: 0,
            pixclock: 0,
            left_margin: 0,
            right_margin: 0,
            upper_margin: 0,
            lower_margin: 0,
            hsync_len: 0,
            vsync_len: 0,
            sync: 0,
            vmode: 0,
            rotate: 0,
            colorspace: 0,
            reserved: [0; 4],
        }
    }
}

#[repr(C)]
struct FbBitfield {
    offset: u32,
    length: u32,
    msb_right: u32,
}

impl Default for FbBitfield {
    fn default() -> Self {
        FbBitfield {
            offset: 0,
            length: 0,
            msb_right: 0,
        }
    }
}

fn main() -> io::Result<()> {
    let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?;

    let mut vinfo = FbVarScreenInfo::default();
    unsafe {
        let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8;
        let len = size_of::<FbVarScreenInfo>();
        libc::ioctl(fb.as_raw_fd(), 0x4600, ptr);
        println!("request ioctl ok!\n");
    }

    let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize];

    // Clear the buffer to black
    buffer.iter_mut().for_each(|x| *x = 0);
    
    // Write the buffer to the framebuffer
    fb.write_all(&buffer)?;
    println!("Write the buffer to the framebuffer!\n");
    Ok(())
}

结果:

use std::fs::OpenOptions;
use std::io::{self, Write};
use std::mem::size_of;
use std::os::fd::AsRawFd;

const FB_DEVICE: &str = "/dev/fb0";

#[repr(C)]
struct FbVarScreenInfo {
    xres: u32,
    yres: u32,
    xres_virtual: u32,
    yres_virtual: u32,
    xoffset: u32,
    yoffset: u32,
    bits_per_pixel: u32,
    grayscale: u32,
    red: FbBitfield,
    green: FbBitfield,
    blue: FbBitfield,
    transp: FbBitfield,
    nonstd: u32,
    activate: u32,
    height: u32,
    width: u32,
    accel_flags: u32,
    pixclock: u32,
    left_margin: u32,
    right_margin: u32,
    upper_margin: u32,
    lower_margin: u32,
    hsync_len: u32,
    vsync_len: u32,
    sync: u32,
    vmode: u32,
    rotate: u32,
    colorspace: u32,
    reserved: [u32; 4],
}

impl Default for FbVarScreenInfo {
    fn default() -> Self {
        FbVarScreenInfo {
            xres: 0,
            yres: 0,
            xres_virtual: 0,
            yres_virtual: 0,
            xoffset: 0,
            yoffset: 0,
            bits_per_pixel: 0,
            grayscale: 0,
            red: FbBitfield::default(),
            green: FbBitfield::default(),
            blue: FbBitfield::default(),
            transp: FbBitfield::default(),
            nonstd: 0,
            activate: 0,
            height: 0,
            width: 0,
            accel_flags: 0,
            pixclock: 0,
            left_margin: 0,
            right_margin: 0,
            upper_margin: 0,
            lower_margin: 0,
            hsync_len: 0,
            vsync_len: 0,
            sync: 0,
            vmode: 0,
            rotate: 0,
            colorspace: 0,
            reserved: [0; 4],
        }
    }
}

#[repr(C)]
struct FbBitfield {
    offset: u32,
    length: u32,
    msb_right: u32,
}

impl Default for FbBitfield {
    fn default() -> Self {
        FbBitfield {
            offset: 0,
            length: 0,
            msb_right: 0,
        }
    }
}

fn main() -> io::Result<()> {
    let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?;

    let mut vinfo = FbVarScreenInfo::default();
    unsafe {
        let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8;
        let len = size_of::<FbVarScreenInfo>();
        libc::ioctl(fb.as_raw_fd(), 0x4600, ptr);
        println!("request ioctl ok!\n");
    }

    let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize];

    // Clear the buffer to black
    buffer.iter_mut().for_each(|x| *x = 0);
    
    // Write the buffer to the framebuffer
    fb.write_all(&buffer)?;
    println!("Write the buffer to the framebuffer!\n");
    Ok(())
}

分析eips

which eips
#/usr/sbin/eips
cp /usr/sbin/eips /mnt/us

分析:

objdump -x /Users/workspace/Desktop/projects/kindle-plugin-touch/files/eips

结果:

/Users/workspace/Desktop/projects/kindle-plugin-touch/files/eips:       file format elf32-littlearm
architecture: arm
start address: 0x000096b8

Program Header:
 UNKNOWN off    0x0000cb9c vaddr 0x00014b9c paddr 0x00014b9c align 2**2
         filesz 0x00000340 memsz 0x00000340 flags r--
    PHDR off    0x00000034 vaddr 0x00008034 paddr 0x00008034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r-x
  INTERP off    0x00000134 vaddr 0x00008134 paddr 0x00008134 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
    LOAD off    0x00000000 vaddr 0x00008000 paddr 0x00008000 align 2**15
         filesz 0x0000cee0 memsz 0x0000cee0 flags r-x
    LOAD off    0x0000d000 vaddr 0x0001d000 paddr 0x0001d000 align 2**15
         filesz 0x000008d0 memsz 0x00005b00 flags rw-
 DYNAMIC off    0x0000d00c vaddr 0x0001d00c paddr 0x0001d00c align 2**2
         filesz 0x000000f8 memsz 0x000000f8 flags rw-
    NOTE off    0x00000148 vaddr 0x00008148 paddr 0x00008148 align 2**2
         filesz 0x00000020 memsz 0x00000020 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rw-

Dynamic Section:
  NEEDED       libm.so.6
  NEEDED       libgcc_s.so.1
  NEEDED       libc.so.6
  INIT         0x00008b1c
  FINI         0x00012ce0
  INIT_ARRAY   0x0001d000
  INIT_ARRAYSZ 0x00000004
  FINI_ARRAY   0x0001d004
  FINI_ARRAYSZ 0x00000004
  GNU_HASH     0x0000818c
  STRTAB       0x000086ec
  SYMTAB       0x000083bc
  STRSZ        0x000001e0
  SYMENT       0x00000010
  DEBUG        0x00000000
  PLTGOT       0x0001d104
  PLTRELSZ     0x00000160
  PLTREL       0x00000011
  JMPREL       0x000089bc
  REL          0x000089a4
  RELSZ        0x00000018
  RELENT       0x00000008
  VERNEED      0x00008934
  VERNEEDNUM   0x00000003
  VERSYM       0x000088cc

Version References:
  required from libgcc_s.so.1:
    0x0b792655 0x00 04 GCC_3.5
  required from libm.so.6:
    0x0d696914 0x00 03 GLIBC_2.4
  required from libc.so.6:
    0x06969196 0x00 05 GLIBC_2.16
    0x0d696914 0x00 02 GLIBC_2.4

Sections:
Idx Name            Size     VMA      Type
  0                 00000000 00000000 
  1 .interp         00000013 00008134 DATA
  2 .note.ABI-tag   00000020 00008148 
  3 .gnu.hash       00000230 0000818c 
  4 .dynsym         00000330 000083bc 
  5 .dynstr         000001e0 000086ec 
  6 .gnu.version    00000066 000088cc 
  7 .gnu.version_r  00000070 00008934 
  8 .rel.dyn        00000018 000089a4 
  9 .rel.plt        00000160 000089bc 
 10 .init           0000000c 00008b1c TEXT
 11 .plt            00000224 00008b28 TEXT
 12 .text           00009f90 00008d50 TEXT
 13 .fini           00000008 00012ce0 TEXT
 14 .rodata         00001afa 00012ce8 DATA
 15 .ARM.extab      000003b8 000147e4 DATA
 16 .ARM.exidx      00000340 00014b9c 
 17 .eh_frame       00000004 00014edc DATA
 18 .init_array     00000004 0001d000 
 19 .fini_array     00000004 0001d004 
 20 .jcr            00000004 0001d008 DATA
 21 .dynamic        000000f8 0001d00c 
 22 .got            000000c0 0001d104 DATA
 23 .data           00000708 0001d1c8 DATA
 24 .bss            00005230 0001d8d0 BSS
 25 .comment        00000011 00000000 
 26 .ARM.attributes 00000035 00000000 
 27 .gnu_debuglink  00000010 00000000 
 28 .shstrtab       00000101 00000000 

SYMBOL TABLE:

给出kindle的eips命令的源码; 以下是Kindle的EIPS命令的Python源代码:

#!/usr/bin/python
#
# eips.py
#
# Kindle eips command line utility
#
# Written by Igor Skochinsky <igor at binary.ninja>
# This code is released into the public domain
#

import os
import sys
import struct
import fcntl
import termios

# ioctl constants
KDSETMODE = 0x4B3A
KD_GRAPHICS = 0x01
KD_TEXT = 0x00
KDFONTOP = 0x4B42
KDFONTOP_SET = 0x01

def ioctl_GWINSZ(fd):
    try:
        cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
    except:
        return None
    return cr

def set_font_size(fd, size):
    fcntl.ioctl(fd, KDFONTOP, KDFONTOP_SET*size)

def set_graphics_mode(fd):
    fcntl.ioctl(fd, KDSETMODE, KD_GRAPHICS)

def set_text_mode(fd):
    fcntl.ioctl(fd, KDSETMODE, KD_TEXT)

def clear_screen():
    os.system("clear")

def print_usage():
    print("Usage: eips [options] [text]")
    print("Options:")
    print("    -h, --help          display this help message")
    print("    -c, --clear         clear the screen")
    print("    -g, --graphics      set graphics mode")
    print("    -t, --text          set text mode")
    print("    -f, --fontsize <n>  set font size (1-4)")
    print("    -x <n>              set X position (0-758)")
    print("    -y <n>              set Y position (0-1023)")
    print("    -i, --image <file>  display image file (JPEG or PNG)")

def print_text(text, x, y):
    print("\033[%d;%dH%s" % (y, x, text))

def display_image(imagefile, x, y):
    try:
        import Image
    except ImportError:
        print("Python Imaging Library is required to display images")
        return

    try:
        im = Image.open(imagefile)
        im = im.convert("L")
        im = im.resize((758, 1023))
    except:
        print("Failed to open image file")
        return

    pix = im.load()
    for j in range(0, 1023):
        for i in range(0, 758):
            if pix[i,j] > 128:
                print_text("#", x+i, y+j)

def main():
    x = 0
    y = 0
    mode = "text"
    font_size = 1
    imagefile = None
    text = None

    # parse arguments
    i = 1
    while i < len(sys.argv):
        arg = sys.argv[i]
        if arg == "-h" or arg == "--help":
            print_usage()
            return
        elif arg == "-c" or arg == "--clear":
            clear_screen()
        elif arg == "-g" or arg == "--graphics":
            mode = "graphics"
        elif arg == "-t" or arg == "--text":
            mode = "text"
        elif arg == "-f" or arg == "--fontsize":
            i += 1
            if i >= len(sys.argv):
                print("Missing font size argument")
                return
            font_size = int(sys.argv[i])
            if font_size < 1 or font_size > 4:
                print("Invalid font size")
                return
        elif arg == "-x":
            i += 1
            if i >= len(sys.argv):
                print("Missing X position argument")
                return
            x = int(sys.argv[i])
            if x < 0 or x > 758:
                print("Invalid X position")
                return
        elif arg == "-y":
            i += 1
            if i >= len(sys.argv):
                print("Missing Y position argument")
                return
            y = int(sys.argv[i])
            if y < 0 or y > 1023:
                print("Invalid Y position")
                return
        elif arg == "-i" or arg == "--image":
            i += 1
            if i >= len(sys.argv):
                print("Missing image file argument")
                return
            imagefile = sys.argv[i]
        else:
            text = arg
        i += 1

    # set font size
    fd = os.open("/dev/tty1", os.O_WRONLY)
    set_font_size(fd, font_size)

    # set mode
    if mode == "graphics":
        set_graphics_mode(fd)
    else:
        set_text_mode(fd)

    # display image
    if imagefile is not None:
        display_image(imagefile, x, y)
    # display text
    elif text is not None:
        print_text(text, x, y)

    os.close(fd)

if __name__ == "__main__":
    main()

这个源码实现了Kindle的EIPS命令的各种选项,包括清除屏幕、设置文本/图形模式、设置字体大小、设置文本位置和显示图像等。

rust运行shell命令

使用RUST;使用Linux;使用musl c库;在代码中调用SHELL的echo hello命令;给出代码; 以下是一个使用Rust,并在Linux上使用musl c库的示例代码,该代码将调用SHELL的echo hello命令并将其输出到终端:

use std::process::Command;

fn main() {
    let output = Command::new("sh")
        .arg("-c")
        .arg("echo hello")
        .output()
        .expect("failed to execute process");

    let hello_str = String::from_utf8(output.stdout).unwrap();
    println!("{}", hello_str);
}

在这个例子中,我们使用std::process::Command模块来启动一个新的进程并运行sh -c "echo hello"命令。该命令将在SHELL中运行,并将输出发送到标准输出流。我们使用String::from_utf8()方法将输出转换为字符串,并将其打印到终端上。

请注意,我们在这个例子中使用了musl C库,这是一个轻量级的C标准库,它可以在Linux系统上提供更小的二进制文件大小和更快的启动时间。如果您想在其他操作系统上运行此代码,您可能需要使用不同的C库或编译选项。

使用RUST调用eips命令

使用RUST;使用Linux;使用musl c库;从https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg获取图片并保存到/mnt/us/img1.jpg;在代码中转换/mnt/us/img1.jpg的格式为bitmap并保存到mnt/us/img1.bmp;调用shell的eips -b /mnt/us/img1.bmp以显示图片;给出完整代码; 以下是使用RUST编写的程序,用于从给定的URL下载图片并将其转换为位图格式,最后在Kindle设备上显示:

use std::fs::File;
use std::io::prelude::*;
use std::process::Command;
use image::{GenericImageView, ImageFormat};

fn main() {
    // Download the image from the URL
    let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg";
    let mut response = reqwest::get(url).unwrap();
    let mut buffer = Vec::new();
    response.read_to_end(&mut buffer).unwrap();

    // Save the image to the file system
    let mut file = File::create("/mnt/us/img1.jpg").unwrap();
    file.write_all(&buffer).unwrap();

    // Convert the image to bitmap format
    let img = image::open("/mnt/us/img1.jpg").unwrap();
    let bmp = img.to_bmp();

    // Save the bitmap to the file system
    let mut file = File::create("/mnt/us/img1.bmp").unwrap();
    bmp.write(&mut file, ImageFormat::Bmp).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-b")
                         .arg("/mnt/us/img1.bmp")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

修改为使用wget命令获取https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg文件; 修改后的代码如下:

use std::process::Command;
use image::{GenericImageView, ImageFormat};

fn main() {
    // Download the image from the URL using wget command
    let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1.jpg")
                         .arg(url)
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));

    // Convert the image to bitmap format
    let img = image::open("/mnt/us/img1.jpg").unwrap();
    let bmp = img.to_bmp();

    // Save the bitmap to the file system
    let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap();
    bmp.write(&mut file, ImageFormat::Bmp).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-b")
                         .arg("/mnt/us/img1.bmp")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

这个代码使用了wget命令来下载图片,而不是使用reqwest库。修改后的代码与之前的代码相比,只有少量的更改。我们用Command::new("wget")来生成一个wget命令,然后使用.arg()方法来传递命令行参数。在这个例子中,我们使用了-O参数来指定输出文件名,url参数来指定要下载的URL。最后,我们使用.output()方法来执行命令并获取输出。

请注意,此程序需要在Linux操作系统上运行,并使用musl c库。在运行之前,请确保已经安装了必要的依赖项(例如Rust编译器、reqwest和image库)。

报错:

 cargo:rerun-if-env-changed=ARMV7_UNKNOWN_LINUX_MUSLEABIHF_OPENSSL_DIR
  ARMV7_UNKNOWN_LINUX_MUSLEABIHF_OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_DIR
  OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_NO_PKG_CONFIG
--- stderr
  thread 'main' panicked at '

  Could not find directory of OpenSSL installation, and this `-sys` crate cannot
  proceed without this knowledge. If OpenSSL is installed and this crate had
  trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
  compilation process.

  Make sure you also have the development packages of openssl installed.
  For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.

  If you're in a situation where you think the directory *should* be found
  automatically, please open a bug at https://github.com/sfackler/rust-openssl
  and include information about your system as well as this message.

  $HOST = aarch64-apple-darwin
  $TARGET = armv7-unknown-linux-musleabihf
  openssl-sys = 0.9.87

  ', /Users/workspace/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-sys-0.9.87/build/find_normal.rs:190:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

这是一个Rust Cargo构建脚本中的一些语句,用于检查环境变量并在必要时重新运行构建脚本。这些语句通常与OpenSSL库的构建和链接相关。

如果你遇到了这些语句并且不知道该怎么做,可以尝试以下步骤:

  1. 检查你的环境变量是否正确设置了OpenSSL的路径。如果你已经安装了OpenSSL,可以尝试设置OPENSSL_DIR环境变量来指定OpenSSL的路径。

  2. 如果你不确定要设置什么环境变量,可以尝试使用pkg-config来获取OpenSSL的路径。你可以安装pkg-config,然后运行pkg-config --cflags openssl来获取OpenSSL的C编译器标志,或者运行pkg-config --libs openssl来获取OpenSSL的链接器标志。

  3. 如果你仍然遇到问题,可以尝试安装或重新安装OpenSSL库,并确保它已正确安装并配置。然后重新运行Cargo构建命令。

如果你仍然无法解决问题,你可以尝试搜索类似的错误信息,或者在Rust社区中寻求帮助。 你可以使用 image crate 来将 JPG 图片转换为 PNG 图片。下面是一个简单的 Rust 代码示例:

use image::io::Reader as ImageReader;
use image::ImageFormat;

fn main() {
    // 打开 JPG 图片
    let img = ImageReader::open("input.jpg").unwrap().decode().unwrap();

    // 将图片保存为 PNG 格式
    img.save_with_format("output.png", ImageFormat::Png).unwrap();
}

这个示例首先使用 ImageReader 打开 JPG 图片,然后将其解码为 DynamicImage。接下来,它使用 save_with_format 方法将图片保存为 PNG 格式。你可以将 "input.jpg" 替换为你想要转换的 JPG 图片的路径,将 "output.png" 替换为你想要保存 PNG 图片的路径。

请确保在你的 Cargo.toml 文件中添加了以下依赖项:

[dependencies]
image = "0.23.12"

这将允许你使用 image crate 来处理图像。

    // Convert the image to bitmap format
    let img = image::open("/mnt/us/img1.jpg").unwrap();
    let bmp = img.save_with_format("", format)

    // Save the bitmap to the file system
    let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap();
    bmp.write(&mut file, ImageFormat::Bmp).unwrap();

修正如上代码; 你的代码中有一个小错误,save_with_format 方法返回一个 Result<(), std::io::Error> 类型的结果,因此你需要使用 unwrap() 或其他错误处理方法来处理它的结果。下面是一个修正后的代码示例:

use image::io::Reader as ImageReader;
use image::{DynamicImage, ImageFormat};

fn main() {
    // 打开 JPG 图片
    let img = ImageReader::open("/mnt/us/img1.jpg").unwrap().decode().unwrap();

    // 将图片保存为 BMP 格式
    let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap();

    // 将 BMP 图片保存到文件系统
    let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap();
    bmp.write(&mut file, ImageFormat::Bmp).unwrap();
}

这个示例首先使用 ImageReader 打开 JPG 图片,然后将其解码为 DynamicImage。接下来,它使用 save_with_format 方法将图片保存为 BMP 格式,并将结果保存到 bmp 变量中。最后,它打开一个文件并将 bmp 变量中的 BMP 图片写入该文件中。

请注意,save_with_format 方法将 BMP 图片直接保存到文件系统中,因此你不需要再次将其写入文件中。

/opt/homebrew/Homebrew/Cellar/openssl@3/3.0.7

报错:

use image::{GenericImageView, ImageFormat};
  --> src/main.rs:17:19
   |
17 |     let bmp = img.to_bmp();
   |                   ^^^^^^ method not found in `DynamicImage`

这个错误提示表明在 DynamicImage 上找不到 to_bmp 方法。这可能是因为 to_bmp 方法已经被删除或者被移动到了另一个模块中。

你可以检查你的代码,确保你正确导入了 image crate,并且使用了最新版本的 image crate。你也可以查看 image crate 的文档,以确定 to_bmp 方法是否已被删除或移动。如果 to_bmp 方法已经被删除或移动,你需要查找替代方法或更新你的代码以适应最新版本的 image crate。

编译:

cargo add reqwest
cargo add image
export OPENSSL_DIR=/opt/homebrew/Homebrew/Cellar/openssl@3/3.0.7
echo $OPENSSL_DIR
cargo build --target armv7-unknown-linux-musleabihf --release

配置vscode插件的网络代理

要配置VSCode插件的网络代理,您需要按照以下步骤进行操作:

  1. 打开VSCode并进入“扩展”选项卡。
  2. 在搜索栏中输入“settings.json”并点击“打开设置(JSON)”。
  3. 在打开的JSON文件中,添加以下代码:
"http.proxy": "http://yourproxyaddress:yourproxyport",
"https.proxy": "http://yourproxyaddress:yourproxyport",
"http.proxyStrictSSL": false

请注意,您需要将“yourproxyaddress”和“yourproxyport”替换为您的代理服务器地址和端口号。如果您的代理服务器需要身份验证,则还需要添加以下代码:

"http.proxyAuthorization": "basic base64encodedcredentials"

在这里,您需要将“base64encodedcredentials”替换为您的代理服务器的用户名和密码的base64编码。

  1. 保存文件并重新启动VSCode,以确保更改生效。

这样,您就可以成功配置VSCode插件的网络代理。

rust的 Blocking waiting for file lock on package cache

原因:rust-analyzer插件

rust的 Blocking waiting for file lock on package cache,如何手动解锁文件? 在Rust中,如果您尝试在使用cargo命令时看到“Blocking waiting for file lock on package cache”错误消息,这意味着某些文件正在被锁定,从而阻止了cargo的正常运行。您可以尝试手动解锁文件来解决此问题。以下是一些可能的解决方法:

  1. 关闭所有正在运行的Rust程序和IDE,并等待一段时间,以确保任何正在使用缓存的进程已经全部关闭。然后再次尝试运行cargo命令,看看问题是否已经解决。

  2. 如果您使用的是Windows操作系统,请尝试使用“资源监视器”来查找并解锁被锁定的文件。打开资源监视器,然后转到“CPU”选项卡,然后选择“关联的句柄”选项卡。在“搜索句柄”字段中输入“package_cache”,然后查找与该文件关联的任何进程。选择进程,然后单击“关闭句柄”按钮,以解锁文件。

  3. 如果您使用的是Linux或MacOS操作系统,请尝试使用“lsof”命令来查找并解锁被锁定的文件。打开终端窗口,然后输入以下命令:

lsof | grep package_cache

这将显示与该文件关联的任何进程。找到进程ID(PID),然后使用以下命令杀死该进程:

kill -9 PID

这将强制终止该进程,并解锁文件。

请注意,手动解锁文件可能会导致数据丢失或其他问题,因此请务必在进行此操作之前备份所有相关文件。

kindle的eips命令显示图片变形

kindle的eips命令显示图片变形是什么原因?图片尺寸与屏幕尺寸相同. 可能是由于图片的分辨率不够高,或者是图片格式不受Kindle支持所导致的。EIPS命令在Kindle上用于显示电子墨水屏幕上的图像,但是如果图片的分辨率不足,它可能会在显示时被拉伸或缩小,从而导致变形。

为了避免这种情况,您可以尝试将图片的分辨率增加到与Kindle屏幕分辨率相同,或者将图片转换为Kindle支持的格式,例如JPEG或PNG格式。您还可以尝试使用其他命令,例如FBI或MANGA,以查看是否会产生相同的变形问题。

eips帮助:

 For complete usage and examples, search eips in wiki page.
usage:
to print strings: eips [row] [col] [-h] string
		 -h for highlighted text
to show image: eips -g|-b image_path [-w waveform -f -x xpos -y ypos -v]
		 -g for png, -b for bitmap
		 -w waveform can be gc16, gl16, reagl, reagld, gck16, glkw16 or du, default is gc16,
		 -f for full update, default is partial update
		 -x -y in units of pixels
		 -v for inverted picture
		 -k filename, save workingbuffer to file
to paint a retangle on screen: eips -d l=a,w=b,h=c [-x xx -y yy -w wf]
		 a is gray level in hex, b is width, c is height, with optional xx yy location, waveform mode
to clear display: eips -c 
to paint checker: eips -q 
to paint grayscale: eips -l 
to paint pattern: eips -p 
to paint barcode: eips -r barcode
to overlay a grid: eips -a 5 (grid size)
to print fb info: eips -i
to flash display with current fb content: eips -s w=758,h=1024 -f

根据如上命令帮助,给出显示/mnt/us/output.png图片的命令;

解释waveform模式和update模式 在Kindle设备上,波形模式和更新模式是用于控制电子墨水显示的两个重要参数。

  1. 波形模式

波形模式是指在Kindle设备上用于控制电子墨水显示的电压波形。Kindle设备支持多种波形模式,包括gc16、gl16、reagl、reagld、gck16、glkw16和du等。每种波形模式都有不同的优点和缺点,例如显示速度、灰度级别和能耗等方面的差异。

  1. 更新模式

更新模式是指在Kindle设备上更新电子墨水屏幕的方式。Kindle设备支持两种更新模式:部分更新和全屏更新。部分更新模式仅更新屏幕上需要更改的部分,而全屏更新模式则会更新整个屏幕。部分更新模式可以更快地更新屏幕,但可能会导致一些残留效应,而全屏更新模式则可以更好地清除屏幕上的残留效应,但更新速度较慢。

在使用eips命令显示图片时,可以使用-w选项来指定波形模式,并使用-f选项来指定更新模式。例如,使用-eips -w reagl -f -g /mnt/us/output.png命令可以使用reagl波形模式并进行全屏更新来显示图片。

使用Python查看png文件的元数据

使用Python查看/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png图片的所有元信息;给出代码; 可以使用Python中的Pillow库来查看图片的元信息。以下是一个示例代码,可以读取并打印指定路径下图片的元信息:

from PIL import Image

# 指定图片路径
image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png'

# 打开图片并获取元信息
with Image.open(image_path) as img:
    exif_data = img._getexif()

# 打印元信息
print(exif_data)

注意,元信息的格式可能因图片类型而异。如果要查看更多特定于图片类型的元信息,请查看Pillow文档。 可以使用Python中的Pillow库来查看图片的元信息。以下是一个示例代码,可以读取并打印指定路径下图片的所有元信息:

from PIL import Image

# 指定图片路径
image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png'

# 打开图片并获取元信息
with Image.open(image_path) as img:
    exif_data = img._getexif()

# 打印元信息
for tag_id in exif_data:
    tag = TAGS.get(tag_id, tag_id)
    data = exif_data.get(tag_id)
    if isinstance(data, bytes):
        data = data.decode()
    print(f"{tag:25}: {data}")

注意,元信息的格式可能因图片类型而异。如果要查看更多特定于图片类型的元信息,请查看Pillow文档。 报错:

TypeError                                 Traceback (most recent call last)
/Users/workspace/Desktop/projects/kindle-plugin-touch/files/test1.ipynb 单元格 1 in ()
      8     exif_data = img._getexif()
     10 # 打印元信息
---> 11 for tag_id in exif_data:
     12     tag = ExifTags.get(tag_id, tag_id)
     13     data = exif_data.get(tag_id)

TypeError: 'NoneType' object is not iterable

这个错误可能是因为该图片没有元信息或元信息无法被解析。在Pillow中,如果图片没有元信息或无法解析元信息,则_getexif()方法将返回None。在这种情况下,我们可以添加一个条件语句来检查exif_data是否为None,以避免迭代None对象而导致的TypeError错误。以下是修改后的代码:

from PIL import Image, ExifTags

# 指定图片路径
image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png'

# 打开图片并获取元信息
with Image.open(image_path) as img:
    exif_data = img._getexif()

# 打印元信息
if exif_data:
    for tag_id in exif_data:
        tag = ExifTags.get(tag_id, tag_id)
        data = exif_data.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        print(f"{tag:25}: {data}")
else:
    print("该图片没有元信息或元信息无法被解析。")

这样,如果图片没有元信息或无法解析元信息,则代码将输出一条相应的提示消息。

在kterm外显示图片

#!/bin/sh
touch /mnt/us/extensions/touch/1.txt
#禁止休眠
lipc-set-prop -i com.lab126.powerd preventScreenSaver 1
#显示图片
#eips -c
#eips -g /mnt/us/extensions/touch/kindle.png
eips -w reagl -f -g /mnt/us/error.png
#eips ''
#开启wifi
lipc-set-prop com.lab126.cmd wirelessEnable 1
#延时5s
sleep 5
#显示另一张图片
#eips -g /mnt/us/extensions/touch/hello.jpg
#延时3秒
#sleep 3
#获取新图
if curl "https://www.qsbye.cn/infoimg/kindle.png"|tee /mnt/us/extensions/touch/kindle.png;then
#eips -c
eips -g /mnt/us/extensions/touch/kindle.png
else
if eips -g /mnt/us/extensions/touch/default.png;then
#sleep 2
eips -g /mnt/us/extensions/touch/error.png
fi
fi
#延时8小时
sleep 8h
#循环获取图片
/mnt/us/extensions/touch/touch.sh
#调试
touch /mnt/us/extensions/touch/2.txt

图片的颜色描述文件和色彩空间

png图片的色彩空间为RGB和Grey有什么不同?在kindle上只有Grey空间的图片能按照原来的比例正确显示的原因是什么? RGB和Grey是两种不同的色彩空间。RGB是一种彩色空间,由红色、绿色和蓝色三个基本颜色组成,可以产生各种颜色。而Grey是一种灰度空间,只有黑白两种颜色,没有彩色。

在Kindle等电子墨水屏幕上,只有Grey空间的图片能够按照原来的比例正确显示。这是因为电子墨水屏幕只能显示黑白两种颜色,无法显示彩色。因此,如果一个彩色图片在电子墨水屏幕上显示,必须将其转换为灰度图像。在这种情况下,只有Grey空间的图片能够按照原来的比例正确显示,因为它们只有一个颜色通道,而RGB空间的图片有三个颜色通道,转换为灰度图像时需要进行颜色通道的合并和平均,会导致比例失真。

转换图片为Grey空间

use std::process::Command;
use image::{GenericImageView, ImageFormat,GrayImage, RgbImage};

fn main() {
    // Download the image from the URL using wget command
    let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1.jpg")
                         .arg(url)
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));

    // Convert the image to bitmap format
    let img = image::open("/mnt/us/img1.jpg").unwrap();
    // 将图片保存为 BMP 格式
    let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-b")
                         .arg("/mnt/us/img1.bmp")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

将bmp保存为Grey颜色空间;使用eips -f -b /mnt/us/img1.bmp显示图片;修正如上代码;

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};

fn main() {
    // Download the image from the URL using wget command
    let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1.jpg")
                         .arg(url)
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));

    // Convert the image to bitmap format and save as gray color space
    let img = image::open("/mnt/us/img1.jpg").unwrap().into_luma8();
    let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-b")
                         .arg("/mnt/us/img1.bmp")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

将图片转换为为png格式而不是bmp格式;使用eips -f -g /mnt/us/img1.png显示图片;修正如上代码; 好的,以下是更新后的代码:

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};

fn main() {
    // Download the image from the URL using wget command
    let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1.jpg")
                         .arg(url)
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));

    // Convert the image to bitmap format and save as png color space
    let img = image::open("/mnt/us/img1.jpg").unwrap().into_luma8();
    let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-g")
                         .arg("/mnt/us/img1.png")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

在这个更新后的代码中,我们将图片的格式从bmp更改为png。我们还将eips命令的参数更改为-g,这将使图片以全屏模式显示。

第七天:2023年5月22日

主要内容:php的图片订阅源代码;CSS布局;

更新php的图片订阅

<?php
//php>7.4
/**定时获取消息,生成图片 */
$font_file6="yuanti.ttf";//圆体
/*  *$sourePic:原图路径  * $smallFileName:小图名称  * $width:小图宽  * $heigh:小图高  */
function pngthumb($smallFileName, $width, $heigh,$cityname,$temperature,$humidity,$weather,$weather_logo,$yiyan,$board,$last_update){
    /*创建画布,添加文字*/
    //创建画布600*800
    $im = imagecreatetruecolor($width, $heigh);
    //填充画布背景色
    $color = imagecolorallocate($im, 255, 255, 255);
    imagefill($im, 0, 0, $color);
    //字体文件
    $font_file="heiti.ttf";
    $font_file2="xingshu.ttf";//春联行书体
    $font_file3="tttgb.ttf";//腾讯体
    $font_file4="miaowu.ttf";//喵物体
    $font_file5="yuanti.ttf";//圆体
  //!!!!!!版权原因,就不上传字体文件了,自行替换
    //设定字体颜色
    $font_color_1 = ImageColorAllocate ($im, 140, 140, 140);
    $font_color_2 = ImageColorAllocate ($im, 28, 28, 28);
    $font_color_3 = ImageColorAllocate ($im, 129, 129, 129);
    $font_color_red = ImageColorAllocate ($im, 217, 45, 32);
    $fang_bg_color = ImageColorAllocate ($im, 254, 216, 217);
    //添加文字
    /**参数:画布,字体大小(磅),旋转角度,基线点x,y(大致从左下开始),字体颜色,字体文件,utf8字符串(php以utf8存储)  */
    imagettftext($im, 57,0, 50, 130, $font_color_2 ,$font_file2, $cityname);
    imagettftext($im, 40,0, 50, 300, $font_color_2 ,$font_file, $temperature);
    imagettftext($im, 40,0, 50, 400, $font_color_2 ,$font_file, $humidity);
    imagettftext($im, 28,0, 50, 460, $font_color_2 ,$font_file3, $weather);
    imagettftext($im, 57,0, 400, 460, $font_color_2 ,$font_file, '☁');
    imagettftext($im, 14,0, 50, 600, $font_color_2 ,$font_file4, $yiyan);
    imagettftext($im, 14,0, 300, 50, $font_color_2 ,$font_file, '公告');
    imagettftext($im, 14,0, 300, 100, $font_color_2 ,$font_file5, $board);
    imagettftext($im, 14,0, 50, 700, $font_color_2 ,$font_file,$last_update);
    /****/
    imagepng($im,$smallFileName);//保存到/infoimg目录
    
    return $smallFileName;//返回小图路径
}
function autowrap($fontsize, $angle, $fontface, $string, $width) {
    //自动换行(11个字)
    // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度
      $content = "";
      $content_this_line='';
      // 将字符串拆分成一个个单字 保存到数组 letter 中
      for ($i=0;$i<mb_strlen($string);$i++) {
        $letter[] = mb_substr($string, $i, 1);
      }
    
      foreach ($letter as $l) {
        $teststr = $content_this_line." ".$l;
        $testbox = imagettfbbox($fontsize, $angle, $fontface, $teststr);
        // 判断拼接后的字符串是否超过预设的宽度
        if ($testbox[2] > $width) {
          $content .= "\n";
          $content_this_line='';
        }
        $content_this_line.=$l;
        $content .= $l;
      }
      return $content;
    }
function GetTextFromUrl(){
    $url1='https://api.seniverse.com/v3/weather/now.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c';
    $url2='https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c';
    $url3='https://v1.hitokoto.cn/?encode=text';
    $url4='https://nightchina.net/wp-content/uploads/2014/05/nightchina.net-web-LOGO-1501.jpg';
    $url5='./board.json';
    $url6='https://v1.hitokoto.cn/';
    $url7='https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=ip&language=zh-Hans&unit=c';//通过ip判断地区
    $now_json = file_get_contents($url1);// 获取数据
    $daily_json=file_get_contents($url2);
    $board_json=file_get_contents($url5);
    $yiyan_json=file_get_contents($url6);
    //$yiyan=file_get_contents($url3);
    global $cityname,$weather,$temperature,$humidity,$wind_directioin,$last_update,$yiyan,$board_message,$board_message_time;
    $now_content=json_decode($now_json); 
    $daily_content=json_decode($daily_json);
    $cityname=$now_content->results[0]->location->name;
    $weather=$now_content->results[0]->now->text;
    $temperature='温度:'.$now_content->results[0]->now->temperature.'℃';
    $humidity='湿度:'.$daily_content->results[0]->daily[0]->humidity.'%';
    $wind_directioin='风向:'.$daily_content->results[0]->daily[0]->wind_direction;
    $last_update=$now_content->results[0]->last_update;
    $last_update[10]=' ';$last_update[19]=' ';$last_update[20]=' ';$last_update[21]=' ';$last_update[22]=' ';$last_update[23]=' ';$last_update[24]=' ';
    $last_update='上次更新:'.$last_update;
    //解析公告板
    $board_content=json_decode($board_json);
    $i1=0;
    while($board_content->contents[$i1]->message!=NULL){
        $board_message[$i1]=$board_content->contents[$i1]->message;
        //换行
        $board_message[$i1]=autowrap(14,0,"yuanti.ttf",$board_message[$i1],300);
        $board_message_time[$i1]=$board_content->contents[$i1]->post_time;
        $i1++;
    }
    //解析一言
    $yiyan_content=json_decode($yiyan_json);
    $yiyan=$yiyan_content->hitokoto;
        return ;
}



GetTextFromUrl();
pngthumb("kindle.png", 600, 800,$cityname,$temperature,$humidity,$weather,NULL,$yiyan,$board_message[0]."\n".'('.$board_message_time[0].')',$last_update);//调用
echo '<html><div style="background-color: red;"><img src="kindle.png"></div></html>';
?>

参考如上代码;页面布局:全部内容布局在屏幕上10%和下30%的区域,中间区域不布局内容;不使用如上代码中的字体;使用Python,给出生成png图片的代码; 以下是使用Python生成PNG图片的代码:

from PIL import Image, ImageDraw, ImageFont
import requests
import json

# 获取天气信息
def get_weather_info():
    url1 = 'https://api.seniverse.com/v3/weather/now.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c'
    url2 = 'https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c'
    url5 = './board.json'
    url6 = 'https://v1.hitokoto.cn/'
    url7 = 'https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=ip&language=zh-Hans&unit=c'
    now_json = requests.get(url1).text
    daily_json = requests.get(url2).text
    board_json = requests.get(url5).text
    yiyan_json = requests.get(url6).text
    now_content = json.loads(now_json)
    daily_content = json.loads(daily_json)
    board_content = json.loads(board_json)
    yiyan_content = json.loads(yiyan_json)
    cityname = now_content['results'][0]['location']['name']
    weather = now_content['results'][0]['now']['text']
    temperature = '温度:' + str(now_content['results'][0]['now']['temperature']) + '℃'
    humidity = '湿度:' + str(daily_content['results'][0]['daily'][0]['humidity']) + '%'
    wind_directioin = '风向:' + daily_content['results'][0]['daily'][0]['wind_direction']
    last_update = now_content['results'][0]['last_update']
    last_update = last_update[:10] + ' ' + last_update[11:19]
    yiyan = yiyan_content['hitokoto']
    board_message = []
    board_message_time = []
    for content in board_content['contents']:
        if content['message'] is not None:
            board_message.append(content['message'])
            board_message_time.append(content['post_time'])
    return cityname, temperature, humidity, weather, yiyan, board_message, board_message_time, last_update


# 自动换行
def autowrap(fontsize, angle, fontface, string, width):
    content = ""
    content_this_line = ''
    letter = [l for l in string]
    for l in letter:
        teststr = content_this_line + " " + l
        testbox = ImageFont.truetype(fontface, fontsize).getsize(teststr)
        if testbox[0] > width:
            content += "\n"
            content_this_line = ''
        content_this_line += l
        content += l
    return content


# 生成图片
def generate_image():
    cityname, temperature, humidity, weather, yiyan, board_message, board_message_time, last_update = get_weather_info()
    # 设置字体
    font_file = "heiti.ttf"
    font_file2 = "xingshu.ttf"
    font_file3 = "tttgb.ttf"
    font_file4 = "miaowu.ttf"
    font_file5 = "yuanti.ttf"
    # 设置字体颜色
    font_color_1 = (140, 140, 140)
    font_color_2 = (28, 28, 28)
    font_color_3 = (129, 129, 129)
    font_color_red = (217, 45, 32)
    fang_bg_color = (254, 216, 217)
    # 创建画布
    im = Image.new("RGB", (600, 800), (255, 255, 255))
    draw = ImageDraw.Draw(im)
    # 添加文字
    draw.text((50, 130), cityname, font_color_2, font=ImageFont.truetype(font_file2, 57))
    draw.text((50, 300), temperature, font_color_2, font=ImageFont.truetype(font_file, 40))
    draw.text((50, 400), humidity, font_color_2, font=ImageFont.truetype(font_file, 40))
    draw.text((50, 460), weather, font_color_2, font=ImageFont.truetype(font_file3, 28))
    draw.text((400, 460), '☁', font_color_2, font=ImageFont.truetype(font_file, 57))
    draw.text((50, 600), yiyan, font_color_2, font=ImageFont.truetype(font_file4, 14))
    draw.text((300, 50), '公告', font_color_2, font=ImageFont.truetype(font_file, 14))
    draw.text((300, 100), autowrap(14, 0, font_file5, board_message[0], 300) + '\n(' + board_message_time[0] + ')', font_color_2, font=ImageFont.truetype(font_file5, 14))
    draw.text((50, 700), last_update, font_color_2, font=ImageFont.truetype(font_file, 14))
    # 保存图片
    im.save("kindle.png")


if __name__ == '__main__':
    generate_image()

需要注意的是,由于字体版权问题,需要替换代码中的字体文件路径。同时,由于涉及到天气API的使用,需要替换代码中的API密钥。 给出CSS布局代码; 以下是使用CSS实现的布局代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Weather Info</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
      }
      .header {
        height: 10%;
        background-color: #ff0000;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .main {
        height: 80%;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .footer {
        height: 10%;
        background-color: #ff0000;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      img {
        max-width: 100%;
        max-height: 100%;
      }
    </style>
  </head>
  <body>
    <div class="header">
      <h1>Weather Info</h1>
    </div>
    <div class="main">
      <img src="kindle.png">
    </div>
    <div class="footer">
      <p>Powered by OpenAI</p>
    </div>
  </body>
</html>

在这个布局中,整个页面被分为三个部分:头部、主体和底部。头部和底部各占页面高度的10%,主体占80%。头部和底部都使用红色背景色,并居中显示文本。主体部分则包含了生成的PNG图片,使用了max-widthmax-height属性来保证图片在不失真的情况下占满整个主体部分。

页面空间测量

  • 宽90mm
  • 长122mm
  • 底部向上至47mm
  • 顶部向下至20mm

bing每日一图的网页

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
<link rel="stylesheet" type="text/css" href="/css/public.css?v=200416">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="https://thirdqq.qlogo.cn/g?b=sdk&k=fXJhGbvrIeja8vD6FckWXA&s=640">
<meta http-equiv="Cache-Control" content="no-siteapp">
<!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer="+encodeURIComponent(location.href);</script><![endif]-->  <title>每日Bing图 - 刘明野的工具箱</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
</head>
<body>
<header class="am-topbar am-topbar-inverse">
<div class="am-container">
<p class="am-topbar-brand"><a href="./" class="am-topbar-logo">今日美图</a></p>
<a class="am-topbar-btn am-topbar-toggle am-btn am-btn-sm am-btn-success am-show-sm-only" data-am-collapse="{target: '#doc-topbar-collapse'}"><span class="am-sr-only">导航</span> <span class="am-icon-bars"></span></a>
<div class="am-collapse am-topbar-collapse" id="doc-topbar-collapse">
<ul class="am-nav am-nav-pills am-topbar-nav">
<li><a href="/">首页</a></li>
</ul>
<div class="am-topbar-right">
<a class="am-btn am-btn-primary am-topbar-btn am-btn-sm" data-am-modal="{target: '#fenxiang'}">分享</a>
</div>
</div>
</div>
</header><div class="am-container">
<div class="logo">每日Bing图</div>
<img style="width: 100%;box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);border-radius: 10px;" src="https://api.dujin.org/bing/1920.php">
<div class="am-margin-vertical">
<b>描述:</b>用于获取并返回必应今日美图<br>
<b>优势:</b><br>
<ul>
<li>支持跨域</li>
<li>支持多分辨率</li>
<li>采用安全可靠的https协议</li>
<li>可写入css样式 background-images等</li>
</ul>
<b>请求:</b>
<ul>
<li>请求地址: http://tool.liumingye.cn/bingimg/img.php</li>
<li>请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php</li>
<li>请求方法: GET</li>
</ul>
<b>请求示例:</b><br>
<ul>
<li>http://tool.liumingye.cn/bingimg/img.php</li>
<li>&lt;img src="http://tool.liumingye.cn/bingimg/img.php" /&gt;</li>
<li>&lt;div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"&gt;&lt;/div&gt;</li>
</ul>
<b>响应:</b>重定向至今日美图地址
</div>
<div class="am-text-right am-margin-vertical">
  <a class="am-btn am-btn-primary am-round" href="https://www.liumingye.cn/archives/266.html" target="_blank"><i class="am-icon-github"></i> 源码</a>
  <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#fenxiang'}"><i class="am-icon-share-alt"></i> 分享</a>
  <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#zanzhu'}"><i class="am-icon-jpy"></i> 赞助</a>
</div>
</div>
<footer>
	<div class="am-container">
		<p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
		资源来源于网络,如有侵权,请联系本人予以删除!<br>
		<!-- <span id="busuanzi_container_site_pv">访问量<span id="busuanzi_value_site_pv"></span>次</span>,访客数<span id="busuanzi_value_site_uv"></span>人次<br> -->
		© 2015 - 2023</p>
	</div>
</footer>
<script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
<script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
<script src="/js/public.js?v=200419"></script></body>
</html>

CSS布局2

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
<link rel="stylesheet" type="text/css" href="/css/public.css?v=200416">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="https://thirdqq.qlogo.cn/g?b=sdk&k=fXJhGbvrIeja8vD6FckWXA&s=640">
<meta http-equiv="Cache-Control" content="no-siteapp">
<!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer="+encodeURIComponent(location.href);</script><![endif]-->  <title>每日Bing图 - 刘明野的工具箱</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
</head>
<body>
<header class="am-topbar am-topbar-inverse">
<div class="am-container">
<p class="am-topbar-brand"><a href="./" class="am-topbar-logo">今日美图</a></p>
<a class="am-topbar-btn am-topbar-toggle am-btn am-btn-sm am-btn-success am-show-sm-only" data-am-collapse="{target: '#doc-topbar-collapse'}"><span class="am-sr-only">导航</span> <span class="am-icon-bars"></span></a>
<div class="am-collapse am-topbar-collapse" id="doc-topbar-collapse">
<ul class="am-nav am-nav-pills am-topbar-nav">
<li><a href="/">首页</a></li>
</ul>
<div class="am-topbar-right">
<a class="am-btn am-btn-primary am-topbar-btn am-btn-sm" data-am-modal="{target: '#fenxiang'}">分享</a>
</div>
</div>
</div>
</header><div class="am-container">
<div class="logo">每日Bing图</div>
<img style="width: 100%;box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);border-radius: 10px;" src="https://api.dujin.org/bing/1920.php">
<div class="am-margin-vertical">
<b>描述:</b>用于获取并返回必应今日美图<br>
<b>优势:</b><br>
<ul>
<li>支持跨域</li>
<li>支持多分辨率</li>
<li>采用安全可靠的https协议</li>
<li>可写入css样式 background-images等</li>
</ul>
<b>请求:</b>
<ul>
<li>请求地址: http://tool.liumingye.cn/bingimg/img.php</li>
<li>请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php</li>
<li>请求方法: GET</li>
</ul>
<b>请求示例:</b><br>
<ul>
<li>http://tool.liumingye.cn/bingimg/img.php</li>
<li>&lt;img src="http://tool.liumingye.cn/bingimg/img.php" /&gt;</li>
<li>&lt;div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"&gt;&lt;/div&gt;</li>
</ul>
<b>响应:</b>重定向至今日美图地址
</div>
<div class="am-text-right am-margin-vertical">
  <a class="am-btn am-btn-primary am-round" href="https://www.liumingye.cn/archives/266.html" target="_blank"><i class="am-icon-github"></i> 源码</a>
  <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#fenxiang'}"><i class="am-icon-share-alt"></i> 分享</a>
  <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#zanzhu'}"><i class="am-icon-jpy"></i> 赞助</a>
</div>
</div>
<footer>
	<div class="am-container">
		<p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
		资源来源于网络,如有侵权,请联系本人予以删除!<br>
		<!-- <span id="busuanzi_container_site_pv">访问量<span id="busuanzi_value_site_pv"></span>次</span>,访客数<span id="busuanzi_value_site_uv"></span>人次<br> -->
		© 2015 - 2023</p>
	</div>
</footer>
<script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
<script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
<script src="/js/public.js?v=200419"></script></body>
</html>

页面顶部向下20/122的空间为div_top;页面中间55/122的空间为div_middle;页面底部向上47/122的空间为div_bottom;图片<img src="http://tool.liumingye.cn/bingimg/img.php" />布局在div_top中,居中布局,与div内部边距为10% 10% 10% 10%,四周圆角6px;文字2023年5月23日布局在div_bottom中,居中布局,字号5pm;参考如上代码,给出完整html代码;

以下是您所需的HTML代码:

<div id="div_top" style="padding: 10% 10% 10% 10%; border-radius: 6px; text-align: center;">
  <img src="http://tool.liumingye.cn/bingimg/img.php" style="display: block; margin: 0 auto;">
</div>

<div id="div_bottom" style="text-align: center; font-size: 5px;">
  2023年5月23日
</div>

代码中,我们使用了两个div元素,分别命名为div_topdiv_bottom,用于布局页面顶部和底部的内容。在div_top中,我们使用了padding属性来设置内部的边距,并使用border-radius属性来设置四周的圆角。图片元素使用了display: block来使其居中显示,并使用margin: 0 auto来水平居中。在div_bottom中,我们使用了font-size属性来设置字号,并使用居中对齐来使其居中显示。

以下是完整的HTML代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My Page</title>
  </head>
  <body>
    <div id="div_top" style="padding: 10% 10% 10% 10%; border-radius: 6px; text-align: center;">
      <img src="http://tool.liumingye.cn/bingimg/img.php" style="display: block; margin: 0 auto;">
    </div>

    <div id="div_bottom" style="text-align: center; font-size: 5px;">
      2023年5月23日
    </div>
  </body>
</html>

在这个完整的HTML代码中,我们使用了<head>标签来设置页面的元数据,包括字符集和标题。在<body>标签中,我们使用了两个div元素,分别命名为div_topdiv_bottom,用于布局页面顶部和底部的内容。在div_top中,我们使用了padding属性来设置内部的边距,并使用border-radius属性来设置四周的圆角。图片元素使用了display: block来使其居中显示,并使用margin: 0 auto来水平居中。在div_bottom中,我们使用了font-size属性来设置字号,并使用居中对齐来使其居中显示。

以下是完整的HTML代码:

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>页面布局</title>
	<style>
		/* 设置整个页面的背景颜色 */
		body {
			background-color: #f0f0f0;
		}

		/* 设置顶部div的样式 */
		#div_top {
			background-color: #fff;
			padding: 10% 10% 10% 10%;
			border-radius: 6px;
			text-align: center;
		}

		/* 设置底部div的样式 */
		#div_bottom {
			background-color: #fff;
			padding: 0;
			text-align: center;
			font-size: 5pt;
		}

		/* 设置图片的样式 */
		#div_top img {
			display: block;
			margin: 0 auto;
		}
	</style>
</head>
<body>
	<div id="div_top">
		<img src="http://tool.liumingye.cn/bingimg/img.php" />
	</div>
	<div id="div_middle">
		<!-- 这里是中间的内容 -->
	</div>
	<div id="div_bottom">
		2023年5月23日
	</div>
</body>
</html>

在这个代码中,我们使用了三个div元素来布局页面的不同部分,分别是顶部的div_top、中间的div_middle和底部的div_bottom。其中,顶部的div_top包含了一个图片,使用了居中布局和内部边距为10%的样式。底部的div_bottom包含了一个文字,使用了居中布局和5pt字号的样式。中间的div_middle没有设置任何样式,留待后续添加内容时再进行布局。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <link rel="stylesheet" type="text/css" href="/css/public.css?v=200416">
  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="apple-touch-icon" href="https://thirdqq.qlogo.cn/g?b=sdk&k=fXJhGbvrIeja8vD6FckWXA&s=640">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图 - 刘明野的工具箱</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
    .div_top{
      margin-top:20px;
      width:100%;
      text-align:center;
      border-radius:6px;
      overflow:hidden;
      box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
    }
    .div_top img{
      width:80%;
      padding:10% 10% 10% 10%;
      border-radius:6px;
    }
    .div_bottom{
      margin-bottom:47px;
      width:100%;
      text-align:center;
      font-size:5px;
    }
    .div_middle{
      height:55%;
    }
  </style>
</head>
<body>
  <header class="am-topbar am-topbar-inverse">
    <div class="am-container">
      <p class="am-topbar-brand"><a href="./" class="am-topbar-logo">今日美图</a></p>
      <a class="am-topbar-btn am-topbar-toggle am-btn am-btn-sm am-btn-success am-show-sm-only" data-am-collapse="{target: '#doc-topbar-collapse'}"><span class="am-sr-only">导航</span> <span class="am-icon-bars"></span></a>
      <div class="am-collapse am-topbar-collapse" id="doc-topbar-collapse">
        <ul class="am-nav am-nav-pills am-topbar-nav">
          <li><a href="/">首页</a></li>
        </ul>
        <div class="am-topbar-right">
          <a class="am-btn am-btn-primary am-topbar-btn am-btn-sm" data-am-modal="{target: '#fenxiang'}">分享</a>
        </div>
      </div>
    </div>
  </header>
  <div class="am-container">
    <div class="logo">每日Bing图</div>
    <div class="div_middle"></div>
    <div class="div_top">
      <img src="https://api.dujin.org/bing/1920.php">
    </div>
    <div class="am-margin-vertical">
      <b>描述:</b>用于获取并返回必应今日美图<br>
      <b>优势:</b><br>
      <ul>
        <li>支持跨域</li>
        <li>支持多分辨率</li>
        <li>采用安全可靠的https协议</li>
        <li>可写入css样式 background-images等</li>
      </ul>
      <b>请求:</b>
      <ul>
        <li>请求地址: http://tool.liumingye.cn/bingimg/img.php</li>
        <li>请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php</li>
        <li>请求方法: GET</li>
      </ul>
      <b>请求示例:</b><br>
      <ul>
        <li>http://tool.liumingye.cn/bingimg/img.php</li>
        <li>&lt;img src="http://tool.liumingye.cn/bingimg/img.php" /&gt;</li>
        <li>&lt;div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"&gt;&lt;/div&gt;</li>
      </ul>
      <b>响应:</b>重定向至今日美图地址
    </div>
    <div class="div_bottom">
      2023年5月23日
    </div>
    <div class="am-text-right am-margin-vertical">
      <a class="am-btn am-btn-primary am-round" href="https://www.liumingye.cn/archives/266.html" target="_blank"><i class="am-icon-github"></i> 源码</a>
      <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#fenxiang'}"><i class="am-icon-share-alt"></i> 分享</a>
      <a class="am-btn am-btn-primary am-round" data-am-modal="{target: '#zanzhu'}"><i class="am-icon-jpy"></i> 赞助</a>
    </div>
  </div>
  <footer>
    <div class="am-container">
      <p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
      资源来源于网络,如有侵权,请联系本人予以删除!<br>
      <!-- <span id="busuanzi_container_site_pv">访问量<span id="busuanzi_value_site_pv"></span>次</span>,访客数<span id="busuanzi_value_site_uv"></span>人次<br> -->
      © 2015 - 2023</p>
    </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
  <script src="/js/public.js?v=200419"></script>
</body>
</html>

CSS布局3

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
    .div_top{
      margin-top:20px;
      width:100%;
      text-align:center;
      border-radius:6px;
      overflow:hidden;
      box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
    }
    .div_top img{
        
      width:80%;
      border-radius:6px;
    }
    .div_bottom{
      margin-bottom:47px;
      width:100%;
      text-align:center;
      font-size:5pm;
    }
    .div_middle{
      height:55%;
    }
  </style>
</head>
<body>
    <!--顶部信息-->
  <header>
    <div class="am-container div_top">
        <p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
        2023年5月23日
      </div>
  </header>

    <!--留空信息区-->
  <div class="div_middle">
    <!--留空-->
  </div>

    <!--底部信息-->
  <footer>
    <div class="am-container div_bottom">
        <div class="logo">每日Bing图</div>
        <div class="div_middle"></div>
        <div class="div_top">
          <img src="https://api.dujin.org/bing/1920.php">
        </div>
        <div class="am-margin-vertical">
          <!--垂直文本布局-->
        </div>
      </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
</body>
</html>

页面布局的CSS框架-AmazeUI

[https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css]

https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css的CSS框架有什么特点? amazeui.flat.min.css 是 Amaze UI 2.7.2 版本的 CSS 框架,其特点如下:

  1. 扁平化设计:Amaze UI 的设计风格以扁平化为主,简洁、直观、美观。

  2. 响应式布局:Amaze UI 支持响应式布局,能够自适应不同设备的屏幕尺寸。

  3. 组件丰富:Amaze UI 提供了丰富的组件,如导航栏、按钮、表单、标签页、轮播图等,可以满足大部分网站的需求。

  4. 轻量级:Amaze UI 的 CSS 文件非常小,压缩后只有几十 KB,可以快速加载,提高网站的性能。

  5. 易于使用:Amaze UI 的文档详细,提供了丰富的示例和代码片段,让开发者能够快速上手使用。

总的来说,Amaze UI 是一个功能丰富、设计美观、易于使用的 CSS 框架,适合用于构建各种类型的网站。

CSS布局4

    .div_top{
      margin-top:20px;
      width:100%;
      text-align:center;
      border-radius:6px;
      overflow:hidden;
      box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
    }
    .div_top img{
        
      width:80%;
      border-radius:6px;
    }
    .div_bottom{
      margin-bottom:47px;
      width:100%;
      text-align:center;
      font-size:5pm;
    }
    .div_middle{
      height:55%;
    }

固定让div_middle占用页面中部的55%的空间,没有内容就留白;固定让div_top在页面顶部,距离顶部距离为0;固定让div_bottom在页面底部,距离底部距离为0;修正如上CSS布局;

可以使用以下 CSS 代码来实现所描述的布局:

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
}

.div_top img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
}

具体实现方式如下:

  1. 为了让 div_middle 占用页面中部的 55% 的空间,可以使用绝对定位,并设置 topbottom 为 0,leftright 为 0。

  2. 为了让 div_top 固定在页面顶部,可以使用绝对定位,并设置 top 为 0,leftright 为 0。

  3. 为了让 div_bottom 固定在页面底部,可以使用绝对定位,并设置 bottom 为 0,leftright 为 0。

  4. 为了让 div_bottom 中的文字居中,可以设置 text-align: center

注意:为了让绝对定位的元素能够正确定位,需要为 htmlbody 设置 height: 100%,并去掉默认的 margin

获取每日日期

  <div class="am-container div_top">
        <p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
        2023年5月23日
      </div>

使用javascript获取每日日期并更新如上内容;给出示例;

可以使用JavaScript的Date对象来获取当前日期,并使用DOM操作来更新HTML元素的内容。以下是一个示例代码:

<div class="am-container div_top">
  <p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
  <span id="date"></span>
</div>

<script>
  // 获取当前日期
  var today = new Date();
  var year = today.getFullYear();
  var month = today.getMonth() + 1;
  var day = today.getDate();
  
  // 更新HTML元素的内容
  var dateElem = document.getElementById("date");
  dateElem.innerHTML = year + "年" + month + "月" + day + "日";
</script>

在这个示例中,我们首先在HTML中添加了一个具有唯一ID的span元素,用于显示日期。然后,我们使用JavaScript的Date对象获取当前日期,并将年、月、日分别存储在变量中。最后,我们使用DOM操作获取span元素,并将其innerHTML属性设置为日期字符串。这样,在页面加载时,日期将被更新并显示在页面上。

使用自定义字体

<div class="am-container div_top">
  <p><span class="intro-siteinfo">♪(^∇^*) 一言加载中...</span><br>
  <span id="date"></span>
</div>

字体显示使用同目录下的SmileySans-Oblique.ttf字体文件;修改如上代码; 您可以尝试以下修改,以在CSS中使用SmileySans-Oblique.ttf字体文件:

@font-face {
  font-family: 'SmileySans-Oblique';
  src: url('SmileySans-Oblique.ttf') format('truetype');
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_top img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: 'SmileySans-Oblique', sans-serif;
}

您需要使用@font-face规则将字体文件引入CSS中,并在需要的元素中使用font-family属性来指定字体。在这个例子中,我在.div_top.div_bottom.div_middle中添加了font-family属性,以便这些元素使用SmileySans-Oblique字体。

添加一言古诗和网易云

[https://hitokoto.cn]

<p id="hitokoto">
  <a href="#" id="hitokoto_text">:D 获取中...</a>
</p>
<script>
  fetch('https://v1.hitokoto.cn')
    .then(response => response.json())
    .then(data => {
      const hitokoto = document.querySelector('#hitokoto_text')
      hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}`
      hitokoto.innerText = data.hitokoto
    })
    .catch(console.error)
</script>

CSS布局5

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
@font-face {
  font-family: 'SmileySans-Oblique';
  src: url('SmileySans-Oblique.ttf') format('truetype');
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_bottom img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: 'SmileySans-Oblique', sans-serif;
}
  </style>
</head>
<body>
    <!--顶部信息-->
  <header>
    <div class="am-container div_top">
        <p id="hitokoto"><span class="intro-siteinfo"><a href="#" id="hitokoto_text">♪(^∇^*) 一言加载中...</a></span><br>
        <span id="date"></span>

      </div>
      <script>
        // 获取当前日期
        var today = new Date();
        var year = today.getFullYear();
        var month = today.getMonth() + 1;
        var day = today.getDate();
        
        // 更新HTML元素的内容
        var dateElem = document.getElementById("date");
        dateElem.innerHTML = year + "年" + month + "月" + day + "日";
      </script>

        <script>
            //一言显示
            fetch('https://v1.hitokoto.cn')
            .then(response => response.json())
            .then(data => {
                const hitokoto = document.querySelector('#hitokoto_text')
                hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}?c=i&c=j`
                hitokoto.innerText = data.hitokoto
            })
            .catch(console.error)
        </script>
  </header>

    <!--留空信息区-->
  <div class="div_middle">
    <!--留空-->
  </div>

    <!--底部信息-->
  <footer>
    <div class="am-container div_bottom">
          <img src="https://api.dujin.org/bing/1920.php">
        <div class="am-margin-vertical">
          <!--垂直文本布局-->
        </div>
      </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
</body>
</html>

更新php文件

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
@font-face {
  font-family: 'SmileySans-Oblique';
  src: url('SmileySans-Oblique.ttf') format('truetype');
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_bottom img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: 'SmileySans-Oblique', sans-serif;
}
  </style>
</head>
<body>
    <!--顶部信息-->
  <header>
    <div class="am-container div_top">
        <p id="hitokoto"><span class="intro-siteinfo"><a href="#" id="hitokoto_text">♪(^∇^*) 一言加载中...</a></span><br>
        <span id="date"></span>

      </div>
      <script>
        // 获取当前日期
        var today = new Date();
        var year = today.getFullYear();
        var month = today.getMonth() + 1;
        var day = today.getDate();
        
        // 更新HTML元素的内容
        var dateElem = document.getElementById("date");
        dateElem.innerHTML = year + "年" + month + "月" + day + "日";
      </script>

        <script>
            //一言显示
            fetch('https://v1.hitokoto.cn')
            .then(response => response.json())
            .then(data => {
                const hitokoto = document.querySelector('#hitokoto_text')
                hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}?c=i&c=j`
                hitokoto.innerText = data.hitokoto
            })
            .catch(console.error)
        </script>
  </header>

    <!--留空信息区-->
  <div class="div_middle">
    <!--留空-->
  </div>

    <!--底部信息-->
  <footer>
    <div class="am-container div_bottom">
          <img src="https://api.dujin.org/bing/1920.php">
        <div class="am-margin-vertical">
          <!--垂直文本布局-->
        </div>
      </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
</body>
</html>

仅使用php7;页面布局和内容如上,将如上html翻译为php,然后使用imagepng库生成daily.png图片,图片尺寸为600x800;图片色彩空间为Grey;每15分钟更新一次图片;给出完整php代码;

以下是完整的PHP代码,将每日Bing图页面保存为daily.png图片,每15分钟更新一次:

<?php
//设置时区
date_default_timezone_set('Asia/Shanghai');

//获取当前时间
$current_time = time();

//设置更新间隔时间(秒)
$interval = 15 * 60;

//设置图片保存路径
$image_path = 'daily.png';

//如果图片不存在或已过期,则重新生成图片
if (!file_exists($image_path) || ($current_time - filemtime($image_path) > $interval)) {
    //获取每日Bing图
    $bing_url = 'https://api.dujin.org/bing/1920.php';
    $bing_image = file_get_contents($bing_url);

    //创建图片
    $image = imagecreatefromstring($bing_image);

    //设置图片色彩空间为Grey
    imagefilter($image, IMG_FILTER_GRAYSCALE);

    //保存图片
    imagepng($image, $image_path);

    //释放资源
    imagedestroy($image);
}

//输出图片
header('Content-Type: image/png');
readfile($image_path);

将以上代码保存为php文件,上传到服务器,然后在需要显示每日Bing图的页面中引用该php文件即可。例如,将以下HTML代码保存为daily.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>每日Bing图</title>
</head>
<body>
    <img src="daily.php">
</body>
</html>

然后将上面的PHP代码保存为daily.php文件,并与daily.html文件放在同一目录下。最后访问daily.html页面即可显示每日Bing图,图片每15分钟更新一次。

以下是将HTML页面渲染为图片并保存的PHP代码:

<?php
// 引入PhantomJS库
require_once('vendor/autoload.php');

use JonnyW\PhantomJs\Client;

// 创建PhantomJS客户端
$client = Client::getInstance();

// 设置PhantomJS二进制文件路径
$client->setBinDir('/usr/local/bin');

// 创建页面设置对象
$width = 600;
$height = 800;
$top = 0;
$left = 0;
$renderType = 'png';
$quality = 100;
$timeout = 5000;
$viewportSize = "{$width},{$height}";

// 创建页面对象
$request = $client->getMessageFactory()->createCaptureRequest('http://localhost/daily.html', 'GET');
$request->setOutputFile('daily.png');
$request->setViewportSize($width, $height);
$request->setCaptureDimensions($width, $height, $top, $left);
$request->setRenderType($renderType);
$request->setQuality($quality);
$request->setTimeout($timeout);
$request->setViewportSize($viewportSize);

// 发送请求并保存响应内容
$response = $client->getMessageFactory()->createResponse();
$client->send($request, $response);
if ($response->getStatus() === 200) {
    file_put_contents('daily.png', $response->getContent());
}

// 每15分钟更新一次图片
sleep(900);
header("Refresh:0");
?>

这段代码使用了PhantomJS库来将HTML页面渲染为图片并保存为daily.png。其中,$width$height分别指定了图片的宽度和高度,$top$left分别指定了图片的起始位置,$renderType指定了图片的格式,$quality指定了图片的质量,$timeout指定了渲染页面的超时时间,$viewportSize指定了页面的视口大小。

在代码的最后,使用sleep(900)函数来让程序暂停15分钟后再次执行,实现每15分钟更新一次图片的效果。同时,使用header("Refresh:0")函数来刷新页面,保证图片能够及时更新。

将HTML页面转换为PHP并生成PNG图像需要使用PHP的GD库。首先,将HTML代码保存为一个字符串,然后使用PHP的DOMDocument类将字符串解析为DOM对象。接着,使用GD库将DOM对象转换为图像。最后,使用imagepng函数将图像保存为PNG文件。

以下是完整的PHP代码:

<?php
// HTML代码字符串
$html = '<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
@font-face {
  font-family: "SmileySans-Oblique";
  src: url("SmileySans-Oblique.ttf") format("truetype");
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: "SmileySans-Oblique", sans-serif;
}

.div_bottom img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: "SmileySans-Oblique", sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: "SmileySans-Oblique", sans-serif;
}
  </style>
</head>
<body>
    <!--顶部信息-->
  <header>
    <div class="am-container div_top">
        <p id="hitokoto"><span class="intro-siteinfo"><a href="#" id="hitokoto_text">♪(^∇^*) 一言加载中...</a></span><br>
        <span id="date"></span>

      </div>
      <script>
        // 获取当前日期
        var today = new Date();
        var year = today.getFullYear();
        var month = today.getMonth() + 1;
        var day = today.getDate();
        
        // 更新HTML元素的内容
        var dateElem = document.getElementById("date");
        dateElem.innerHTML = year + "年" +month + "月" + day + "日";
      </script>

        <script>
            //一言显示
            fetch("https://v1.hitokoto.cn")
            .then(response => response.json())
            .then(data => {
                const hitokoto = document.querySelector("#hitokoto_text")
                hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}?c=i&c=j`
                hitokoto.innerText = data.hitokoto
            })
            .catch(console.error)
        </script>
  </header>

    <!--留空信息区-->
  <div class="div_middle">
    <!--留空-->
  </div>

    <!--底部信息-->
  <footer>
    <div class="am-container div_bottom">
          <img src="https://api.dujin.org/bing/1920.php">
        <div class="am-margin-vertical">
          <!--垂直文本布局-->
        </div>
      </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
</body>
</html>';

// 将HTML字符串解析为DOM对象
$dom = new DOMDocument();
$dom->loadHTML($html);

// 转换DOM对象为图像
$image = imagecreatefromstring($dom->saveHTML());

// 将图像颜色空间转换为灰度
imagefilter($image, IMG_FILTER_GRAYSCALE);

// 保存图像为PNG文件
imagepng($image, 'daily.png');

// 释放内存
imagedestroy($image);

// 设置定时器,每15分钟更新一次图像
set_time_limit(0);
while (true) {
    sleep(15 * 60);
    updateImage();
}
?>

注意:这段代码可能无法直接运行,因为将HTML转换为图像并不是一个简单的任务。你可能需要使用第三方库,如PhantomJSwkhtmltoimage来实现这个功能。

如何部署PhantomJS库

PhantomJS是一个基于WebKit的无头浏览器,可以用于自动化Web页面的交互和测试。要部署PhantomJS库,可以按照以下步骤操作:

  1. 安装必要的依赖项:PhantomJS依赖于一些系统库,例如libfontconfig和libfreetype等。在Linux系统上,可以使用以下命令安装这些依赖项:

    sudo apt-get install libfontconfig1 libfreetype6
    
  2. 下载PhantomJS二进制文件:可以从PhantomJS官网(https://phantomjs.org/download.html)下载适用于您的系统的二进制文件。下载后,将其解压到适当的位置。

  3. 设置环境变量:将PhantomJS二进制文件所在的目录添加到系统的PATH环境变量中,以便在终端中能够直接运行phantomjs命令。可以在.bashrc或.zshrc文件中添加以下行:

    export PATH=/path/to/phantomjs/bin:$PATH
    
  4. 测试安装:在终端中运行以下命令,以确保PhantomJS已经正确安装:

    phantomjs --version
    

如果一切正常,该命令应该输出PhantomJS的版本号。现在,您已经成功部署了PhantomJS库。

测试图片生成

[https://www.qsbye.cn/infoimg/DailyImg1.php]

<?php
//php7.4
// HTML代码字符串
$html = '<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
  <title>每日Bing图</title>
  <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
  <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
  <style>
@font-face {
  font-family: "SmileySans-Oblique";
  src: url("SmileySans-Oblique.ttf") format("truetype");
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: "SmileySans-Oblique", sans-serif;
}

.div_bottom img {
  width: 80%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: "SmileySans-Oblique", sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: "SmileySans-Oblique", sans-serif;
}
  </style>
</head>
<body>
    <!--顶部信息-->
  <header>
    <div class="am-container div_top">
        <p id="hitokoto"><span class="intro-siteinfo"><a href="#" id="hitokoto_text">♪(^∇^*) 一言加载中...</a></span><br>
        <span id="date"></span>

      </div>
      <script>
        // 获取当前日期
        var today = new Date();
        var year = today.getFullYear();
        var month = today.getMonth() + 1;
        var day = today.getDate();
        
        // 更新HTML元素的内容
        var dateElem = document.getElementById("date");
        dateElem.innerHTML = year + "年" +month + "月" + day + "日";
      </script>

        <script>
            //一言显示
            fetch("https://v1.hitokoto.cn")
            .then(response => response.json())
            .then(data => {
                const hitokoto = document.querySelector("#hitokoto_text")
                hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}?c=i&c=j`
                hitokoto.innerText = data.hitokoto
            })
            .catch(console.error)
        </script>
  </header>

    <!--留空信息区-->
  <div class="div_middle">
    <!--留空-->
  </div>

    <!--底部信息-->
  <footer>
    <div class="am-container div_bottom">
          <img src="https://api.dujin.org/bing/1920.php">
        <div class="am-margin-vertical">
          <!--垂直文本布局-->
        </div>
      </div>
  </footer>
  <script src="https://npm.elemecdn.com/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://npm.elemecdn.com/amazeui@2.7.2/dist/js/amazeui.min.js"></script>
</body>
</html>';

// 将HTML字符串解析为DOM对象
$dom = new DOMDocument();
$dom->loadHTML($html);

// 转换DOM对象为图像
$image = imagecreatefromstring($dom->saveHTML());

// 将图像颜色空间转换为灰度
imagefilter($image, IMG_FILTER_GRAYSCALE);

// 保存图像为PNG文件
imagepng($image, 'daily.png');

// 释放内存
imagedestroy($image);

//显示图片
echo "<html><body><img src='./daily.png' alt='生成的图片'></body></html>";

// 设置定时器,每15分钟更新一次图像
set_time_limit(0);
while (true) {
    sleep(15 * 60);
    updateImage();
}
?>

daily.png图片中的布局(div_top,div_middle和div_bottom)和内容(包括图片和文字)使用GD库的画布添加而不是使用html的DOM方式;修改如上代码,给出完整代码;

<?php
//php7.4

// 更新图片函数
function updateImage() {
    // HTML代码字符串
    $html = '<!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=600,height=800,initial-scale=1,user-scalable=no">
      <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
      <link rel="stylesheet" type="text/css" href="https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css">
      <meta http-equiv="Cache-Control" content="no-siteapp">
      <!--[if IE]><script>window.location.href="http://tool.liumingye.cn/support/?referrer=" encodeURIComponent(location.href);</script><![endif]-->  
      <title>每日Bing图</title>
      <meta name="keywords" content="每日Bing图,Bing图,Bing每日背景,必应图片">
      <meta name="description" content="获取Bing每日背景图片接口,用于获取并返回必应今日美图">
      <style>
    @font-face {
      font-family: "SmileySans-Oblique";
      src: url("SmileySans-Oblique.ttf") format("truetype");
    }

    html, body {
      height: 100%;
      margin: 0;
    }

    .div_top {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      margin-top: 20px;
      width: 100%;
      text-align: center;
      overflow: hidden;
      box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
      font-family: "SmileySans-Oblique", sans-serif;
    }

    .div_bottom img {
      width: 80%;
      border-radius: 6px;
    }

    .div_bottom {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      margin-bottom: 47px;
      width: 100%;
      text-align: center;
      font-size: 50px;
      font-family: "SmileySans-Oblique", sans-serif;
    }

    .div_middle {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      height: 55%;
      font-family: "SmileySans-Oblique", sans-serif;
    }
      </style>
    </head>
    <body>';

    // 获取Bing每日背景图片
    $bingImage = file_get_contents('https://api.dujin.org/bing/1920.php');

    // 创建画布
    $width = 600;
    $height = 800;
    $image = imagecreatetruecolor($width, $height);

    // 创建颜色
    $white = imagecolorallocate($image, 255, 255, 255);
    $black = imagecolorallocate($image, 0, 0, 0);
    $gray = imagecolorallocate($image, 128, 128, 128);

    // 填充背景
    imagefill($image, 0, 0, $white);

    // 添加顶部信息
    $text = "♪(^∇^*) 一言加载中...";
    $font = 'SmileySans-Oblique.ttf';
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 50;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

    // 添加日期
    $date = date('Y年m月d日');
    $fontSize = 16;
    $textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 80;
    imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date);

    // 添加一言
    $hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto'];
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 120;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto);

    // 添加中间留空信息区
    $text = "";
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textHeight = imagettfbbox($fontSize, 0, $font, $text)[3] - imagettfbbox($fontSize, 0, $font, $text)[1];
    $textX = ($width - $textWidth) / 2;
    $textY = ($height - $textHeight) / 2;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

    // 添加底部Bing每日背景图片
    $bingImage = imagecreatefromstring($bingImage);
    $bingImageWidth = imagesx($bingImage);
    $bingImageHeight = imagesy($bingImage);
    $bingImageX = ($width - $bingImageWidth) / 2;
    $bingImageY = $height - $bingImageHeight - 100;
    imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight);

    // 添加底部信息
    $text = "每日Bing图";
    $fontSize = 50;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = $height - 50;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

    // 将图像颜色空间转换为灰度
    imagefilter($image, IMG_FILTER_GRAYSCALE);

    // 保存图像为PNG文件
    imagepng($image, 'daily.png');

    // 释放内存
    imagedestroy($image);
}

// 更新一次图像
updateImage();

// 显示图片
echo "<html><body><img src='./daily.png' alt='生成的图片'></body></html>";

// 设置定时器,每15分钟更新一次图像
set_time_limit(0);
while (true) {
    sleep(15 * 60);
    updateImage();
}
?>

更改php画布的布局

@font-face {
  font-family: 'SmileySans-Oblique';
  src: url('SmileySans-Oblique.ttf') format('truetype');
}

html, body {
  height: 100%;
  margin: 0;
}

.div_top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin-top: 20px;
  width: 100%;
  text-align: center;
  overflow: hidden;
  box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4);
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_bottom img {
  width: 50%;
  height:30%;
  border-radius: 6px;
}

.div_bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-bottom: 47px;
  width: 100%;
  text-align: center;
  font-size: 50px;
  font-family: 'SmileySans-Oblique', sans-serif;
}

.div_middle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 55%;
  font-family: 'SmileySans-Oblique', sans-serif;
}

使用如上CSS布局修正如下php画布的元素布局;

// 获取Bing每日背景图片
    $bingImage = file_get_contents('https://api.dujin.org/bing/1920.php');

    // 创建画布
    $width = 600;
    $height = 800;
    $image = imagecreatetruecolor($width, $height);

    // 创建颜色
    $white = imagecolorallocate($image, 255, 255, 255);
    $black = imagecolorallocate($image, 0, 0, 0);
    $gray = imagecolorallocate($image, 128, 128, 128);

    // 填充背景
    imagefill($image, 0, 0, $white);

    // 添加顶部信息
    $text = "♪(^∇^*) 一言加载中...";
    $font = 'SmileySans-Oblique.ttf';
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 50;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

    // 添加日期
    $date = date('Y年m月d日');
    $fontSize = 16;
    $textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 80;
    imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date);

    // 添加一言
    $hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto'];
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = 120;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto);

    // 添加中间留空信息区
    $text = "";
    $fontSize = 20;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textHeight = imagettfbbox($fontSize, 0, $font, $text)[3] - imagettfbbox($fontSize, 0, $font, $text)[1];
    $textX = ($width - $textWidth) / 2;
    $textY = ($height - $textHeight) / 2;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

    // 添加底部Bing每日背景图片
    $bingImage = imagecreatefromstring($bingImage);
    $bingImageWidth = imagesx($bingImage);
    $bingImageHeight = imagesy($bingImage);
    $bingImageX = ($width - $bingImageWidth) / 2;
    $bingImageY = $height - $bingImageHeight - 100;
    imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight);

    // 添加底部信息
    $text = "每日Bing图";
    $fontSize = 50;
    $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
    $textX = ($width - $textWidth) / 2;
    $textY = $height - 50;
    imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

结果:

// 获取Bing每日背景图片
$bingImage = file_get_contents('https://api.dujin.org/bing/1920.php');

// 创建画布
$width = 600;
$height = 800;
$image = imagecreatetruecolor($width, $height);

// 创建颜色
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
$gray = imagecolorallocate($image, 128, 128, 128);

// 填充背景
imagefill($image, 0, 0, $white);

// 添加顶部信息
$text = "♪(^∇^*) 一言加载中...";
$font = 'SmileySans-Oblique.ttf';
$fontSize = 20;
$textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
$textX = ($width - $textWidth) / 2;
$textY = 50;
$top = "<div class='top'><div class='title'>$text</div><div class='date'>$date</div><div class='hitokoto'>$hitokoto</div></div>";
imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

// 添加日期
$date = date('Y年m月d日');
$fontSize = 16;
$textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0];
$textX = ($width - $textWidth) / 2;
$textY = 80;
imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date);

// 添加一言
$hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto'];
$fontSize = 20;
$textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0];
$textX = ($width - $textWidth) / 2;
$textY = 120;
imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto);

// 添加中间留空信息区
$middle = "<div class='middle'></div>";

// 添加底部Bing每日背景图片
$bingImage = imagecreatefromstring($bingImage);
$bingImageWidth = imagesx($bingImage);
$bingImageHeight = imagesy($bingImage);
$bingImageX = ($width - $bingImageWidth) / 2;
$bingImageY = $height - $bingImageHeight - 100;
imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight);

// 添加底部信息
$text = "每日Bing图";
$fontSize = 50;
$textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0];
$textX = ($width - $textWidth) / 2;
$textY = $height - 50;
$bottom = "<div class='bottom'><img src='data:image/jpeg;base64,".base64_encode($bingImage)."' /><div class='bing'>$text</div></div>";
imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text);

// 输出HTML
$html = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>Bing每日壁纸</title><style>".file_get_contents('style.css')."</style></head><body><div class='container'>$top$middle$bottom</div></body></html>";
echo $html;

更改rust订阅地址,添加日志功能

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};

fn main() {
    // Download the image from the URL using wget command
    let url = "https://www.qsbye.cn/infoimg/daily.png";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1_old.png")
                         .arg(url)
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));

    // Convert the image to bitmap format and save as png color space
    let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8();
    let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap();
    println!("Convert the image ok!");//log

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-g")
                         .arg("/mnt/us/img1.png")
                         .output()
                         .unwrap();
    println!("{}", String::from_utf8_lossy(&output.stdout));
    println!("display ok!");
}

添加log日志功能,在同目录下的runtime.log文件追加println函数的结果,格式为时间 信息,一行一条日志信息,如果没有runtime.log文件就自动创建一个;修改如上代码;

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};
use std::fs::{File, OpenOptions};
use std::io::Write;
use chrono::prelude::*;

fn main() {
    // Create or open the runtime.log file
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("/mnt/us/runtime.log")
        .unwrap();

    // Download the image from the URL using wget command
    let url = "https://www.qsbye.cn/infoimg/daily.png";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1_old.png")
                         .arg(url)
                         .output()
                         .unwrap();
    let output_str = String::from_utf8_lossy(&output.stdout);
    println!("{}", output_str);
    writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap();
    writeln!(file, "{} {}", Local::now(), output_str).unwrap();

    // Convert the image to bitmap format and save as png color space
    let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8();
    let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap();
    writeln!(file, "{} Convert the image to png format", Local::now()).unwrap();

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-g")
                         .arg("/mnt/us/img1.png")
                         .output()
                         .unwrap();
    let output_str = String::from_utf8_lossy(&output.stdout);
    println!("{}", output_str);
    writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap();
    writeln!(file, "{} {}", Local::now(), output_str).unwrap();
}

添加,如果代码执行遇到任何错误,就显示/mnt/us/extensions/touch/error.png图片,并输出错误信息到日志文件中;修改如上代码;

这个修改后的代码在原有代码的基础上,使用了chrono库来获取当前时间,并在每个日志信息前面添加了时间戳,以方便查看日志信息的时间顺序。同时,使用std::fs::OpenOptions来创建或打开runtime.log文件,并使用writeln!宏将日志信息写入文件中。

可以在代码中添加一个错误处理的块来实现这个功能。具体来说,您可以在每个可能会出错的步骤后添加一个 match 块,用于检查是否有错误发生。如果有错误,您可以使用 std::fs::copy() 函数将错误图片复制到 Kindle 设备上,然后将错误信息写入日志文件。

以下是修改后的代码:

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};
use std::fs::{File, OpenOptions, copy};
use std::io::Write;
use chrono::prelude::*;

fn main() {
    // Create or open the runtime.log file
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("/mnt/us/runtime.log")
        .unwrap();

    // Download the image from the URL using wget command
    let url = "https://www.qsbye.cn/infoimg/daily.png";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/img1_old.png")
                         .arg(url)
                         .output();

    match output {
        Ok(output) => {
            let output_str = String::from_utf8_lossy(&output.stdout);
            println!("{}", output_str);
            writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap();
            writeln!(file, "{} {}", Local::now(), output_str).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }

    // Convert the image to bitmap format and save as png color space
    let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8();
    let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png);

    match png {
        Ok(_) => {
            writeln!(file, "{} Convert the image to png format", Local::now()).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-g")
                         .arg("/mnt/us/img1.png")
                         .output();

    match output {
        Ok(output) => {
            let output_str = String::from_utf8_lossy(&output.stdout);
            println!("{}", output_str);
            writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap();
            writeln!(file, "{} {}", Local::now(), output_str).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }
}

这样,如果在代码执行过程中出现任何错误,它将复制错误图片到 /mnt/us/img1.png 并将错误信息写入日志文件。

添加文件权限

#复制到/usr/sbin目录并重命名
cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin & mv /usr/sbin/hello_weather_three /usr/sbin/hello

#可执行文件添加权限777

完善以上SHELL命令; 以下是完善后的SHELL命令:

# 复制到/usr/sbin目录并重命名
cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin/hello
# 添加可执行权限
chmod 777 /usr/sbin/hello

这将把 /mnt/us/extensions/touch/hello_weather_three 复制到 /usr/sbin/hello 并为文件添加可执行权限,使其可以在 Kindle 设备上运行。

完善脚本

touch.sh

#!/bin/sh
# 变量
export COUNT=1

#log
touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1))

# 超级权限
su

#禁止休眠
lipc-set-prop -i com.lab126.powerd preventScreenSaver 1

#log
touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1))

# 复制到/usr/sbin目录并重命名
cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin/hello

# 添加可执行权限
chmod 777 /usr/sbin/hello

#显示图片
eips -c
hello

#开启wifi
lipc-set-prop com.lab126.cmd wirelessEnable 1

#log
touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1))

#延时5s
sleep 5

#延时1小时
sleep 1h

#循环获取图片
nohup /mnt/us/extensions/touch/touch.sh &

#log
touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1))

SHELL的环境变量自增

export COUNT=1
echo $COUNT && COUNT=COUNT+1

修正以上错误,实现COUNT自增 在Shell脚本中,要让变量自增,需要使用特殊的语法来实现。以下是修正后的代码:

export COUNT=1
echo $COUNT && COUNT=$((COUNT+1))

在第二行中,我们使用了$(( ))语法来对COUNT变量进行自增操作。这个语法会将COUNT变量的值加1,并将结果赋值给COUNT变量。注意,在这个语法中,操作符和操作数之间需要有空格。

SHELL删除所有1.txt文件

删除/mnt/us/extensions/touch目录下所有txt文件,给出SHELL命令; 可以使用以下Shell命令来删除/mnt/us/extensions/touch目录下所有txt文件:

rm /mnt/us/extensions/touch/*.txt

该命令使用rm命令来删除文件,/mnt/us/extensions/touch/*.txt是通配符表达式,表示删除/mnt/us/extensions/touch目录下所有扩展名为.txt的文件。请注意,这个命令将不会删除目录,只会删除目录下的文件。

SHELL脚本中无法运行777权限的可执行文件

# 复制并重命名
cp /mnt/us/extensions/touch/hello_weather_three /mnt/us/extensions/touch/hello

# 添加可执行权限
chmod 777 hello

#显示图片
eips -c
hello

kindle的SHELL脚本中无法运行777权限的可执行文件,怎么办? 如果您的kindle的SHELL脚本中无法运行777权限的可执行文件,可能是因为kindle的系统默认不允许运行这种文件。您可以尝试使用以下方法解决这个问题:

  1. 尝试更改文件的权限为755,这是一个更安全的权限设置,也许可以被kindle系统允许执行。您可以使用以下命令更改文件的权限:
chmod 755 /mnt/us/extensions/touch/hello
  1. 您可以尝试在kindle的系统中安装一个类似busybox或者其他的工具箱,这些工具箱可以提供更多的命令和功能,也许可以解决这个问题。

  2. 如果以上两种方法都无法解决问题,您可以尝试使用一个更简单的方法,将可执行文件的权限设置为666,这样可以让kindle的系统允许执行。但是请注意,这种方法可能会存在一些安全隐患,因为其他用户也可以访问和修改这个文件。您可以使用以下命令更改文件的权限:

chmod 666 /mnt/us/extensions/touch/hello

希望这些方法可以帮助您解决问题。

调用kterm执行SHELL命令

如何调用/mnt/us/extensions/kterm/bin/kterm可执行文件执行SHELL命令eips -c? 要调用/mnt/us/extensions/kterm/bin/kterm可执行文件来执行SHELL命令eips -c,您可以使用以下命令:

/mnt/us/extensions/kterm/bin/kterm -e "eips -c"

这个命令的含义是:使用/mnt/us/extensions/kterm/bin/kterm可执行文件打开一个新的终端窗口,并在该终端窗口中执行eips -c命令。

其中,-e选项用于指定要在新终端窗口中执行的命令。请注意,这个命令需要在kindle的SHELL脚本中执行,因为只有在kindle的SHELL环境中,才能调用/mnt/us/extensions/kterm/bin/kterm可执行文件。

防止SHELL语句歧义

nohup /mnt/us/extensions/kterm/bin/kterm -e hello &

在语句中添加分隔符防止语句歧义 如果您想在以下语句中添加分隔符防止语句歧义:

nohup /mnt/us/extensions/kterm/bin/kterm -e hello &

可以使用分号(;)或者换行符来分隔多个命令,例如:

nohup /mnt/us/extensions/kterm/bin/kterm -e hello ; &

或者:

nohup /mnt/us/extensions/kterm/bin/kterm -e hello
&

这样,您就可以在一个语句中执行多个命令,并且不会出现歧义。请注意,在第二个示例中,&符号需要单独放在一行,因为它需要作为一个单独的命令来执行。

SHELL命令永不退出

/mnt/us/extensions/kterm/bin/kterm -e 'hello'

在执行hello运行空语句,永不退出,给出修改; 可以使用bash的后台运行和wait命令来实现在执行hello命令后等待其结束并退出。 以下是修改后的命令:

/mnt/us/extensions/kterm/bin/kterm -e 'hello & wait'

这个命令中,&符号将hello命令放到后台运行,然后wait命令等待其结束。这样,当hello命令执行完毕后,wait命令也就结束了,整个命令也就退出了。

SHELL空语句防止进入docker容器后闪退

在进入Docker容器后,如果执行空语句会导致容器闪退,这可能是因为Shell会立即退出,从而导致容器停止运行。

为了防止这种情况发生,可以在Shell中添加一个无限循环来保持Shell运行。以下是一个示例命令:

/bin/bash -c "while true; do sleep 3600; done"

这个命令将在Shell中启动一个无限循环,每个循环都会让Shell休眠3600秒(1小时),然后重新开始循环。这样,即使没有其他命令在Shell中运行,Shell也会保持运行状态,从而防止容器闪退。

kterm的命令参数

/mnt/us/extensions/kterm/bin/kterm -e
/mnt/us/extensions/kterm/bin/kterm -c

kindle中如上命令有什么作用?如何防止kterm执行完命令后退出? 这两个命令都是在Kindle设备上使用的,用于启动KTerm终端模拟器。具体作用如下:

  • /mnt/us/extensions/kterm/bin/kterm -e:启动KTerm并执行一个Shell命令,这里没有指定具体的命令,所以KTerm会启动后立即退出。
  • /mnt/us/extensions/kterm/bin/kterm -c:启动KTerm并进入命令行模式,等待用户在终端中输入命令。

为了防止KTerm执行完命令后退出,可以使用/bin/bash命令来启动一个新的Bash Shell,并在其中执行命令。例如,要在KTerm中执行ls命令并等待用户输入命令,可以使用以下命令:

/mnt/us/extensions/kterm/bin/kterm -e "/bin/bash -c 'ls; exec /bin/bash'"

这个命令将在KTerm中启动一个Bash Shell,执行ls命令,然后进入一个无限循环,等待用户输入命令。exec /bin/bash命令将替换当前Shell进程,使得用户输入的所有命令都在同一个Shell中执行,从而避免了KTerm在执行完命令后退出的问题。

rust程序改为定时执行,后台运行,永不退出

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};
use std::fs::{File, OpenOptions, copy};
use std::io::Write;
use chrono::prelude::*;

fn main() {
    // Create or open the runtime.log file
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("/mnt/us/runtime.log")
        .unwrap();

    // Download the image from the URL using wget command
    let url = "https://www.qsbye.cn/infoimg/daily.png";
    let output = Command::new("wget")
                         .arg("-O")
                         .arg("/mnt/us/extensions/touch/img1_old.png")
                         .arg(url)
                         .output();

    match output {
        Ok(output) => {
            let output_str = String::from_utf8_lossy(&output.stdout);
            println!("{}", output_str);
            writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap();
            writeln!(file, "{} {}", Local::now(), output_str).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }

    // Convert the image to bitmap format and save as png color space
    let img = image::open("/mnt/us/extensions/touch/img1_old.png").unwrap().into_luma8();
    let png = img.save_with_format("/mnt/us/extensions/touch/img1.png", ImageFormat::Png);

    match png {
        Ok(_) => {
            writeln!(file, "{} Convert the image to png format", Local::now()).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }

    // Display the bitmap on the Kindle device
    let output = Command::new("eips")
                         .arg("-f")
                         .arg("-g")
                         .arg("/mnt/us/extensions/touch/img1.png")
                         .output();

    match output {
        Ok(output) => {
            let output_str = String::from_utf8_lossy(&output.stdout);
            println!("{}", output_str);
            writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap();
            writeln!(file, "{} {}", Local::now(), output_str).unwrap();
        },
        Err(e) => {
            println!("Error: {:?}", e);
            copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
            writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
            return;
        }
    }
}

添加定时获取图片并显示,每隔1小时就运行一次,程序永不退出;给出完整代码; 好的,以下是添加定时获取图片并显示的完整代码:

use std::process::Command;
use image::{GenericImageView, ImageFormat, GrayImage, RgbImage};
use std::fs::{File, OpenOptions, copy};
use std::io::Write;
use chrono::prelude::*;
use std::thread::sleep;
use std::time::Duration;

fn main() {
    // Create or open the runtime.log file
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("/mnt/us/runtime.log")
        .unwrap();

    loop {
        // Download the image from the URL using wget command
        let url = "https://www.qsbye.cn/infoimg/daily.png";
        let output = Command::new("wget")
                             .arg("-O")
                             .arg("/mnt/us/extensions/touch/img1_old.png")
                             .arg(url)
                             .output();

        match output {
            Ok(output) => {
                let output_str = String::from_utf8_lossy(&output.stdout);
                println!("{}", output_str);
                writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap();
                writeln!(file, "{} {}", Local::now(), output_str).unwrap();
            },
            Err(e) => {
                println!("Error: {:?}", e);
                copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
                writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
                continue;
            }
        }

        // Convert the image to bitmap format and save as png color space
        let img = image::open("/mnt/us/extensions/touch/img1_old.png").unwrap().into_luma8();
        let png = img.save_with_format("/mnt/us/extensions/touch/img1.png", ImageFormat::Png);

        match png {
            Ok(_) => {
                writeln!(file, "{} Convert the image to png format", Local::now()).unwrap();
            },
            Err(e) => {
                println!("Error: {:?}", e);
                copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
                writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
                continue;
            }
        }

        // Display the bitmap on the Kindle device
        let output = Command::new("eips")
                             .arg("-f")
                             .arg("-g")
                             .arg("/mnt/us/extensions/touch/img1.png")
                             .output();

        match output {
            Ok(output) => {
                let output_str = String::from_utf8_lossy(&output.stdout);
                println!("{}", output_str);
                writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap();
                writeln!(file, "{} {}", Local::now(), output_str).unwrap();
            },
            Err(e) => {
                println!("Error: {:?}", e);
                copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap();
                writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap();
                continue;
            }
        }

        // Wait for an hour before running again
        sleep(Duration::from_secs(3600));
    }
}

这个程序会不断循环运行,每隔1小时就会获取一次图片并显示在Kindle设备上。如果获取或显示图片的过程中出现错误,程序会记录错误信息并跳过当前循环,等待下一个循环重新尝试获取和显示图片。 程序没有闪退了.

复制文件夹

# 检查/mnt/us/extensions是否有kterm目录,否则复制/mnt/us/extensions/touch/kterm目录过去

完善如上命令 可以使用以下命令来检查/mnt/us/extensions是否有kterm目录,如果没有,则复制/mnt/us/extensions/touch/kterm目录到该目录下:

if [ ! -d "/mnt/us/extensions/kterm" ]; then cp -r /mnt/us/extensions/touch/kterm /mnt/us/extensions/; fi

该命令首先使用条件语句if来检查/mnt/us/extensions是否有kterm目录,如果没有,则执行复制命令cp将/mnt/us/extensions/touch/kterm目录复制到/mnt/us/extensions目录下。其中,-r选项用于递归复制整个目录。

随机图片接口(图片画报)

[https://www.dujin.org/12142.html] 之前缙哥哥已经分享过《5种方法获取Bing每日图片 附送高清API接口及网站背景调用》,但是 Bing 这边的图片是一天一更新,有时候缙哥哥想让小伙伴每次访问都能看到不同的背景(图片),并且呢,图片不想放自己的服务器上,毕竟很占宽带,用CDN又费钱……

所以,今天就在网上找了找,弄了个随机图片接口,接口地址为:

https://api.dujin.org/pic/
https://api.dujin.org/pic/fengjing
https://api.dujin.org/pic/ghibli
https://api.dujin.org/pic/ghibli/ceqt
https://api.dujin.org/pic/ghibli/qfl
https://api.dujin.org/pic/ghibli/alad
https://api.dujin.org/pic/ghibli/qyqx
https://api.dujin.org/pic/ghibli/hyjwy
https://api.dujin.org/pic/ghibli/ydcb
https://api.dujin.org/pic/ghibli/stj

插入wordpress当登录页背景

将下面代码放入主题文件夹中的functions文件即可

/**
 * 缙哥哥自定义必应壁纸为登录页面背景
 * https://www.dujin.org/12142.html
 */
function custom_login_head(){
$imgurl = 'https://api.dujin.org/pic/';
if($imgurl){
    echo'
'; }} add_action('login_head', 'custom_login_head'); add_filter('login_headerurl', create_function(false,"return get_bloginfo('url');")); add_filter('login_headertitle', create_function(false,"return get_bloginfo('name');"));

制作Keynote演示文档用于视频制作

大纲:

  • 以往成果
  • 此次修改相比上一次的改进
  • 简述过程
  • 简述效果

视频简介🎉

使用Rust语言重写在Kindle上定时显示图片的插件. 开源地址👏:[https://gitee.com/qsbye/kindle-plugin-touch],hello_weather_three目录, 视频配套文档🌈:[https://www.cnblogs.com/qsbye/p/17420780.html].

视频稿

B站的小伙伴们,大家好 时隔一年,我用Rust语言重写了在Kindle上显示图片的插件. 以往成果:用Shell脚本调用eips命令显示图片,不足:图片显示不完全,图片尺寸与屏幕尺寸相同,但是显示时被拉伸. 改进:使用内存安全、带有包管理器的Rust语言重写插件,将png图片的颜色空间转换为Grey灰度图解决了图片显示不完全的问题;添加Bing每日一图;添加日志功能. 过程: 思考过程简述:使用Rust语言,使用Musl C库,使用Linux作为目标平台,从指定网址获取600x800大小的png图片,转换为Grey颜色空间后调用eips命令显示,如果获取图片失败,显示error.png图片,程序永不退出,间隔5分钟从头执行一次.所以只需要服务端配合生成600x800大小的图片就可以实现图片的自动更新.

效果: 点击touch按钮后屏幕清空,然后通过kterm执行bash然后执行自制的程序hello,屏幕显示图片.

使用ffmpeg导出视频的音频

使用ffmpeg导出视频的音频的命令 使用ffmpeg导出视频的音频可以使用以下命令:

ffmpeg -i "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mkv" -vn -acodec libmp3lame "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mp3"

其中,-i选项指定输入视频文件名,-vn选项表示不需要视频流,只需要音频流,-acodec copy选项表示将音频流复制到输出文件中,而不需要重新编码,最后指定输出音频文件名为output_audio.aac

请注意,输出音频文件的格式可以根据需要进行更改。如果需要将音频编码为其他格式,可以使用相应的编解码器。例如,要将音频编码为MP3格式,可以使用-acodec libmp3lame选项。

变音器

将mp3音频变声为大叔声音的Shell命令 要将mp3音频变声为大叔声音,可以使用SoX工具和Ffmpeg组合完成。下面是一个Shell命令示例:

brew install sox
sox  "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mp3" -C 192 -p pad 0 1 | sox - -C 192  "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40-dashu.mp3" pitch -600

这个命令将输入文件input.mp3读取,使用SoX工具将其转换为raw音频数据,并将其输出到stdout。然后,该命令将raw音频数据传递给第二个SoX命令,该命令将音频数据处理为大叔声音,并将其编码为mp3格式,最终输出到output.mp3文件中。

在这个命令中,-C 192选项指定输出文件的比特率为192kbps,pad 0 1选项用于添加一个额外的空白样本以避免SoX在处理文件时出现问题。pitch -600选项用于将音频音高降低600个单位,从而使其听起来更像大叔声音。

请注意,SoX工具和Ffmpeg需要事先安装才能使用。

更改为随机图片

[https://api.dujin.org/pic/fengjing] 测试: [https://www.qsbye.cn/infoimg/DailyImg1.php]

第八天:2023年6月2日

主要内容:添加网址;安装filebrowser;

优质RSS订阅源

[https://github.com/weekend-project-space/top-rss-list]

  1. [https://www.zhihu.com/rss]
  2. [https://www.ruanyifeng.com/blog/atom.xml]
  3. [http://feeds.feedburner.com/ruanyifeng]
  4. [https://sspai.com/feed]
  5. [https://rsshub.app/meituan/tech/home]
  6. [https://v2ex.com/index.xml]
  7. [http://coolshell.cn/feed]
  8. [https://www.ifanr.com/feed]
  9. [https://rsshub.app/zhihu/hotlist]
  10. [https://plink.anyfeeder.com/zaobao/realtime/china]
  11. [https://rsshub.app/infzm/2]
  12. [https://www.gcores.com/rss]
  13. [https://rsshub.app/zhihu/daily]
  14. [http://feed.appinn.com/]
  15. [https://rsshub.app/juejin/category/frontend]
  16. [https://rsshub.app/coolapk/tuwen-xinxian]
  17. [https://wangyurui.com/feed.xml]
  18. [http://feed.iplaysoft.com/]
  19. [https://rss.huxiu.com/]
  20. [https://rsshub.app/juejin/posts/1838039172387262]
  21. [https://rsshub.app/oschina/news/project]
  22. [http://feeds.feedburner.com/zhihu-daily]
  23. [http://node2.feed43.com/mryw.xml]
  24. [https://www.ithome.com/rss/]
  25. [https://rsshub.app/zaker/focusread]
  26. [https://www.36kr.com/feed]
  27. [https://tech.youzan.com/rss/]
  28. [https://plink.anyfeeder.com/idaily/today]
  29. [http://hellogithub.com/rss]
  30. [https://plink.anyfeeder.com/chinadaily/dual]
  31. [https://blogs.360.net/rss.html]
  32. [https://plink.anyfeeder.com/weixin/dukuxiaobao]
  33. [https://wanqu.co/feed/]
  34. [https://plink.anyfeeder.com/weixin/qnwzwx]
  35. [https://plink.anyfeeder.com/newscn/whxw]
  36. [https://plink.anyfeeder.com/infoq/recommend]
  37. [https://plink.anyfeeder.com/weixin/lifeweek]
  38. [https://plink.anyfeeder.com/wsj/cn]
  39. [https://plink.anyfeeder.com/weixin/cctvnewscenter]
  40. [https://plink.anyfeeder.com/guangmingribao]
  41. [https://plink.anyfeeder.com/weixin/banyuetan-weixin]
  42. [https://plink.anyfeeder.com/people-daily]
  43. [https://plink.anyfeeder.com/bjnews]
  44. [https://plink.anyfeeder.com/thepaper]
  45. [https://plink.anyfeeder.com/weixin/phoenixweekly]
  46. [https://plink.anyfeeder.com/weixin/CSDNnews]
  47. [https://plink.anyfeeder.com/weixin/MSRAsia]
  48. [https://plink.anyfeeder.com/weixin/AI_era]
  49. [https://plink.anyfeeder.com/weixin/vistaweek]
  50. [https://cn.wsj.com/zh-hans/rss]
  51. [https://plink.anyfeeder.com/weixin/ScientificAmerican]
  52. [https://plink.anyfeeder.com/weixin/hqsbwx]
  53. [https://plink.anyfeeder.com/weixin/diqiuzhishiju]
  54. [https://plink.anyfeeder.com/linux.cn]
  55. [https://plink.anyfeeder.com/weixin/dili360]
  56. [https://plink.anyfeeder.com/cnbeta]
  57. [https://rsshub.app/bing]
  58. [https://plink.anyfeeder.com/weixin/xueqiujinghua]

KUAL源码

[https://github.com/bfabiszewski/kterm] [https://www.mobileread.com/forums/showthread.php?t=225030] This is a simple GTK+ terminal emulator with embedded virtual keyboard. It is based on VteTerminal library. Some initial settings may be defined in kterm.conf file. Keyboard layouts are defined in xml config files. The keyboard config files follow the same rules as matchbox keyboard configs (backward compatible with kterm 1.x which used embedded matchbox keyboard).

Kterm has been developed for Kindle Touch. It is reported to also work on Paperwhites. Generally it should work on any platform which supports GTK+, either version 2 or 3.

On Kindle menu pops up on two fingers tap in the terminal window. On other devices on right button mouse click.

kindle插件的其他启动方式

如果使用KUAL 无法启动,可以将 bin 目录下的 RUNME.sh 复制到Kindle根目录,在搜索栏 输入 ;log runme 即可运行天气插件!

安装filebrowser

[https://bookfere.com/post/823.html/comment-page-2#comment-28241] [https://github.com/ngxson/hobby-kindle-http-file-server] 登录验证的默认用户名和密码均为“admin”(可登录之后在设置页面自行修改),

插件启动后默认会显示 Kindle 的根目录(即 Kindle 系统中的绝对路径 /mnt/us)。你可以修改插件文件夹中的 start.sh 文件,将最后一行中的路径 /mnt/us 改成你想要的路径,比如存放电子书的路径 /mnt/us/documents,或 Kindle 系统的根目录 /。注意,修改完成后需要重新开启插件才能生效。

扫描局域网ip地址:

#扫描局域网ip
sudo nmap -sP -PI -PT 192.168.0.0/24

结果:

Nmap scan report for 192.168.0.100
Host is up (0.095s latency).
MAC Address: 00:BB:3A:9E:69:7D (Amazon Technologies)
  • 访问:[192.168.0.100:80]
  • 新建用户:qsbye(qsbye)
  • 新建用户:408(408)
木兰宽松许可证, 第2版 木兰宽松许可证, 第2版 2020年1月 http://license.coscl.org.cn/MulanPSL2 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 0. 定义 “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 “法人实体”是指提交贡献的机构及其“关联实体”。 “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 1. 授予版权许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 2. 授予专利许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 3. 无商标许可 “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 4. 分发限制 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 5. 免责声明与责任限制 “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 6. 语言 “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 条款结束 如何将木兰宽松许可证,第2版,应用到您的软件 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 3, 请将如下声明文本放入每个源文件的头部注释中。 Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. Mulan Permissive Software License,Version 2 Mulan Permissive Software License,Version 2 (Mulan PSL v2) January 2020 http://license.coscl.org.cn/MulanPSL2 Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 0. Definition Software means the program and related documents which are licensed under this License and comprise all Contribution(s). Contribution means the copyrightable work licensed by a particular Contributor under this License. Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. Legal Entity means the entity making a Contribution and all its Affiliates. Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 1. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 2. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 3. No Trademark License No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 4. Distribution Restriction You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 5. Disclaimer of Warranty and Limitation of Liability THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 6. Language THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. END OF THE TERMS AND CONDITIONS How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details.

简介

为越狱的kindle开发的插件「touch」,显示每日天气 展开 收起
MulanPSL-2.0
取消

发行版

暂无发行版

贡献者 (2)

全部

近期动态

接近2年前推送了新的提交到 master 分支,2b4a8f8...5f5730a
接近2年前推送了新的提交到 master 分支,61f42b3...2b4a8f8
接近2年前推送了新的提交到 master 分支,69c6579...61f42b3
接近2年前推送了新的提交到 master 分支,2eb14d5...69c6579
接近2年前推送了新的提交到 master 分支,51eec3a...2eb14d5
加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
PHP
1
https://gitee.com/qsbye/kindle-plugin-touch.git
git@gitee.com:qsbye/kindle-plugin-touch.git
qsbye
kindle-plugin-touch
kindle-plugin-touch
master

搜索帮助